def test_get_relation_from_unit_fails(self): self.patch_object(juju_utils, '_get_unit_names') self.patch_object(juju_utils, 'yaml') self.patch_object(juju_utils, 'model') self._get_unit_names.return_value = ['aunit/0', 'otherunit/0'] self.model.get_relation_id.return_value = 42 self.model.run_on_unit.return_value = {'Code': 1, 'Stderr': 'ERROR'} with self.assertRaises(Exception): juju_utils.get_relation_from_unit('aunit/0', 'otherunit/0', 'arelation') self.model.run_on_unit.assert_called_with( 'aunit/0', 'relation-get --format=yaml -r "42" - "otherunit/0"') self.assertFalse(self.yaml.safe_load.called)
def test_get_relation_from_unit(self): self.patch_object(juju_utils, '_get_unit_names') self.patch_object(juju_utils, 'yaml') self.patch_object(juju_utils, 'model') self._get_unit_names.return_value = ['aunit/0', 'otherunit/0'] data = {'foo': 'bar'} self.model.get_relation_id.return_value = 42 self.model.run_on_unit.return_value = {'Code': 0, 'Stdout': str(data)} juju_utils.get_relation_from_unit('aunit/0', 'otherunit/0', 'arelation') self.model.run_on_unit.assert_called_with( 'aunit/0', 'relation-get --format=yaml -r "42" - "otherunit/0"') self.yaml.safe_load.assert_called_with(str(data))
def _ceph_to_ceph_osd_relation(self, remote_unit_name): """Verify the cephX to ceph-osd relation data. Helper function to test the relation. """ logging.info('Checking {}:ceph-osd mon relation data...'.format( remote_unit_name)) unit_name = 'ceph-osd/0' relation_name = 'osd' remote_unit = zaza_model.get_unit_from_name(remote_unit_name) remote_ip = remote_unit.public_address cmd = 'leader-get fsid' result = zaza_model.run_on_unit(remote_unit_name, cmd) fsid = result.get('Stdout').strip() expected = { 'private-address': remote_ip, 'ceph-public-address': remote_ip, 'fsid': fsid, } relation = zaza_juju.get_relation_from_unit(unit_name, remote_unit_name, relation_name) for e_key, e_value in expected.items(): a_value = relation[e_key] self.assertEqual(e_value, a_value) self.assertTrue(relation['osd_bootstrap_key'] is not None)
def test_404_connection(self): """Verify the apache status module gets disabled when hardening apache. Ported from amulet tests. """ logging.info('Checking apache mod_status gets disabled.') unit_name = zaza_model.get_lead_unit_name('openstack-dashboard') keystone_unit = zaza_model.get_lead_unit_name('keystone') dashboard_relation = openstack_juju.get_relation_from_unit( keystone_unit, unit_name, 'identity-service') dashboard_ip = dashboard_relation['private-address'] logging.debug("... dashboard_ip is:{}".format(dashboard_ip)) logging.debug('Maybe enabling hardening for apache...') _app_config = zaza_model.get_application_config(self.application_name) logging.info(_app_config['harden']) with self.config_change( {'harden': _app_config['harden'].get('value', '')}, {'harden': 'apache'}): try: urllib.request.urlopen( 'http://{}/server-status'.format(dashboard_ip)) except urllib.request.HTTPError as e: if e.code == 404: return # test failed if it didn't return 404 msg = "Apache mod_status check failed." assert False, msg
def test_configure_compression(self): """Enable compression and validate properties flush through to pool.""" if not self.mimic_or_newer: logging.info('Skipping test, Mimic or newer required.') return if self.application_name == 'ceph-osd': # The ceph-osd charm itself does not request pools, neither does # the BlueStore Compression configuration options it have affect # pool properties. logging.info('test does not apply to ceph-osd charm.') return elif self.application_name == 'ceph-radosgw': # The Ceph RadosGW creates many light weight pools to keep track of # metadata, we only compress the pool containing actual data. app_pools = ['.rgw.buckets.data'] else: # Retrieve which pools the charm under test has requested skipping # metadata pools as they are deliberately not compressed. app_pools = [ pool for pool in zaza_ceph.get_pools_from_broker_req( self.application_name, model_name=self.model_name) if 'metadata' not in pool ] ceph_pools_detail = zaza_ceph.get_ceph_pool_details( model_name=self.model_name) logging.debug('BEFORE: {}'.format(ceph_pools_detail)) try: logging.info('Checking Ceph pool compression_mode prior to change') self._assert_pools_properties( app_pools, ceph_pools_detail, {'options': {'compression_mode': 'none'}}) except KeyError: logging.info('property does not exist on pool, which is OK.') logging.info('Changing "bluestore-compression-mode" to "force" on {}' .format(self.application_name)) with self.config_change( {'bluestore-compression-mode': 'none'}, {'bluestore-compression-mode': 'force'}): # Retrieve pool details from Ceph after changing configuration ceph_pools_detail = zaza_ceph.get_ceph_pool_details( model_name=self.model_name) logging.debug('CONFIG_CHANGE: {}'.format(ceph_pools_detail)) logging.info('Checking Ceph pool compression_mode after to change') self._assert_pools_properties( app_pools, ceph_pools_detail, {'options': {'compression_mode': 'force'}}) ceph_pools_detail = zaza_ceph.get_ceph_pool_details( model_name=self.model_name) logging.debug('AFTER: {}'.format(ceph_pools_detail)) logging.debug(zaza_juju.get_relation_from_unit( 'ceph-mon', self.application_name, None, model_name=self.model_name)) logging.info('Checking Ceph pool compression_mode after restoring ' 'config to previous value') self._assert_pools_properties( app_pools, ceph_pools_detail, {'options': {'compression_mode': 'none'}})
def _wait_to_resolve_test_record(self): dns_ip = zaza_juju.get_relation_from_unit( 'designate/0', 'designate-bind/0', 'dns-backend').get('private-address') logging.info('Waiting for dns record to propagate @ {}'.format(dns_ip)) lookup_cmd = [ 'dig', '+short', '@{}'.format(dns_ip), self.TEST_WWW_RECORD ] cmd_out = subprocess.check_output(lookup_cmd, universal_newlines=True).rstrip() if not self.TEST_RECORD[self.TEST_WWW_RECORD] == cmd_out: raise Exception("Record Doesn't Exist")
def _get_dashboard_ip(): """Get the IP of the dashboard. :returns: The IP of the dashboard :rtype: str """ unit_name = zaza_model.get_lead_unit_name('openstack-dashboard') keystone_unit = zaza_model.get_lead_unit_name('keystone') dashboard_relation = openstack_juju.get_relation_from_unit( keystone_unit, unit_name, 'identity-service') dashboard_ip = dashboard_relation['private-address'] logging.debug("dashboard_ip is: {}".format(dashboard_ip)) return dashboard_ip
def test_ceph_osd_ceph_relation_address(self): """Verify the ceph-osd to ceph relation data.""" logging.info('Checking ceph-osd:ceph-mon relation data...') unit_name = 'ceph-osd/0' remote_unit_name = 'ceph-mon/0' relation_name = 'osd' remote_unit = zaza_model.get_unit_from_name(remote_unit_name) remote_ip = remote_unit.public_address relation = zaza_juju.get_relation_from_unit(unit_name, remote_unit_name, relation_name) # Get private-address in relation rel_private_ip = relation.get('private-address') # The private address in relation should match ceph-mon/0 address self.assertEqual(rel_private_ip, remote_ip)
def test_404_connection(self): """Verify the apache status module gets disabled when hardening apache. Ported from amulet tests. """ logging.info('Checking apache mod_status gets disabled.') unit_name = zaza_model.get_lead_unit_name('openstack-dashboard') keystone_unit = zaza_model.get_lead_unit_name('keystone') dashboard_relation = openstack_juju.get_relation_from_unit( keystone_unit, unit_name, 'identity-service') dashboard_ip = dashboard_relation['private-address'] logging.debug("... dashboard_ip is:{}".format(dashboard_ip)) logging.debug('Maybe enabling hardening for apache...') _app_config = zaza_model.get_application_config(self.application_name) logging.info(_app_config['harden']) # NOTE(ajkavanagh): it seems that apache2 doesn't start quickly enough # for the test, and so it gets reset errors; repeat until either that # stops or there is a failure @tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, min=5, max=10), retry=tenacity.retry_if_exception_type( http.client.RemoteDisconnected), reraise=True) def _do_request(): return urllib.request.urlopen( 'http://{}/server-status'.format(dashboard_ip)) with self.config_change( {'harden': _app_config['harden'].get('value', '')}, {'harden': 'apache'}): try: _do_request() except urllib.request.HTTPError as e: # test failed if it didn't return 404 msg = "Apache mod_status check failed." self.assertEqual(e.code, 404, msg) logging.info('OK')
def test_400_connection(self): """Test that dashboard responds to http request. Ported from amulet tests. """ logging.info('Checking dashboard http response...') unit_name = zaza_model.get_lead_unit_name('openstack-dashboard') keystone_unit = zaza_model.get_lead_unit_name('keystone') dashboard_relation = openstack_juju.get_relation_from_unit( keystone_unit, unit_name, 'identity-service') dashboard_ip = dashboard_relation['private-address'] logging.debug("... dashboard_ip is:{}".format(dashboard_ip)) # NOTE(fnordahl) there is a eluding issue that currently makes the # first request to the OpenStack Dashboard error out # with 500 Internal Server Error in CI. Temporarilly # add retry logic to unwedge the gate. This issue # should be revisited and root caused properly when time # allows. @tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, min=5, max=10), reraise=True) def do_request(): logging.info("... trying to fetch the page") try: response = urllib.request.urlopen( 'http://{}/horizon'.format(dashboard_ip)) logging.info("... fetched page") except Exception as e: logging.info("... exception raised was {}".format(str(e))) raise return response.read().decode('utf-8') html = do_request() self.assertIn('OpenStack Dashboard', html, "Dashboard frontpage check failed")
def setup_bgp_speaker(peer_application_name, keystone_session=None): """Perform BGP Speaker setup. :param peer_application_name: String name of BGP peer application :type peer_application_name: string :param keystone_session: Keystone session object for overcloud :type keystone_session: keystoneauth1.session.Session object :returns: None :rtype: None """ # Get ASNs from deployment dr_relation = juju_utils.get_relation_from_unit( 'neutron-dynamic-routing', peer_application_name, 'bgpclient') peer_asn = dr_relation.get('asn') logging.debug('peer ASn: "{}"'.format(peer_asn)) peer_relation = juju_utils.get_relation_from_unit( peer_application_name, 'neutron-dynamic-routing', 'bgp-speaker') dr_asn = peer_relation.get('asn') logging.debug('our ASn: "{}"'.format(dr_asn)) # If a session has not been provided, acquire one if not keystone_session: keystone_session = openstack_utils.get_overcloud_keystone_session() # Get authenticated clients neutron_client = openstack_utils.get_neutron_session_client( keystone_session) # Create BGP speaker logging.info("Setting up BGP speaker") bgp_speaker = openstack_utils.create_bgp_speaker( neutron_client, local_as=dr_asn) # Add networks to bgp speaker logging.info("Advertising BGP routes") openstack_utils.add_network_to_bgp_speaker( neutron_client, bgp_speaker, EXT_NET) openstack_utils.add_network_to_bgp_speaker( neutron_client, bgp_speaker, PRIVATE_NET) logging.debug("Advertised routes: {}" .format( neutron_client.list_route_advertised_from_bgp_speaker( bgp_speaker["id"]))) # Create peer logging.info("Setting up BGP peer") bgp_peer = openstack_utils.create_bgp_peer(neutron_client, peer_application_name, remote_as=peer_asn) # Add peer to bgp speaker logging.info("Adding BGP peer to BGP speaker") openstack_utils.add_peer_to_bgp_speaker( neutron_client, bgp_speaker, bgp_peer) # Create Floating IP to advertise logging.info("Creating floating IP to advertise") port = openstack_utils.create_port(neutron_client, FIP_TEST, PRIVATE_NET) floating_ip = openstack_utils.create_floating_ip(neutron_client, EXT_NET, port=port) logging.info( "Advertised floating IP: {}".format( floating_ip["floating_ip_address"])) # NOTE(fnordahl): As a workaround for LP: #1784083 remove BGP speaker from # dragent and add it back. logging.info( "Waiting for Neutron agent 'neutron-bgp-dragent' to appear...") keystone_session = openstack_utils.get_overcloud_keystone_session() neutron_client = openstack_utils.get_neutron_session_client( keystone_session) agents = openstack_utils.neutron_agent_appears(neutron_client, 'neutron-bgp-dragent') agent_id = None for agent in agents.get('agents', []): agent_id = agent.get('id', None) if agent_id is not None: break logging.info( 'Waiting for BGP speaker to appear on agent "{}"...'.format(agent_id)) bgp_speakers = openstack_utils.neutron_bgp_speaker_appears_on_agent( neutron_client, agent_id) logging.info( "Removing and adding back bgp-speakers to agent (LP: #1784083)...") while True: try: for bgp_speaker in bgp_speakers.get('bgp_speakers', []): bgp_speaker_id = bgp_speaker.get('id', None) logging.info('removing "{}" from "{}"' ''.format(bgp_speaker_id, agent_id)) neutron_client.remove_bgp_speaker_from_dragent( agent_id, bgp_speaker_id) except neutronclient.common.exceptions.NotFound as e: logging.info('Exception: "{}"'.format(e)) break neutron_client.add_bgp_speaker_to_dragent( agent_id, {'bgp_speaker_id': bgp_speaker_id})
def setup_bgp_speaker(peer_application_name, keystone_session=None): """Perform BGP Speaker setup. :param peer_application_name: String name of BGP peer application :type peer_application_name: string :param keystone_session: Keystone session object for overcloud :type keystone_session: keystoneauth1.session.Session object :returns: None :rtype: None """ # Get ASNs from deployment dr_relation = juju_utils.get_relation_from_unit('neutron-dynamic-routing', peer_application_name, 'bgpclient') peer_asn = dr_relation.get('asn') logging.debug('peer ASn: "{}"'.format(peer_asn)) peer_relation = juju_utils.get_relation_from_unit( peer_application_name, 'neutron-dynamic-routing', 'bgp-speaker') dr_asn = peer_relation.get('asn') logging.debug('our ASn: "{}"'.format(dr_asn)) # If a session has not been provided, acquire one if not keystone_session: keystone_session = openstack_utils.get_overcloud_keystone_session() # Get authenticated clients neutron_client = openstack_utils.get_neutron_session_client( keystone_session) # Create BGP speaker logging.info("Setting up BGP speaker") bgp_speaker = openstack_utils.create_bgp_speaker(neutron_client, local_as=dr_asn) # Add networks to bgp speaker logging.info("Advertising BGP routes") openstack_utils.add_network_to_bgp_speaker(neutron_client, bgp_speaker, EXT_NET) openstack_utils.add_network_to_bgp_speaker(neutron_client, bgp_speaker, PRIVATE_NET) logging.debug("Advertised routes: {}".format( neutron_client.list_route_advertised_from_bgp_speaker( bgp_speaker["id"]))) # Create peer logging.info("Setting up BGP peer") bgp_peer = openstack_utils.create_bgp_peer(neutron_client, peer_application_name, remote_as=peer_asn) # Add peer to bgp speaker logging.info("Adding BGP peer to BGP speaker") openstack_utils.add_peer_to_bgp_speaker(neutron_client, bgp_speaker, bgp_peer) # Create Floating IP to advertise logging.info("Creating floating IP to advertise") port = openstack_utils.create_port(neutron_client, FIP_TEST, PRIVATE_NET) floating_ip = openstack_utils.create_floating_ip(neutron_client, EXT_NET, port=port) logging.info("Advertised floating IP: {}".format( floating_ip["floating_ip_address"]))
def test_401_authenticate(self): """Validate that authentication succeeds for client log in. Ported from amulet tests. """ logging.info('Checking authentication through dashboard...') unit_name = zaza_model.get_lead_unit_name('openstack-dashboard') keystone_unit = zaza_model.get_lead_unit_name('keystone') dashboard_relation = openstack_juju.get_relation_from_unit( keystone_unit, unit_name, 'identity-service') dashboard_ip = dashboard_relation['private-address'] logging.debug("... dashboard_ip is:{}".format(dashboard_ip)) url = 'http://{}/horizon/auth/login/'.format(dashboard_ip) overcloud_auth = openstack_utils.get_overcloud_auth() if overcloud_auth['OS_AUTH_URL'].endswith("v2.0"): api_version = 2 else: api_version = 3 keystone_client = openstack_utils.get_keystone_client(overcloud_auth) catalog = keystone_client.service_catalog.get_endpoints() logging.info(catalog) if api_version == 2: region = catalog['identity'][0]['publicURL'] else: region = [ i['url'] for i in catalog['identity'] if i['interface'] == 'public' ][0] # NOTE(ajkavanagh) there used to be a trusty/icehouse test in the # amulet test, but as the zaza tests only test from trusty/mitaka # onwards, the test has been dropped if (openstack_utils.get_os_release() >= openstack_utils.get_os_release('bionic_stein')): expect = "Sign Out" # update the in dashboard seems to require region to be default in # this test configuration region = 'default' else: expect = 'Projects - OpenStack Dashboard' # NOTE(thedac) Similar to the connection test above we get occasional # intermittent authentication fails. Wrap in a retry loop. @tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, min=5, max=10), retry=tenacity.retry_unless_exception_type( self.AuthExceptions), reraise=True) def _do_auth_check(expect): # start session, get csrftoken client = requests.session() client.get(url) if 'csrftoken' in client.cookies: csrftoken = client.cookies['csrftoken'] else: raise Exception("Missing csrftoken") # build and send post request auth = { 'domain': 'admin_domain', 'username': '******', 'password': overcloud_auth['OS_PASSWORD'], 'csrfmiddlewaretoken': csrftoken, 'next': '/horizon/', 'region': region, } # In the minimal test deployment /horizon/project/ is unauthorized, # this does not occur in a full deployment and is probably due to # services/information missing that horizon wants to display data # for. # Redirect to /horizon/identity/ instead. if (openstack_utils.get_os_release() >= openstack_utils.get_os_release('xenial_queens')): auth['next'] = '/horizon/identity/' if (openstack_utils.get_os_release() >= openstack_utils.get_os_release('bionic_stein')): auth['region'] = 'default' if api_version == 2: del auth['domain'] logging.info('POST data: "{}"'.format(auth)) response = client.post(url, data=auth, headers={'Referer': url}) if expect not in response.text: msg = 'FAILURE code={} text="{}"'.format( response, response.text) # NOTE(thedac) amulet.raise_status exits on exception. # Raise a custom exception. logging.info("Yeah, wen't wrong: {}".format(msg)) raise self.FailedAuth(msg) raise self.PassedAuth() try: _do_auth_check(expect) except self.FailedAuth as e: assert False, str(e) except self.PassedAuth: pass