def test_remove_calls_omshell_correctly(self): server_address = factory.make_string() shared_key = factory.make_string() mac_address = factory.make_mac_address() shell = Omshell(server_address, shared_key, ipv6=self.ipv6) # Instead of calling a real omshell, we'll just record the # parameters passed to Popen. recorder = FakeMethod(result=(0, b"thing1\nthing2\nobj: <null>")) shell._run = recorder shell.remove(mac_address) expected_script = dedent("""\ server {server} port {port} key omapi_key {key} connect new host set name = "{mac}" open remove """).format( server=server_address, port=self.port, key=shared_key, mac=mac_address.replace(':', '-')) expected_results = (expected_script.encode("utf-8"),) # Check that the 'stdin' arg contains the correct set of # commands. self.assertEqual( [expected_results], recorder.extract_args())
def test_reports_interfaces_to_region(self): fixture = self.useFixture(MockLiveClusterToRegionRPCFixture()) protocol, connecting = fixture.makeEventLoop(region.UpdateInterfaces) self.addCleanup((yield connecting)) interfaces = { "eth0": { "type": "physical", "mac_address": factory.make_mac_address(), "parents": [], "links": [], "enabled": True, } } rpc_service = services.getServiceNamed('rpc') service = RackNetworksMonitoringService( rpc_service, Clock(), enable_monitoring=False, enable_beaconing=False) service.getInterfaces = lambda: succeed(interfaces) # Put something in the cache. This tells recordInterfaces that refresh # has already run but the interfaces have changed thus they need to be # updated. service._recorded = {} service.startService() yield service.stopService() self.assertThat( protocol.UpdateInterfaces, MockCalledOnceWith( protocol, system_id=rpc_service.getClient().localIdent, interfaces=interfaces, topology_hints=None))
def test_modify_calls_omshell_correctly(self): server_address = factory.make_string() shared_key = factory.make_string() ip_address = factory.make_ip_address(ipv6=self.ipv6) mac_address = factory.make_mac_address() shell = Omshell(server_address, shared_key, ipv6=self.ipv6) # Instead of calling a real omshell, we'll just record the # parameters passed to Popen. recorder = FakeMethod(result=(0, b"hardware-type")) shell._run = recorder shell.modify(ip_address, mac_address) expected_script = dedent("""\ server {server} key omapi_key {key} connect new host set name = "{name}" open set ip-address = {ip} set hardware-address = {mac} set hardware-type = 1 update """) expected_script = expected_script.format( server=server_address, key=shared_key, ip=ip_address, mac=mac_address, name=mac_address.replace(':', '-')) # Check that the 'stdin' arg contains the correct set of # commands. self.assertEqual( [1, (expected_script.encode("utf-8"),)], [recorder.call_count, recorder.extract_args()[0]])
def test_processNotification_send_to_region(self): protocol, connecting = self.patch_rpc_UpdateLease() self.addCleanup((yield connecting)) client = getRegionClient() rpc_service = MagicMock() rpc_service.getClientNow.return_value = defer.succeed(client) service = LeaseSocketService(rpc_service, reactor) # Notification to region. packet = { "action": "commit", "mac": factory.make_mac_address(), "ip_family": "ipv4", "ip": factory.make_ipv4_address(), "timestamp": int(time.time()), "lease_time": 30, "hostname": factory.make_name("host"), } yield service.processNotification(packet, clock=reactor) self.assertThat( protocol.UpdateLease, MockCalledOnceWith( protocol, cluster_uuid=client.localIdent, action=packet["action"], mac=packet["mac"], ip_family=packet["ip_family"], ip=packet["ip"], timestamp=packet["timestamp"], lease_time=packet["lease_time"], hostname=packet["hostname"], ), )
def test__event_type_is_registered_on_first_call_only(self): protocol, connecting = self.patch_rpc_methods(side_effect=[{}, {}]) self.addCleanup((yield connecting)) mac_address = factory.make_mac_address() description = factory.make_name('description') event_name = random.choice(list(map_enum(EVENT_TYPES))) event_detail = EVENT_DETAILS[event_name] event_hub = NodeEventHub() # On the first call, the event type is registered before the log is # sent to the region. yield event_hub.logByMAC(event_name, mac_address, description) self.assertThat( protocol.RegisterEventType, MockCalledOnceWith(ANY, name=event_name, description=event_detail.description, level=event_detail.level)) self.assertThat(protocol.SendEventMACAddress, MockCalledOnce()) # Reset RPC call handlers. protocol.RegisterEventType.reset_mock() protocol.SendEventMACAddress.reset_mock() # On the second call, the event type is known to be registered, so the # log is sent to the region immediately. yield event_hub.logByMAC(event_name, mac_address, description) self.assertThat(protocol.RegisterEventType, MockNotCalled()) self.assertThat(protocol.SendEventMACAddress, MockCalledOnce())
def test_create_succeeds_when_host_map_already_exists(self): # To omshell, creating the same host map twice is an error. But # Omshell.create swallows the error and makes it look like # success. params = { "ip": factory.make_ip_address(ipv6=self.ipv6), "mac": factory.make_mac_address(), "hostname": factory.make_name("hostname"), } shell = Omshell( factory.make_name("server"), factory.make_name("key"), ipv6=self.ipv6, ) # This is the kind of error output we get if a host map has # already been created. error_output = (dedent("""\ obj: host ip-address = %(ip)s hardware-address = %(mac)s name = "%(hostname)s" > can't open object: I/O error obj: host ip-address = %(ip)s hardware-address = %(mac)s name = "%(hostname)s" """) % params) shell._run = Mock(return_value=(0, error_output.encode("ascii"))) shell.create(params["ip"], params["mac"]) # The test is that we get here without error. pass
def test_format_bootif_makes_mac_address_lower(self): fake_mac = factory.make_mac_address("-") fake_mac = fake_mac.upper() self.assertEqual( "01-%s" % fake_mac.replace(":", "-").lower(), format_bootif(fake_mac), )
def test_re_config_file_matches_classic_pxelinux_cfg(self): # The default config path is simply "pxelinux.cfg" (without # leading slash). The regex matches this. mac = factory.make_mac_address("-").encode("ascii") match = re_config_file.match(b"ppc64el/pxelinux.cfg/01-%s" % mac) self.assertIsNotNone(match) self.assertEqual({"mac": mac}, match.groupdict())
def test_logs_error_on_duplicate_macs(self): protocol, connecting = self.prepare_region_rpc() self.addCleanup((yield connecting)) system_id = factory.make_name("system-id") maaslog = self.patch(provisioningserver.rpc.utils, "maaslog") uuid = "node-" + factory.make_UUID() macs = sorted(factory.make_mac_address() for _ in range(3)) arch = factory.make_name("architecture") power_type = factory.make_name("power_type") power_parameters = { "power_address": factory.make_ipv4_address(), "power_user": factory.make_name("power_user"), "power_pass": factory.make_name("power_pass"), "power_control": None, "system_id": uuid, } protocol.CreateNode.side_effect = [ defer.succeed({"system_id": system_id}), defer.fail(NodeAlreadyExists("Node already exists.")), ] yield create_node(macs, arch, power_type, power_parameters) yield create_node(macs, arch, power_type, power_parameters) self.assertThat( maaslog.error, MockCalledOnceWith( "A node with one of the mac addresses in %s already " "exists.", macs, ), )
def _test_get_render_file(self, local, remote): # For paths matching PXEBootMethod.match_path, TFTPBackend.get_reader() # returns a Deferred that will yield a BytesReader. mac = factory.make_mac_address("-") config_path = compose_config_path(mac) backend = TFTPBackend(self.make_dir(), Mock()) # python-tx-tftp sets up call context so that backends can discover # more about the environment in which they're running. call_context = {"local": local, "remote": remote} @partial(self.patch, backend, "get_boot_method_reader") def get_boot_method_reader(boot_method, params): params_json = json.dumps(params).encode("ascii") params_json_reader = BytesReader(params_json) return succeed(params_json_reader) reader = yield context.call(call_context, backend.get_reader, config_path) output = reader.read(10000).decode("ascii") # The addresses provided by python-tx-tftp in the call context are # passed over the wire as address:port strings. expected_params = { "mac": mac, "local_ip": call_context["local"][0], # address only. "remote_ip": call_context["remote"][0], # address only. "bios_boot_method": "pxe", } observed_params = json.loads(output) self.assertEqual(expected_params, observed_params)
def test_interface_mac_vid(self): mac = factory.make_mac_address() vid = random.randint(1, 300) nic = DiscoveredMachineInterface(mac_address=mac, vid=vid) self.assertEqual(mac, nic.mac_address) self.assertEqual(vid, nic.vid) self.assertEqual([], nic.tags)
def make_host( hostname=None, interface_name=None, mac_address=None, ip=None, ipv6=False, dhcp_snippets=None, ): """Return a host entry for a subnet from network.""" if hostname is None: hostname = factory.make_name("host") if interface_name is None: interface_name = factory.make_name("eth") if mac_address is None: mac_address = factory.make_mac_address() if ip is None: ip = factory.make_ip_address(ipv6=ipv6) if dhcp_snippets is None: dhcp_snippets = make_host_dhcp_snippets() return { "host": "%s-%s" % (hostname, interface_name), "mac": mac_address, "ip": ip, "dhcp_snippets": dhcp_snippets, }
def test_is_valid_returns_false_for_truncated_vlan(self): src_mac = factory.make_mac_address() dst_mac = factory.make_mac_address() ethertype = ETHERTYPE.ARP payload = factory.make_bytes(48) vid = random.randrange(4095) packet = make_ethernet_packet( dst_mac=dst_mac, src_mac=src_mac, ethertype=ethertype, payload=payload, vid=vid, ) packet = packet[0:15] eth = Ethernet(packet) self.assertThat(eth.is_valid(), Equals(False))
def test_probe_and_enlist_recs_probes_and_enlists_no_commission(self): user = factory.make_name("user") ip, port, username, password, node_id, context = self.make_context() domain = factory.make_name("domain") macs = [factory.make_mac_address() for _ in range(3)] mock_get_nodes = self.patch(RECSAPI, "get_nodes") mock_get_nodes.return_value = {node_id: {"macs": macs, "arch": "arm"}} self.patch(RECSAPI, "set_boot_source") mock_create_node = self.patch(recs_module, "create_node") mock_create_node.side_effect = asynchronous(lambda *args: node_id) mock_commission_node = self.patch(recs_module, "commission_node") yield deferToThread( probe_and_enlist_recs, user, ip, int(port), username, password, False, domain, ) self.expectThat( mock_create_node, MockCalledOnceWith(macs, "armhf", "recs_box", context, domain), ) self.expectThat(mock_commission_node, MockNotCalled())
def test_machine(self): hostname = factory.make_name("hostname") cores = random.randint(1, 8) cpu_speed = random.randint(1000, 2000) memory = random.randint(4096, 8192) power_state = factory.make_name("unknown") interfaces = [ DiscoveredMachineInterface(mac_address=factory.make_mac_address()) for _ in range(3) ] block_devices = [ DiscoveredMachineBlockDevice( model=factory.make_name("model"), serial=factory.make_name("serial"), size=random.randint(512, 1024), ) for _ in range(3) ] tags = [factory.make_name("tag") for _ in range(3)] machine = DiscoveredMachine( hostname=hostname, architecture="amd64/generic", cores=cores, cpu_speed=cpu_speed, memory=memory, power_state=power_state, interfaces=interfaces, block_devices=block_devices, tags=tags, ) self.assertEqual(cores, machine.cores) self.assertEqual(cpu_speed, machine.cpu_speed) self.assertEqual(memory, machine.memory) self.assertEqual(interfaces, machine.interfaces) self.assertEqual(block_devices, machine.block_devices) self.assertEqual(tags, machine.tags)
def test__issue_ipmi_chassis_config_with_power_boot_type(self): context = make_context() driver = IPMIPowerDriver() ip_address = factory.make_ipv4_address() find_ip_via_arp = self.patch(ipmi_module, 'find_ip_via_arp') find_ip_via_arp.return_value = ip_address power_change = "on" context['mac_address'] = factory.make_mac_address() context['power_address'] = random.choice((None, "", " ")) context['power_boot_type'] = IPMI_BOOT_TYPE.EFI self.patch_autospec(driver, "_issue_ipmi_chassis_config_command") self.patch_autospec(driver, "_issue_ipmipower_command") driver._issue_ipmi_command(power_change, **context) # The IP address is passed to _issue_ipmi_chassis_config_command. self.assertThat( driver._issue_ipmi_chassis_config_command, MockCalledOnceWith( ANY, power_change, ip_address, power_boot_type=IPMI_BOOT_TYPE.EFI)) # The IP address is also within the command passed to # _issue_ipmi_chassis_config_command. self.assertThat( driver._issue_ipmi_chassis_config_command.call_args[0], Contains(ip_address)) # The IP address is passed to _issue_ipmipower_command. self.assertThat( driver._issue_ipmipower_command, MockCalledOnceWith(ANY, power_change, ip_address))
def test_probe_and_enlist_msftocs_probes_and_enlists(self): context = make_context() user = factory.make_name("user") system_id = factory.make_name("system_id") domain = factory.make_name("domain") macs = [factory.make_mac_address() for _ in range(3)] mock_get_blades = self.patch(MicrosoftOCSPowerDriver, "get_blades") mock_get_blades.return_value = {"%s" % context["blade_id"]: macs} self.patch(MicrosoftOCSPowerDriver, "set_next_boot_device") mock_create_node = self.patch(msftocs_module, "create_node") mock_create_node.side_effect = asynchronous(lambda *args: system_id) mock_commission_node = self.patch(msftocs_module, "commission_node") yield deferToThread( probe_and_enlist_msftocs, user, context["power_address"], int(context["power_port"]), context["power_user"], context["power_pass"], True, domain, ) self.expectThat( mock_create_node, MockCalledOnceWith(macs, "amd64", "msftocs", context, domain), ) self.expectThat(mock_commission_node, MockCalledOnceWith(system_id, user))
def test_reports_interfaces_with_hints_if_beaconing_enabled(self): fixture = self.useFixture(MockLiveClusterToRegionRPCFixture()) protocol, connecting = fixture.makeEventLoop(region.UpdateInterfaces) # Don't actually wait for beaconing to complete. pause_mock = self.patch(services_module, "pause") queue_mcast_mock = self.patch( services_module.BeaconingSocketProtocol, "queueMulticastBeaconing" ) self.addCleanup((yield connecting)) interfaces = { "eth0": { "type": "physical", "mac_address": factory.make_mac_address(), "parents": [], "links": [], "enabled": True, } } rpc_service = services.getServiceNamed("rpc") service = RackNetworksMonitoringService( rpc_service, Clock(), enable_monitoring=False, enable_beaconing=True, ) service.getInterfaces = lambda: succeed(interfaces) # Put something in the cache. This tells recordInterfaces that refresh # has already run but the interfaces have changed thus they need to be # updated. service._recorded = {} service.startService() yield service.stopService() self.assertThat( protocol.UpdateInterfaces, MockCalledOnceWith( protocol, system_id=rpc_service.getClient().localIdent, interfaces=interfaces, topology_hints=[], ), ) # The service should have sent out beacons, waited three seconds, # solicited for more beacons, then waited another three seconds before # deciding that beaconing is complete. self.assertThat(pause_mock, MockCallsMatch(call(3.0), call(3.0))) self.assertThat( queue_mcast_mock, MockCallsMatch( # Called when the service starts. call(solicitation=True), # Called three seconds later. call(solicitation=True), # Not called again when the service shuts down. ), )
def test_match_path_pxe_config_without_mac(self): method = S390XBootMethod() fake_mac = factory.make_mac_address("-") self.patch(s390x_module, "get_remote_mac").return_value = fake_mac config_path = b"s390x/pxelinux.cfg/default" params = method.match_path(None, config_path) expected = {"arch": "s390x", "mac": fake_mac} self.assertEqual(expected, params)
def test_match_path_mac_dash(self): method = UEFIAMD64BootMethod() backend = random.choice(["http", "tftp"]) mac = factory.make_mac_address().replace(":", "-") self.assertEqual( {"mac": mac}, method.match_path(backend, f"/grub/grub.cfg-{mac}".encode()), )
def test_get_reader_logs_node_event_with_mac_address(self): mac_address = factory.make_mac_address() self.patch(tftp_module, 'get_remote_mac').return_value = mac_address data = factory.make_string().encode("ascii") reader = yield self.get_reader(data) self.addCleanup(reader.finish) self.assertThat(tftp_module.log_request, MockCalledOnceWith(mac_address, ANY))
def test_get_node_info(self): method = WindowsPXEBootMethod() mac = factory.make_mac_address() self.patch(windows_module, "get_remote_mac").return_value = mac mock_request_node_info = self.patch( windows_module, "request_node_info_by_mac_address") method.get_node_info() self.assertThat(mock_request_node_info, MockCalledOnceWith(mac))
def test_parses_non_vlan(self): src_mac = factory.make_mac_address() dst_mac = factory.make_mac_address() ethertype = ETHERTYPE.ARP payload = factory.make_bytes(48) eth = Ethernet( make_ethernet_packet( dst_mac=dst_mac, src_mac=src_mac, ethertype=ethertype, payload=payload, )) self.assertThat(eth.dst_mac, Equals(hex_str_to_bytes(dst_mac))) self.assertThat(eth.src_mac, Equals(hex_str_to_bytes(src_mac))) self.assertThat(eth.ethertype, Equals(ethertype)) self.assertThat(eth.payload, Equals(payload)) self.assertThat(eth.is_valid(), Equals(True))
def get_example_path_and_components() -> TFTPPathAndComponents: """Return a plausible path and its components. The path is intended to match `re_config_file`, and the components are the expected groups from a match. """ mac = factory.make_mac_address("-") return compose_config_path(mac), {"mac": mac.encode("ascii")}
def test_interface_mac_vid_tags(self): mac = factory.make_mac_address() vid = random.randint(1, 300) tags = [factory.make_name("tag") for _ in range(3)] nic = DiscoveredMachineInterface(mac_address=mac, vid=vid, tags=tags) self.assertEquals(mac, nic.mac_address) self.assertEquals(vid, nic.vid) self.assertEquals(tags, nic.tags)
def test_get_ip_addr_json_returns_json(self): results = { factory.make_name("eth"): {"mac": factory.make_mac_address()} } patch_get_ip_addr = self.patch(ipaddr_module, "get_ip_addr") patch_get_ip_addr.return_value = results observed = get_ip_addr_json() self.assertIsInstance(observed, str) self.assertEquals(results, json.loads(observed))
def test_match_path_static_file_clean_path(self): method = WindowsPXEBootMethod() mock_mac = factory.make_mac_address() mock_get_node_info = self.patch(windows_module, 'get_remote_mac') mock_get_node_info.return_value = mock_mac params = yield method.match_path(None, '\\Boot\\BCD') self.assertEqual(mock_mac, params['mac']) self.assertEqual('bcd', params['path'])
def test_match_path_static_file_clean_path(self): method = WindowsPXEBootMethod() mock_mac = factory.make_mac_address() mock_get_node_info = self.patch(windows_module, "get_remote_mac") mock_get_node_info.return_value = mock_mac params = yield method.match_path(None, "\\Boot\\BCD") self.assertEqual(mock_mac, params["mac"]) self.assertEqual("bcd", params["path"])
def test_probe_and_enlist(self): node_id = make_node_id() node_list = NODE_LIST % node_id node_info = NODE_INFO % (node_id, self.product_name) node_macaddr = NODE_MACADDR % ( node_id, factory.make_mac_address(), factory.make_mac_address(), ) macs = re.findall(r":".join(["[0-9a-f]{2}"] * 6), node_macaddr) user = factory.make_name("user") host = factory.make_hostname("mscm") username = factory.make_name("user") password = factory.make_name("password") domain = factory.make_name("domain") system_id = factory.make_name("system_id") mscm_driver = self.patch(mscm_module, "MSCMPowerDriver").return_value mscm_driver.run_mscm_command.side_effect = ( node_list, None, node_info, node_macaddr, ) create_node = self.patch(mscm_module, "create_node") create_node.side_effect = asynchronous(lambda *args: system_id) commission_node = self.patch(mscm_module, "commission_node") params = { "power_address": host, "power_user": username, "power_pass": password, "node_id": node_id, } yield deferToThread( probe_and_enlist_mscm, user, host, username, password, True, domain ) self.expectThat( create_node, MockCalledOnceWith(macs, self.arch, "mscm", params, domain), ) self.expectThat(commission_node, MockCalledOnceWith(system_id, user))
def test_set_boot_order_network(self): power_partition_name = factory.make_name("power_partition_name") mac_address = factory.make_mac_address() cpc = self.fake_session.hmc.cpcs.add( { "name": factory.make_name("cpc"), "dpm-enabled": True, } ) partition = cpc.partitions.add( { "name": power_partition_name, "status": "terminated", } ) partition.nics.add({"mac-address": factory.make_mac_address()}) nic = partition.nics.add({"mac-address": mac_address}) yield self.hmcz.set_boot_order( None, self.make_context(power_partition_name), [ { "id": random.randint(0, 100), "name": factory.make_name("name"), "mac_address": mac_address, "vendor": factory.make_name("vendor"), "product": factory.make_name("product"), } ] + [ { factory.make_name("key"): factory.make_name("value") for _ in range(5) } for _ in range(5) ], ) self.assertEqual( "network-adapter", partition.properties["boot-device"] ) self.assertEqual(nic.uri, partition.properties["boot-network-device"])