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))
Beispiel #3
0
    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)
Beispiel #4
0
    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
Beispiel #5
0
    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'}})
Beispiel #6
0
    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")
Beispiel #7
0
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
Beispiel #8
0
 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)
Beispiel #9
0
    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')
Beispiel #10
0
    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"]))
Beispiel #13
0
    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