def create_discovered_host(name=None, ip_address=None, mac_address=None, options=None): """Creates a discovered host. :param str name: Name of discovered host. :param str ip_address: A valid ip address. :param str mac_address: A valid mac address. :param dict options: additional facts to add to discovered host :return: dict of ``entities.DiscoveredHost`` facts. """ if name is None: name = gen_string('alpha') if ip_address is None: ip_address = gen_ipaddr() if mac_address is None: mac_address = gen_mac(multicast=False) if options is None: options = {} facts = { 'name': name, 'discovery_bootip': ip_address, 'discovery_bootif': mac_address, 'interfaces': 'eth0', 'ipaddress': ip_address, 'ipaddress_eth0': ip_address, 'macaddress': mac_address, 'macaddress_eth0': mac_address, } facts.update(options) return entities.DiscoveredHost().facts(json={'facts': facts})
def _create_discovered_host(name=None, ipaddress=None, macaddress=None): """Creates discovered host by uploading few fake facts. :param str name: Name of discovered host. If ``None`` then a random value will be generated. :param str ipaddress: A valid ip address. If ``None`` then then a random value will be generated. :param str macaddress: A valid mac address. If ``None`` then then a random value will be generated. :return: A ``dict`` of ``DiscoveredHost`` facts. """ if name is None: name = gen_string('alpha') if ipaddress is None: ipaddress = gen_ipaddr() if macaddress is None: macaddress = gen_mac(multicast=False) return entities.DiscoveredHost().facts( json={ u'facts': { u'name': name, u'discovery_bootip': ipaddress, u'discovery_bootif': macaddress, u'interfaces': 'eth0', u'ipaddress': ipaddress, u'macaddress': macaddress, u'macaddress_eth0': macaddress, u'ipaddress_eth0': ipaddress, } })
def test_positive_upload_facts(self): """Upload fake facts to create a discovered host :id: c1f40204-bbb0-46d0-9b60-e42f00ad1649 :BZ: 1349364, 1392919 :Steps: 1. POST /api/v2/discovered_hosts/facts 2. Read the created discovered host :expectedresults: Host should be created successfully :CaseImportance: High :CaseLevel: Integration """ for name in valid_data_list(): with self.subTest(name): result = _create_discovered_host(name) discovered_host = entities.DiscoveredHost( id=result['id']).read() host_name = 'mac{0}'.format( discovered_host.mac.replace(':', '')) self.assertEqual(discovered_host.name, host_name)
def test_positive_upload_facts(): """Upload fake facts to create a discovered host :id: c1f40204-bbb0-46d0-9b60-e42f00ad1649 :BZ: 1349364, 1392919 :Steps: 1. POST /api/v2/discovered_hosts/facts 2. Read the created discovered host :expectedresults: Host should be created successfully :CaseImportance: High :CaseLevel: Integration :BZ: 1731112 """ name = gen_choice(list(valid_data_list().values())) result = _create_discovered_host(name) discovered_host = entities.DiscoveredHost(id=result['id']).read_json() host_name = 'mac{}'.format(discovered_host['mac'].replace(':', '')) assert discovered_host['name'] == host_name
def test_positive_reboot_all_pxe_hosts(_module_user, discovered_host_cleanup, discovery_settings, provisioning_env): """Rebooting all pxe-based discovered hosts :id: 69c807f8-5646-4aa6-8b3c-5ecdb69560ed :parametrized: yes :Setup: Provisioning should be configured and a hosts should be discovered via PXE boot. :Steps: PUT /api/v2/discovered_hosts/reboot_all :expectedresults: All disdcovered host should be rebooted successfully :CaseAutomation: Automated :CaseImportance: Medium """ cfg = get_nailgun_config() if _module_user: cfg.auth = (_module_user[0].login, _module_user[1]) # open ssh channels and attach them to foreman-tail output channel_1, channel_2 = ssh.get_client().invoke_shell(), ssh.get_client( ).invoke_shell() channel_1.send('foreman-tail\r') channel_2.send('foreman-tail\r') with LibvirtGuest() as pxe_host_1: _assert_discovered_host(pxe_host_1, channel_1, user_config=cfg) with LibvirtGuest() as pxe_host_2: _assert_discovered_host(pxe_host_2, channel_2, user_config=cfg) # reboot_all method leads to general /discovered_hosts/ path, so it doesn't matter # what DiscoveredHost object we execute this on try: entities.DiscoveredHost().reboot_all() except simplejson.errors.JSONDecodeError as e: if is_open('BZ:1893349'): pass else: raise e # assert that server receives DHCP discover from hosts PXELinux # this means that the hosts got rebooted for pxe_host in [(pxe_host_1, channel_1), (pxe_host_2, channel_2)]: for pattern in [ ( f"DHCPDISCOVER from {pxe_host[0].mac}", "DHCPDISCOVER", ), (f"DHCPACK on [0-9.]+ to {pxe_host[0].mac}", "DHCPACK"), ]: try: _wait_for_log(pxe_host[1], pattern[0], timeout=30) except TimedOutError: # raise assertion error raise AssertionError( f'Timed out waiting for {pattern[1]} from ' f'{pxe_host[0].mac}')
def test_positive_provision_pxe_host_non_admin(self): """Provision a pxe-based discovered hosts by non-admin user :id: 02144040-6cf6-4251-abaf-b0fad2c83109 :Setup: Provisioning should be configured and a host should be discovered :Steps: PUT /api/v2/discovered_hosts/:id :expectedresults: Host should be provisioned successfully by non admin user :CaseImportance: Critical :bz: 1638403 """ non_admin = create_org_admin_user(orgs=[self.org.id], locs=[self.loc.id]) nonadmin_config = get_nailgun_config(non_admin) if not self.configured_env: self.__class__.configured_env = configure_env_for_provision( org={ 'id': self.org.id, 'name': self.org.name }, loc={ 'id': self.loc.id, 'name': self.loc.name }, ) with LibvirtGuest() as pxe_host: hostname = pxe_host.guest_name discovered_host = self._assertdiscoveredhost( hostname, nonadmin_config) # Provision just discovered host discovered_host.hostgroup = entities.HostGroup( nonadmin_config, id=self.configured_env['hostgroup']['id']).read() discovered_host.root_pass = gen_string('alphanumeric') discovered_host.update(['hostgroup', 'root_pass']) # Assertions provisioned_host = entities.Host(nonadmin_config).search( query={ 'search': 'name={}.{}'.format(discovered_host.name, self.configured_env['domain']['name']) })[0] assert provisioned_host.subnet.read( ).name == self.configured_env['subnet']['name'] assert (provisioned_host.operatingsystem.read().ptable[0].read(). name == self.configured_env['ptable']['name']) assert (provisioned_host.operatingsystem.read().title == self.configured_env['os']['title']) assert not entities.DiscoveredHost(nonadmin_config).search( query={'search': f'name={discovered_host.name}'})
def test_positive_delete_pxe_host(): """Delete a pxe-based discovered hosts :id: 2ab2ad88-4470-4d4c-8e0b-5892ad8d675e :Setup: Provisioning should be configured and a host should be discovered :Steps: DELETE /api/v2/discovered_hosts/:id :expectedresults: Discovered Host should be deleted successfully :CaseAutomation: Automated :CaseImportance: High """ name = gen_choice(list(valid_data_list().values())) result = _create_discovered_host(name) entities.DiscoveredHost(id=result['id']).delete() search = entities.DiscoveredHost().search( query={'search': 'name == {}'.format(result['name'])}) assert len(search) == 0
def test_positive_provision_pxe_host(_module_user, discovery_settings, provisioning_env): """Provision a pxe-based discovered hosts :id: e805b9c5-e8f6-4129-a0e6-ab54e5671ddb :parametrized: yes :Setup: Provisioning should be configured and a host should be discovered :Steps: PUT /api/v2/discovered_hosts/:id :expectedresults: Host should be provisioned successfully :CaseImportance: Critical """ cfg = get_nailgun_config() if _module_user: cfg.auth = (_module_user[0].login, _module_user[1]) # open a ssh channel and attach it to foreman-tail output ssh_client = ssh.get_client() with ssh_client.invoke_shell() as channel: channel.send('foreman-tail\r') with LibvirtGuest() as pxe_host: discovered_host = _assert_discovered_host(pxe_host, channel, user_config=cfg) # Provision just discovered host discovered_host.hostgroup = entities.HostGroup( cfg, id=provisioning_env['hostgroup']['id']).read() discovered_host.root_pass = gen_string('alphanumeric') discovered_host.update(['hostgroup', 'root_pass']) # Assertions provisioned_host = entities.Host(cfg).search( query={ 'search': 'name={}.{}'.format(discovered_host.name, provisioning_env['domain']['name']) })[0] assert provisioned_host.subnet.read( ).name == provisioning_env['subnet']['name'] assert (provisioned_host.operatingsystem.read().ptable[0].read(). name == provisioning_env['ptable']['name']) assert provisioned_host.operatingsystem.read( ).title == provisioning_env['os']['title'] assert not entities.DiscoveredHost(cfg).search( query={'search': f'name={discovered_host.name}'})
def test_positive_discovered_host(session): """Check if the Discovered Host widget is working in the Dashboard UI :id: 74afef58-71f4-49e1-bbb6-6d4355d385f8 :Steps: 1. Create a Discovered Host. 2. Navigate Monitor -> Dashboard 3. Review the Discovered Host Status. :expectedresults: The widget is updated with all details. :CaseLevel: Integration """ org = entities.Organization().create() loc = entities.Location(organization=[org]).create() ipaddress = gen_ipaddr() macaddress = gen_mac(multicast=False) model = gen_string('alpha', length=5) host_name = 'mac{0}'.format(macaddress.replace(':', '')) entities.DiscoveredHost().facts(json={ u'facts': { u'name': gen_string('alpha'), u'discovery_bootip': ipaddress, u'discovery_bootif': macaddress, u'interfaces': 'eth0', u'ipaddress': ipaddress, u'macaddress': macaddress, u'macaddress_eth0': macaddress, u'ipaddress_eth0': ipaddress, u'foreman_organization': org.name, u'foreman_location': loc.name, u'model': model, u'memorysize_mb': 1000, u'physicalprocessorcount': 2, } }) with session: session.organization.select(org_name=org.name) session.location.select(loc_name=loc.name) values = session.dashboard.read('DiscoveredHosts') assert len(values['hosts']) == 1 assert values['hosts_count'] == '1 Discovered Host' assert values['hosts'][0]['Host'] == host_name assert values['hosts'][0]['Model'] == model assert values['hosts'][0]['CPUs'] == '2' assert values['hosts'][0]['Memory'] == '1000 MB'
def test_positive_provision_pxe_host(self): """Provision a pxe-based discovered hosts :id: e805b9c5-e8f6-4129-a0e6-ab54e5671ddb :Setup: Provisioning should be configured and a host should be discovered :Steps: PUT /api/v2/discovered_hosts/:id :expectedresults: Host should be provisioned successfully :CaseImportance: Critical """ if not self.configured_env: self.__class__.configured_env = configure_env_for_provision( org={ 'id': self.org.id, 'name': self.org.name }, loc={ 'id': self.loc.id, 'name': self.loc.name }, ) with LibvirtGuest() as pxe_host: hostname = pxe_host.guest_name discovered_host = self._assertdiscoveredhost(hostname) # Provision just discovered host discovered_host.hostgroup = entities.HostGroup( id=self.configured_env['hostgroup']['id']).read() discovered_host.root_pass = gen_string('alphanumeric') discovered_host.update(['hostgroup', 'root_pass']) # Assertions provisioned_host = entities.Host().search( query={ 'search': 'name={}.{}'.format(discovered_host.name, self.configured_env['domain']['name']) })[0] assert provisioned_host.subnet.read( ).name == self.configured_env['subnet']['name'] assert (provisioned_host.operatingsystem.read().ptable[0].read(). name == self.configured_env['ptable']['name']) assert (provisioned_host.operatingsystem.read().title == self.configured_env['os']['title']) assert not entities.DiscoveredHost().search( query={'search': f'name={discovered_host.name}'})
def _assertdiscoveredhost(self, hostname, user_config=None): """Check if host is discovered and information about it can be retrieved back Introduced a delay of 300secs by polling every 10 secs to get expected host """ default_config = entity_mixins.DEFAULT_SERVER_CONFIG for _ in range(30): discovered_host = entities.DiscoveredHost( user_config or default_config).search(query={'search': f'name={hostname}'}) if discovered_host: break time.sleep(10) else: raise HostNotDiscoveredException( f"The host {hostname} is not discovered within 300 seconds") return discovered_host[0]
def test_positive_auto_provision_pxe_host(_module_user, module_org, module_location, discovery_settings, provisioning_env): """Auto provision a pxe-based host by executing discovery rules :id: c93fd7c9-41ef-4eb5-8042-f72e87e67e10 :parametrized: yes :Setup: Provisioning should be configured and a host should be discovered :Steps: POST /api/v2/discovered_hosts/:id/auto_provision :expectedresults: Selected Host should be auto-provisioned successfully :CaseAutomation: Automated :CaseImportance: Critical """ cfg = get_nailgun_config() if _module_user: cfg.auth = (_module_user[0].login, _module_user[1]) # open a ssh channel and attach it to foreman-tail output ssh_client = ssh.get_client() with ssh_client.invoke_shell() as channel: channel.send('foreman-tail\r') with LibvirtGuest() as pxe_host: discovered_host = _assert_discovered_host(pxe_host, channel, user_config=cfg) # Provision just discovered host discovered_host.hostgroup = entities.HostGroup( cfg, id=provisioning_env['hostgroup']['id']).read() # create a discovery rule that will match hosts MAC address entities.DiscoveryRule( name=gen_string('alphanumeric'), search_=f"mac = {discovered_host.mac}", organization=[module_org], location=[module_location], hostgroup=entities.HostGroup( cfg, id=provisioning_env['hostgroup']['id']).read(), ).create() # Auto-provision the host discovered_host.auto_provision() # Assertions provisioned_host = entities.Host(cfg).search( query={ 'search': 'name={}.{}'.format(discovered_host.name, provisioning_env['domain']['name']) })[0] assert provisioned_host.subnet.read( ).name == provisioning_env['subnet']['name'] assert (provisioned_host.operatingsystem.read().ptable[0].read(). name == provisioning_env['ptable']['name']) assert provisioned_host.operatingsystem.read( ).title == provisioning_env['os']['title'] assert not entities.DiscoveredHost(cfg).search( query={'search': f'name={discovered_host.name}'})
def discovered_host_cleanup(): hosts = entities.DiscoveredHost().search() for host in hosts: host.delete()
def _assert_discovered_host(host, channel=None, user_config=None): """Check if host is discovered and information about it can be retrieved back Introduced a delay of 300secs by polling every 10 secs to get expected host """ # assert that server receives DHCP discover from hosts PXELinux for pattern in [ ( f"DHCPDISCOVER from {host.mac}", "DHCPDISCOVER", ), (f"DHCPACK on [0-9.]+ to {host.mac}", "DHCPACK"), ]: try: dhcp_pxe = _wait_for_log(channel, pattern[0], timeout=10) except TimedOutError: # raise assertion error raise AssertionError(f'Timed out waiting for {pattern[1]} from VM') groups = re.search('DHCPACK on (\\d.+) to', dhcp_pxe.out) assert len(groups.groups()) == 1, 'Unable to parse bootloader ip address' pxe_ip = groups.groups()[0] # assert that server retrieves pxelinux config over TFTP for pattern in [ (f'Client {pxe_ip} finished pxelinux.0', 'pxelinux.0'), (f'Client {pxe_ip} finished pxelinux.cfg/default', 'pxelinux.cfg/default'), (f'Client {pxe_ip} finished boot/fdi-image/vmlinuz0', 'fdi-image/vmlinuz0'), (f'Client {pxe_ip} finished boot/fdi-image/initrd0.img', 'fdi-image/initrd0.img'), ]: try: _wait_for_log(channel, pattern[0], timeout=20) except TimedOutError: # raise assertion error raise AssertionError( f'Timed out waiting for VM (tftp) to fetch {pattern[1]}') # assert that server receives DHCP discover from FDI for pattern in [ ( f"DHCPDISCOVER from {host.mac}", "DHCPDISCOVER", ), (f"DHCPACK on [0-9.]+ to {host.mac}", "DHCPACK"), ]: try: dhcp_fdi = _wait_for_log(channel, pattern[0], timeout=30) except TimedOutError: # raise assertion error raise AssertionError(f'Timed out waiting for {pattern[1]} from VM') groups = re.search('DHCPACK on (\\d.+) to', dhcp_fdi.out) assert len(groups.groups()) == 1, 'Unable to parse FDI ip address' fdi_ip = groups.groups()[0] # finally, assert that the FDI successfully uploaded its facts to the server try: facts_fdi = _wait_for_log( channel, f'\\[I\\|app\\|[a-z0-9]+\\] Started POST ' f'"/api/v2/discovered_hosts/facts" for {fdi_ip}', timeout=60, ) except TimedOutError: # raise assertion error raise AssertionError('Timed out waiting for /facts POST request') groups = re.search('\\[I\\|app\\|([a-z0-9]+)\\]', facts_fdi.out) assert len(groups.groups()) == 1, 'Unable to parse POST request UUID' req_id = groups.groups()[0] try: _wait_for_log(channel, f'\\[I\\|app\\|{req_id}\\] Completed 201 Created') except TimedOutError: # raise assertion error raise AssertionError('Timed out waiting for "/facts" 201 response') default_config = entity_mixins.DEFAULT_SERVER_CONFIG try: wait_for( lambda: len( entities.DiscoveredHost(user_config or default_config).search( query={'search': f'name={host.guest_name}'})) > 0, timeout=20, delay=2, logger=logger, ) except TimedOutError: raise AssertionError( 'Timed out waiting for discovered_host to appear on satellite') discovered_host = entities.DiscoveredHost( user_config or default_config).search(query={'search': f'name={host.guest_name}'}) return discovered_host[0]
def test_positive_provision_pxe_host_dhcp_change(self, discovery_settings, provisioning_env): """Discovered host is provisioned in dhcp range defined in subnet entity :id: 7ab654de-16dd-4a8b-946d-f6adde310340 :bz: 1367549 :Setup: Provisioning should be configured and a host should be discovered :Steps: 1. Set some dhcp range in dhcpd.conf in satellite. 2. Create subnet entity in satellite with a range different from whats defined in `dhcpd.conf`. 3. Create Hostgroup with the step 2 subnet. 4. Discover a new host in satellite. 5. Provision a host with the hostgroup created in step 3. :expectedresults: 1. The discovered host should be discovered with range defined in dhcpd.conf 2. But provisoning the discovered host should acquire an IP from dhcp range defined in subnet entity. :CaseImportance: Critical """ subnet = entities.Subnet(id=provisioning_env['subnet']['id']).read() # Updating satellite subnet component and dhcp conf ranges # Storing now for restoring later old_sub_from = subnet.from_ old_sub_to = subnet.to old_sub_to_4o = old_sub_to.split('.')[-1] # Calculating Subnet's new `from` range in Satellite Subnet Component new_subnet_from = subnet.from_[:subnet.from_.rfind('.') + 1] + str(int(old_sub_to_4o) - 9) # Same time, calculating dhcp confs new `to` range new_dhcp_conf_to = subnet.to[:subnet.to.rfind('.') + 1] + str(int(old_sub_to_4o) - 10) cfg = get_nailgun_config() ssh_client = ssh.get_client() with ssh_client.invoke_shell() as channel: channel.send('foreman-tail\r') try: # updating the ranges in component and in dhcp.conf subnet.from_ = new_subnet_from subnet.update(['from_']) ssh_client.exec_command( f'cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd_backup.conf && ' f'sed -ie \'s/{subnet.to}/{new_dhcp_conf_to}/\' /etc/dhcp/dhcpd.conf && ' f'systemctl restart dhcpd') with LibvirtGuest() as pxe_host: discovered_host = _assert_discovered_host( pxe_host, channel, cfg) # Assert Discovered host discovered within dhcp.conf range before provisioning assert int(discovered_host.ip.split('.')[-1]) <= int( new_dhcp_conf_to.split('.')[-1]) # Provision just discovered host discovered_host.hostgroup = entities.HostGroup( id=provisioning_env['hostgroup']['id']).read() discovered_host.root_pass = gen_string('alphanumeric') discovered_host.update(['hostgroup', 'root_pass']) # Assertions provisioned_host = entities.Host().search( query={ 'search': 'name={}.{}'.format( discovered_host.name, provisioning_env['domain']['name']) })[0] assert int(provisioned_host.ip.split('.')[-1]) >= int( new_subnet_from.split('.')[-1]) assert int(provisioned_host.ip.split('.')[-1]) <= int( old_sub_to_4o) assert not entities.DiscoveredHost().search( query={'search': f'name={discovered_host.name}'}) finally: subnet.from_ = old_sub_from subnet.update(['from_']) ssh_client.exec_command( 'mv /etc/dhcp/dhcpd_backup.conf /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf' )