def probe_seamicro15k_and_enlist( user, ip, username, password, power_control=None, accept_all=False, domain=None, ): power_control = power_control or "ipmi" maaslog.info("Probing for seamicro15k servers as %s@%s", username, ip) servers = find_seamicro15k_servers(ip, username, password, power_control) for system_id, macs in servers: params = { "power_address": ip, "power_user": username, "power_pass": password, "power_control": power_control, "system_id": system_id, } maaslog.info("Creating seamicro15k node with MACs: %s", macs) system_id = create_node(macs, "amd64", "sm15k", params, domain=domain).wait(30) if accept_all: commission_node(system_id, user).wait(30)
def probe_and_enlist_recs( user: str, ip: str, port: Optional[int], username: Optional[str], password: Optional[str], accept_all: bool = False, domain: str = None, ): maaslog.info("Probing for RECS servers as %s@%s", username, ip) port = 80 if port is None or port == 0 else port api = RECSAPI(ip, port, username, password) try: # if get_nodes works, we have access to the system nodes = api.get_nodes() except urllib.error.HTTPError as e: raise RECSError( "Failed to probe nodes for RECS_Master with ip=%s " "port=%s, username=%s, password=%s. HTTP error code: %s" % (ip, port, username, password, e.code) ) except urllib.error.URLError as e: raise RECSError( "Failed to probe nodes for RECS_Master with ip=%s " "port=%s, username=%s, password=%s. " "Server could not be reached: %s" % (ip, port, username, password, e.reason) ) for node_id, data in nodes.items(): params = { "power_address": ip, "power_port": port, "power_user": username, "power_pass": password, "node_id": node_id, } arch = "amd64" if data["arch"] == "arm": arch = "armhf" maaslog.info( "Creating RECS node %s with MACs: %s", node_id, data["macs"] ) # Set default (persistent) boot to HDD api.set_boot_source(node_id, "HDD", True) # Set next boot to PXE api.set_boot_source(node_id, "PXE", False) system_id = create_node( data["macs"], arch, "recs_box", params, domain ).wait(30) if accept_all: commission_node(system_id, user).wait(30)
def _probe_and_enlist_vmware_servers( api, accept_all, host, password, port, prefix_filter, protocol, servers, user, username, domain, ): maaslog.info("Found %d VMware servers", len(servers)) for system_name in servers: if not system_name.startswith(prefix_filter): maaslog.info( "Skipping node named '%s'; does not match prefix filter '%s'", system_name, prefix_filter, ) continue properties = servers[system_name] params = { "power_vm_name": system_name, "power_uuid": properties["uuid"], "power_address": host, "power_port": port, "power_protocol": protocol, "power_user": username, "power_pass": password, } # Note: the system name is URL encoded, so before we go to log # and/or create the node, we need to unquote it. # Otherwise we might pass in names like "Ubuntu%20(64-bit)" system_name = unquote(system_name) maaslog.info( "Creating VMware node with MACs: %s (%s)", properties["macs"], system_name, ) system_id = create_node( properties["macs"], properties["architecture"], "vmware", params, domain, system_name, ).wait(30) if system_id is not None: api.set_pxe_boot(properties) if accept_all and system_id is not None: commission_node(system_id, user).wait(30)
def probe_virsh_and_enlist(user: str, poweraddr: str, password: str = None, prefix_filter: str = None, accept_all: bool = False, domain: str = None): """Extracts all of the VMs from virsh and enlists them into MAAS. :param user: user for the nodes. :param poweraddr: virsh connection string. :param password: password connection string. :param prefix_filter: only enlist nodes that have the prefix. :param accept_all: if True, commission enlisted nodes. :param domain: The domain for the node to join. """ conn = VirshSSH(dom_prefix=prefix_filter) logged_in = conn.login(poweraddr, password) if not logged_in: raise VirshError('Failed to login to virsh console.') conn_list = conn.list_machines() for machine in conn_list: arch = conn.get_machine_arch(machine) state = conn.get_machine_state(machine) macs = conn.list_machine_mac_addresses(machine) params = { 'power_address': poweraddr, 'power_id': machine, } if password is not None: params['power_pass'] = password system_id = create_node(macs, arch, 'virsh', params, domain, hostname=machine).wait(30) # If the system_id is None an error occured when creating the machine. # Most likely the error is the node already exists. if system_id is None: continue # Force the machine off, as MAAS will control the machine # and it needs to be in a known state of off. if state == VirshVMState.ON: conn.poweroff(machine) conn.configure_pxe_boot(machine) if accept_all: commission_node(system_id, user).wait(30) conn.logout()
def probe_and_enlist_ucsm( user: str, url: str, username: Optional[str], password: Optional[str], accept_all: bool = False, domain: str = None, ): """Probe a UCS Manager and enlist all its servers. Here's what happens here: 1. Get a list of servers from the UCS Manager, along with their MAC addresses. 2. Configure each server to boot from LAN first. 3. Add each server to MAAS as a new node, with a power control method of 'ucsm'. The URL and credentials supplied are persisted with each node so MAAS knows how to access UCSM to manage the node in the future. This code expects each server in the system to have already been associated with a service profile. The servers must have networking configured, and their boot profiles must include a boot from LAN option. During enlistment, the boot profile for each service profile used by a server will be modified to move LAN boot to the highest priority boot option. Also, if any node fails to enlist, this enlistment process will stop and won't attempt to enlist any additional nodes. If a node is already known to MAAS, it will fail to enlist, so all nodes must be added at once. There is also room for optimization during enlistment. While our client deals with a single server at a time, the API is capable of reading/writing the settings of multiple servers in the same request. """ with logged_in(url, username, password) as api: servers = probe_servers(api) for server, _ in servers: set_lan_boot_default(api, server) for server, macs in servers: params = { "power_address": url, "power_user": username, "power_pass": password, "uuid": server.get("uuid"), } system_id = create_node(macs, "amd64", "ucsm", params, domain).wait(30) if accept_all: commission_node(system_id, user).wait(30)
def probe_and_enlist_msftocs( user: str, ip: str, port: Optional[int], username: Optional[str], password: Optional[str], accept_all: bool = False, domain: str = None, ): """Extracts all of nodes from msftocs, sets all of them to boot via HDD by, default, sets them to bootonce via PXE, and then enlists them into MAAS. """ # The default port for a MicrosoftOCS chassis is 8000. We expect an # integer from the AddChassis RPC call. port = 8000 if port is None or port == 0 else port msftocs_driver = MicrosoftOCSPowerDriver() context = { "power_address": ip, "power_port": str(port), "power_user": username, "power_pass": password, } try: # if get_blades works, we have access to the system blades = msftocs_driver.get_blades(context) except urllib.error.HTTPError as e: raise PowerFatalError( "Failed to probe nodes for Microsoft OCS with ip=%s " "port=%s, username=%s, password=%s. HTTP error code: %s" % (ip, port, username, password, e.code)) except urllib.error.URLError as e: raise PowerFatalError( "Failed to probe nodes for Microsoft OCS with ip=%s " "port=%s, username=%s, password=%s. " "Server could not be reached: %s" % (ip, port, username, password, e.reason)) else: for blade_id, macs in blades.items(): context["blade_id"] = blade_id # Set default (persistent) boot to HDD msftocs_driver.set_next_boot_device(context, persistent=True) # Set next boot to PXE msftocs_driver.set_next_boot_device(context, pxe=True) system_id = create_node(macs, "amd64", "msftocs", context, domain).wait(30) if accept_all: commission_node(system_id, user).wait(30)
def probe_hmcz_and_enlist( user: str, hostname: str, username: str, password: str, accept_all: bool = False, domain: str = None, prefix_filter: str = None, ): """Extracts all of the VMs from an HMC for Z and enlists them into MAAS. :param user: user for the nodes. :param hostname: Hostname for Proxmox :param username: The username to connect to Proxmox to :param password: The password to connect to Proxmox with. :param accept_all: If True, commission enlisted nodes. :param domain: What domain discovered machines to be apart of. :param prefix_filter: only enlist nodes that have the prefix. """ session = Session(hostname, username, password) client = Client(session) # Each HMC manages one or more CPCs(Central Processor Complex). Iterate # over all CPCs to find all partitions to add. for cpc in client.cpcs.list(): if not cpc.dpm_enabled: maaslog.warning( f"DPM is not enabled on '{cpc.get_property('name')}', " "skipping") continue for partition in cpc.partitions.list(): if prefix_filter and not partition.name.startswith(prefix_filter): continue system_id = yield create_node( [ nic.get_property("mac-address") for nic in partition.nics.list() ], "s390x", "hmcz", { "power_address": hostname, "power_user": username, "power_pass": password, "power_partition_name": partition.name, }, domain, partition.name, ) # If the system_id is None an error occured when creating the machine. # Most likely the error is the node already exists. if system_id is None: continue if accept_all: yield commission_node(system_id, user)
def test_calls_commission_node_rpc(self): protocol, connecting = self.prepare_region_rpc() self.addCleanup((yield connecting)) protocol.CommissionNode.return_value = defer.succeed({}) system_id = factory.make_name('system_id') user = factory.make_name('user') yield commission_node(system_id, user) self.assertThat( protocol.CommissionNode, MockCalledOnceWith( protocol, system_id=system_id, user=user))
def process_vms(data): extra_headers, response_data = data vms = json.loads(response_data)["data"] if not vms: raise PowerActionError( "No VMs returned! Are permissions set correctly?" ) for vm in vms: if prefix_filter and not vm["name"].startswith(prefix_filter): continue # Proxmox doesn't have an easy way to get the MAC address, it # includes it with a bunch of other data in the config. vm_config_data = yield proxmox._webhook_request( b"GET", proxmox._get_url( context, f"nodes/{vm['node']}/{vm['type']}/{vm['vmid']}/config", ), proxmox._make_auth_headers("", {}, extra_headers), verify_ssl, ) macs = [ mac[0] for mac in mac_regex.findall(vm_config_data.decode()) ] system_id = yield create_node( macs, "amd64", "proxmox", {"power_vm_name": vm["vmid"], **context}, domain, hostname=vm["name"].replace(" ", "-"), ) # If the system_id is None an error occured when creating the machine. # Most likely the error is the node already exists. if system_id is None: continue if vm["status"] != "stopped": yield proxmox._webhook_request( b"POST", proxmox._get_url( context, f"nodes/{vm['node']}/{vm['type']}/{vm['vmid']}/" "status/stop", ), proxmox._make_auth_headers(system_id, {}, extra_headers), context.get("power_verify_ssl") == SSL_INSECURE_YES, ) if accept_all: yield commission_node(system_id, user)
def probe_seamicro15k_and_enlist( user, ip, username, password, power_control=None, accept_all=False, domain=None): power_control = power_control or 'ipmi' maaslog.info("Probing for seamicro15k servers as %s@%s", username, ip) servers = find_seamicro15k_servers(ip, username, password, power_control) for system_id, macs in servers: params = { 'power_address': ip, 'power_user': username, 'power_pass': password, 'power_control': power_control, 'system_id': system_id } maaslog.info("Creating seamicro15k node with MACs: %s", macs) system_id = create_node( macs, 'amd64', 'sm15k', params, domain=domain).wait(30) if accept_all: commission_node(system_id, user).wait(30)
def test_logs_error_when_not_able_to_commission(self): protocol, connecting = self.prepare_region_rpc() self.addCleanup((yield connecting)) maaslog = self.patch(provisioningserver.rpc.utils, 'maaslog') system_id = factory.make_name('system_id') user = factory.make_name('user') error = CommissionNodeFailed('error') protocol.CommissionNode.return_value = defer.fail(error) yield commission_node(system_id, user) self.assertThat( maaslog.error, MockCalledOnceWith( "Could not commission with system_id %s because %s.", system_id, error.args[0]))
def probe_and_enlist_mscm( user: str, host: str, username: Optional[str], password: Optional[str], accept_all: bool = False, domain: str = None, ): """ Extracts all of nodes from the MSCM, sets all of them to boot via M.2 by, default, sets them to bootonce via PXE, and then enlists them into MAAS. If accept_all is True, it will also commission them. """ mscm_driver = MSCMPowerDriver() # Discover all available nodes # # Example of output from running "show node list": # "show node list\r\r\nSlot ID Proc Manufacturer # Architecture Memory Power Health\r\n---- # ----- ---------------------- -------------------- # ------ ----- ------\r\n 01 c1n1 Intel Corporation # x86 Architecture 32 GB On OK \r\n 02 c2n1 # N/A No Asset Information \r\n\r\n'" node_list = mscm_driver.run_mscm_command( "show node list", power_address=host, power_user=username, power_pass=password, ) nodes = re.findall(r"c\d+n\d", node_list) for node_id in nodes: params = { "power_address": host, "power_user": username, "power_pass": password, "node_id": node_id, } # Set default boot to M.2 mscm_driver.run_mscm_command("set node boot M.2 %s" % node_id, **params) # Retrieve node architecture # # Example of output from running "show node info <node_id>": # "show node info c1n1\r\r\n\r\nCartridge #1 \r\n # Type: Compute\r\n Manufacturer: HP\r\n # Product Name: ProLiant m500 Server Cartridge\r\n" node_info = mscm_driver.run_mscm_command("show node info %s" % node_id, **params) match = re.search(r"Product Name:\s*([A-Za-z0-9 ]+)", node_info) if match is None: raise PowerFatalError( "MSCM Power Driver unable to extract node architecture" " from: %s" % node_info) else: cartridge = match.group(1) if cartridge in cartridge_mapping: arch = cartridge_mapping[cartridge] else: arch = cartridge_mapping["Default"] # Retrieve node MACs # # Example of output from running "show node macaddr <node_id>": # "show node macaddr c1n1\r\r\nSlot ID NIC 1 (Switch A) # NIC 2 (Switch B) NIC 3 (Switch A) NIC 4 (Switch B)\r\n # ---- ----- ----------------- ----------------- ----------------- # -----------------\r\n 1 c1n1 a0:1d:48:b5:04:34 a0:1d:48:b5:04:35 # a0:1d:48:b5:04:36 a0:1d:48:b5:04:37\r\n\r\n\r\n" node_macaddr = mscm_driver.run_mscm_command( "show node macaddr %s" % node_id, **params) macs = re.findall(r":".join(["[0-9a-f]{2}"] * 6), node_macaddr) # Create node system_id = create_node(macs, arch, "mscm", params, domain).wait(30) if accept_all: commission_node(system_id, user).wait(30)