diff --git a/agents/redfish/fence_redfish.py b/agents/redfish/fence_redfish.py index 390a4827..c7c6492c 100644 --- a/agents/redfish/fence_redfish.py +++ b/agents/redfish/fence_redfish.py @@ -1,164 +1,164 @@ #!@PYTHON@ -tt # Copyright (c) 2018 Dell Inc. or its subsidiaries. All Rights Reserved. # Fence agent for devices that support the Redfish API Specification. import sys import re import logging import json import requests import atexit sys.path.append("@FENCEAGENTSLIBDIR@") -from requests.packages.urllib3.exceptions import InsecureRequestWarning from fencing import * from fencing import fail_usage, run_delay GET_HEADERS = {'accept': 'application/json', 'OData-Version': '4.0'} POST_HEADERS = {'content-type': 'application/json', 'accept': 'application/json', 'OData-Version': '4.0'} def get_power_status(conn, options): response = send_get_request(options, options["--systems-uri"]) if response['ret'] is False: fail_usage("Couldn't get power information") data = response['data'] try: logging.debug("PowerState is: " + data[u'PowerState']) except Exception: fail_usage("Unable to get PowerState: " + "https://" + options["--ip"] + ":" + str(options["--ipport"]) + options["--systems-uri"]) if data[u'PowerState'].strip() == "Off": return "off" else: return "on" def set_power_status(conn, options): action = { 'on' : "On", 'off': "ForceOff", 'reboot': "ForceRestart" }[options["--action"]] payload = {'ResetType': action} # Search for 'Actions' key and extract URI from it response = send_get_request(options, options["--systems-uri"]) if response['ret'] is False: return {'ret': False} data = response['data'] action_uri = data["Actions"]["#ComputerSystem.Reset"]["target"] response = send_post_request(options, action_uri, payload) if response['ret'] is False: fail_usage("Error sending power command") return def send_get_request(options, uri): full_uri = "https://" + options["--ip"] + ":" + str(options["--ipport"]) + uri try: resp = requests.get(full_uri, verify=not "--ssl-insecure" in options, headers=GET_HEADERS, auth=(options["--username"], options["--password"])) data = resp.json() except Exception as e: fail_usage("Failed: send_get_request: " + str(e)) return {'ret': True, 'data': data} def send_post_request(options, uri, payload): full_uri = "https://" + options["--ip"] + ":" + str(options["--ipport"]) + uri try: requests.post(full_uri, data=json.dumps(payload), headers=POST_HEADERS, verify=not "--ssl-insecure" in options, auth=(options["--username"], options["--password"])) except Exception as e: fail_usage("Failed: send_post_request: " + str(e)) return {'ret': True} def find_systems_resource(options): response = send_get_request(options, options["--redfish-uri"]) if response['ret'] is False: return {'ret': False} data = response['data'] if 'Systems' not in data: # Systems resource not found" return {'ret': False} else: response = send_get_request(options, data["Systems"]["@odata.id"]) if response['ret'] is False: return {'ret': False} data = response['data'] # need to be able to handle more than one entry for member in data[u'Members']: system_uri = member[u'@odata.id'] return {'ret': True, 'uri': system_uri} def define_new_opts(): all_opt["redfish-uri"] = { "getopt" : ":", "longopt" : "redfish-uri", "help" : "--redfish-uri=[uri] Base or starting Redifsh URI", "required" : "0", "default" : "/redfish/v1", "shortdesc" : "Base or starting Redfish URI", "order": 1 } all_opt["systems-uri"] = { "getopt" : ":", "longopt" : "systems-uri", "help" : "--systems-uri=[uri] Redfish Systems resource URI", "required" : "0", "shortdesc" : "Redfish Systems resource URI, i.e. /redfish/v1/Systems/System.Embedded.1", "order": 1 } def main(): atexit.register(atexit_handler) device_opt = ["ipaddr", "login", "passwd", "redfish-uri", "systems-uri", "ssl"] define_new_opts() opt = process_input(device_opt) all_opt["ssl"]["default"] = "1" options = check_input(device_opt, opt) docs = {} docs["shortdesc"] = "I/O Fencing agent for Redfish" docs["longdesc"] = "fence_redfish is an I/O Fencing agent which can be used with \ Out-of-Band controllers that support Redfish APIs. These controllers provide remote \ access to control power on a server." docs["vendorurl"] = "http://www.dmtf.org" show_docs(options, docs) run_delay(options) ## ## Operate the fencing device #### # Disable insecure-certificate-warning message if "--ssl-insecure" in opt: - requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + import urllib3 + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # backwards compatibility for : if options["--ip"].count(":") == 1: (options["--ip"], options["--ipport"]) = options["--ip"].split(":") if "--systems-uri" not in opt: # Systems URI not provided, find it sysresult = find_systems_resource(options) if sysresult['ret'] is False: sys.exit(1) else: options["--systems-uri"] = sysresult["uri"] result = fence_action(None, options, set_power_status, get_power_status, None) sys.exit(result) if __name__ == "__main__": main() diff --git a/agents/vmware_soap/fence_vmware_soap.py b/agents/vmware_soap/fence_vmware_soap.py index dd1a4ed6..53e8d8f4 100644 --- a/agents/vmware_soap/fence_vmware_soap.py +++ b/agents/vmware_soap/fence_vmware_soap.py @@ -1,261 +1,261 @@ #!@PYTHON@ -tt import sys import shutil, tempfile, suds import logging, requests import atexit, signal sys.path.append("@FENCEAGENTSLIBDIR@") from suds.client import Client from suds.sudsobject import Property from suds.transport.http import HttpAuthenticated from suds.transport import Reply, TransportError from fencing import * from fencing import fail, fail_usage, EC_STATUS, EC_LOGIN_DENIED, EC_INVALID_PRIVILEGES, EC_WAITING_ON, EC_WAITING_OFF from fencing import run_delay options_global = None conn_global = None class RequestsTransport(HttpAuthenticated): def __init__(self, **kwargs): self.cert = kwargs.pop('cert', None) self.verify = kwargs.pop('verify', True) self.session = requests.Session() # super won't work because not using new style class HttpAuthenticated.__init__(self, **kwargs) def send(self, request): self.addcredentials(request) resp = self.session.post(request.url, data=request.message, headers=request.headers, cert=self.cert, verify=self.verify) result = Reply(resp.status_code, resp.headers, resp.content) return result def soap_login(options): run_delay(options) if "--ssl" in options or "--ssl-secure" in options or "--ssl-insecure" in options: if "--ssl-insecure" in options: import ssl - from requests.packages.urllib3.exceptions import InsecureRequestWarning + import urllib3 if hasattr(ssl, '_create_unverified_context'): ssl._create_default_https_context = ssl._create_unverified_context - requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) verify = False else: verify = True url = "https://" else: verify = False url = "http://" url += options["--ip"] + ":" + str(options["--ipport"]) + "/sdk" tmp_dir = tempfile.mkdtemp() tempfile.tempdir = tmp_dir atexit.register(remove_tmp_dir, tmp_dir) try: headers = {"Content-Type" : "text/xml;charset=UTF-8", "SOAPAction" : "vim25"} conn = Client(url + "/vimService.wsdl", location=url, transport=RequestsTransport(verify=verify), headers=headers) mo_ServiceInstance = Property('ServiceInstance') mo_ServiceInstance._type = 'ServiceInstance' ServiceContent = conn.service.RetrieveServiceContent(mo_ServiceInstance) mo_SessionManager = Property(ServiceContent.sessionManager.value) mo_SessionManager._type = 'SessionManager' conn.service.Login(mo_SessionManager, options["--username"], options["--password"]) except requests.exceptions.SSLError as ex: fail_usage("Server side certificate verification failed") except Exception: fail(EC_LOGIN_DENIED) options["ServiceContent"] = ServiceContent options["mo_SessionManager"] = mo_SessionManager return conn def process_results(results, machines, uuid, mappingToUUID): for m in results.objects: info = {} for i in m.propSet: info[i.name] = i.val # Prevent error KeyError: 'config.uuid' when reaching systems which P2V failed, # since these systems don't have a valid UUID if "config.uuid" in info: machines[info["name"]] = (info["config.uuid"], info["summary.runtime.powerState"]) uuid[info["config.uuid"]] = info["summary.runtime.powerState"] mappingToUUID[m.obj.value] = info["config.uuid"] return (machines, uuid, mappingToUUID) def get_power_status(conn, options): mo_ViewManager = Property(options["ServiceContent"].viewManager.value) mo_ViewManager._type = "ViewManager" mo_RootFolder = Property(options["ServiceContent"].rootFolder.value) mo_RootFolder._type = "Folder" mo_PropertyCollector = Property(options["ServiceContent"].propertyCollector.value) mo_PropertyCollector._type = 'PropertyCollector' ContainerView = conn.service.CreateContainerView(mo_ViewManager, recursive=1, container=mo_RootFolder, type=['VirtualMachine']) mo_ContainerView = Property(ContainerView.value) mo_ContainerView._type = "ContainerView" FolderTraversalSpec = conn.factory.create('ns0:TraversalSpec') FolderTraversalSpec.name = "traverseEntities" FolderTraversalSpec.path = "view" FolderTraversalSpec.skip = False FolderTraversalSpec.type = "ContainerView" objSpec = conn.factory.create('ns0:ObjectSpec') objSpec.obj = mo_ContainerView objSpec.selectSet = [FolderTraversalSpec] objSpec.skip = True propSpec = conn.factory.create('ns0:PropertySpec') propSpec.all = False propSpec.pathSet = ["name", "summary.runtime.powerState", "config.uuid"] propSpec.type = "VirtualMachine" propFilterSpec = conn.factory.create('ns0:PropertyFilterSpec') propFilterSpec.propSet = [propSpec] propFilterSpec.objectSet = [objSpec] try: raw_machines = conn.service.RetrievePropertiesEx(mo_PropertyCollector, propFilterSpec) except Exception: fail(EC_STATUS) (machines, uuid, mappingToUUID) = process_results(raw_machines, {}, {}, {}) # Probably need to loop over the ContinueRetreive if there are more results after 1 iteration. while hasattr(raw_machines, 'token'): try: raw_machines = conn.service.ContinueRetrievePropertiesEx(mo_PropertyCollector, raw_machines.token) except Exception: fail(EC_STATUS) (more_machines, more_uuid, more_mappingToUUID) = process_results(raw_machines, {}, {}, {}) machines.update(more_machines) uuid.update(more_uuid) mappingToUUID.update(more_mappingToUUID) # Do not run unnecessary SOAP requests if "--uuid" in options and options["--uuid"] in uuid: break if ["list", "monitor"].count(options["--action"]) == 1: return machines else: if "--uuid" not in options: if options["--plug"].startswith('/'): ## Transform InventoryPath to UUID mo_SearchIndex = Property(options["ServiceContent"].searchIndex.value) mo_SearchIndex._type = "SearchIndex" vm = conn.service.FindByInventoryPath(mo_SearchIndex, options["--plug"]) try: options["--uuid"] = mappingToUUID[vm.value] except KeyError: fail(EC_STATUS) except AttributeError: fail(EC_STATUS) else: ## Name of virtual machine instead of path ## warning: if you have same names of machines this won't work correctly try: (options["--uuid"], _) = machines[options["--plug"]] except KeyError: fail(EC_STATUS) except AttributeError: fail(EC_STATUS) try: if uuid[options["--uuid"]] == "poweredOn": return "on" else: return "off" except KeyError: fail(EC_STATUS) def set_power_status(conn, options): mo_SearchIndex = Property(options["ServiceContent"].searchIndex.value) mo_SearchIndex._type = "SearchIndex" vm = conn.service.FindByUuid(mo_SearchIndex, vmSearch=1, uuid=options["--uuid"]) mo_machine = Property(vm.value) mo_machine._type = "VirtualMachine" try: if options["--action"] == "on": conn.service.PowerOnVM_Task(mo_machine) else: conn.service.PowerOffVM_Task(mo_machine) except suds.WebFault as ex: if (str(ex).find("Permission to perform this operation was denied")) >= 0: fail(EC_INVALID_PRIVILEGES) else: if options["--action"] == "on": fail(EC_WAITING_ON) else: fail(EC_WAITING_OFF) def remove_tmp_dir(tmp_dir): shutil.rmtree(tmp_dir) def logout(): try: conn_global.service.Logout(options_global["mo_SessionManager"]) except Exception: pass def signal_handler(signum, frame): raise Exception("Signal \"%d\" received which has triggered an exit of the process." % signum) def main(): global options_global global conn_global device_opt = ["ipaddr", "login", "passwd", "web", "ssl", "notls", "port"] atexit.register(atexit_handler) atexit.register(logout) signal.signal(signal.SIGTERM, signal_handler) options_global = check_input(device_opt, process_input(device_opt)) ## ## Fence agent specific defaults ##### docs = {} docs["shortdesc"] = "Fence agent for VMWare over SOAP API" docs["longdesc"] = "fence_vmware_soap is an I/O Fencing agent \ which can be used with the virtual machines managed by VMWare products \ that have SOAP API v4.1+. \ \n.P\n\ Name of virtual machine (-n / port) has to be used in inventory path \ format (e.g. /datacenter/vm/Discovered virtual machine/myMachine). \ In the cases when name of yours VM is unique you can use it instead. \ Alternatively you can always use UUID to access virtual machine." docs["vendorurl"] = "http://www.vmware.com" show_docs(options_global, docs) logging.basicConfig(level=logging.INFO) logging.getLogger('suds.client').setLevel(logging.CRITICAL) logging.getLogger("requests").setLevel(logging.CRITICAL) logging.getLogger("urllib3").setLevel(logging.CRITICAL) ## ## Operate the fencing device #### conn_global = soap_login(options_global) result = fence_action(conn_global, options_global, set_power_status, get_power_status, get_power_status) ## Logout from system is done automatically via atexit() sys.exit(result) if __name__ == "__main__": main()