diff --git a/Makefile b/Makefile index 748f136c..052bd3ba 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,6 @@ sync: bin/charm_helpers_sync.py @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-hooks.yaml @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-tests.yaml -publish: lint unit_test +publish: lint test bzr push lp:charms/neutron-gateway bzr push lp:charms/trusty/neutron-gateway diff --git a/tests/00-setup b/tests/00-setup index 27476744..95dffc44 100755 --- a/tests/00-setup +++ b/tests/00-setup @@ -4,9 +4,13 @@ set -ex sudo add-apt-repository --yes ppa:juju/stable sudo apt-get update --yes -sudo apt-get install --yes python-amulet \ +sudo apt-get install --yes amulet \ + python-cinderclient \ python-distro-info \ - python-neutronclient \ + python-glanceclient \ + python-heatclient \ python-keystoneclient \ + python-neutronclient \ python-novaclient \ - python-glanceclient + python-pika \ + python-swiftclient diff --git a/tests/019-basic-vivid-kilo b/tests/019-basic-vivid-kilo old mode 100644 new mode 100755 diff --git a/tests/020-basic-trusty-liberty b/tests/020-basic-trusty-liberty new file mode 100755 index 00000000..a2d0c9e1 --- /dev/null +++ b/tests/020-basic-trusty-liberty @@ -0,0 +1,11 @@ +#!/usr/bin/python + +"""Amulet tests on a basic quantum-gateway deployment on trusty-liberty.""" + +from basic_deployment import NeutronGatewayBasicDeployment + +if __name__ == '__main__': + deployment = NeutronGatewayBasicDeployment(series='trusty', + openstack='cloud:trusty-liberty', + source='cloud:trusty-updates/liberty') + deployment.run_tests() diff --git a/tests/021-basic-wily-liberty b/tests/021-basic-wily-liberty new file mode 100644 index 00000000..4dc44baa --- /dev/null +++ b/tests/021-basic-wily-liberty @@ -0,0 +1,9 @@ +#!/usr/bin/python + +"""Amulet tests on a basic quantum-gateway deployment on wily-liberty.""" + +from basic_deployment import NeutronGatewayBasicDeployment + +if __name__ == '__main__': + deployment = NeutronGatewayBasicDeployment(series='wily') + deployment.run_tests() diff --git a/tests/README b/tests/README index 80a2cc02..c52d967d 100644 --- a/tests/README +++ b/tests/README @@ -1,6 +1,16 @@ This directory provides Amulet tests that focus on verification of quantum-gateway deployments. +test_* methods are called in lexical sort order, although each individual test +should be idempotent, and expected to pass regardless of run order. + +Test name convention to ensure desired test order: + 1xx service and endpoint checks + 2xx relation checks + 3xx config checks + 4xx functional checks + 9xx restarts and other final checks + In order to run tests, you'll need charm-tools installed (in addition to juju, of course): sudo add-apt-repository ppa:juju/stable diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index 9232beef..881a6ab3 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -13,8 +13,8 @@ from charmhelpers.contrib.openstack.amulet.deployment import ( from charmhelpers.contrib.openstack.amulet.utils import ( OpenStackAmuletUtils, - DEBUG, # flake8: noqa - ERROR + DEBUG, + #ERROR ) # Use DEBUG to turn on debug logging @@ -45,30 +45,31 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): """ this_service = {'name': 'neutron-gateway'} other_services = [{'name': 'mysql'}, - {'name': 'rabbitmq-server'}, {'name': 'keystone'}, - {'name': 'nova-cloud-controller'}] - if self._get_openstack_release() >= self.trusty_kilo: - other_services.append({'name': 'neutron-api'}) - super(NeutronGatewayBasicDeployment, self)._add_services(this_service, - other_services) + {'name': 'rabbitmq-server'}, + {'name': 'keystone'}, + {'name': 'nova-cloud-controller'}, + {'name': 'neutron-api'}] + + super(NeutronGatewayBasicDeployment, self)._add_services( + this_service, other_services) def _add_relations(self): """Add all of the relations for the services.""" relations = { - 'keystone:shared-db': 'mysql:shared-db', - 'neutron-gateway:shared-db': 'mysql:shared-db', - 'neutron-gateway:amqp': 'rabbitmq-server:amqp', - 'nova-cloud-controller:quantum-network-service': \ - 'neutron-gateway:quantum-network-service', - 'nova-cloud-controller:shared-db': 'mysql:shared-db', - 'nova-cloud-controller:identity-service': 'keystone:identity-service', - 'nova-cloud-controller:amqp': 'rabbitmq-server:amqp' + 'keystone:shared-db': 'mysql:shared-db', + 'neutron-gateway:shared-db': 'mysql:shared-db', + 'neutron-gateway:amqp': 'rabbitmq-server:amqp', + 'nova-cloud-controller:quantum-network-service': + 'neutron-gateway:quantum-network-service', + 'nova-cloud-controller:shared-db': 'mysql:shared-db', + 'nova-cloud-controller:identity-service': 'keystone:' + 'identity-service', + 'nova-cloud-controller:amqp': 'rabbitmq-server:amqp', + 'neutron-api:shared-db': 'mysql:shared-db', + 'neutron-api:amqp': 'rabbitmq-server:amqp', + 'neutron-api:neutron-api': 'nova-cloud-controller:neutron-api', + 'neutron-api:identity-service': 'keystone:identity-service' } - if self._get_openstack_release() >= self.trusty_kilo: - relations['neutron-api:shared-db'] = 'mysql:shared-db' - relations['neutron-api:amqp'] = 'rabbitmq-server:amqp' - relations['neutron-api:neutron-api'] = 'nova-cloud-controller:neutron-api' - relations['neutron-api:identity-service'] = 'keystone:identity-service' super(NeutronGatewayBasicDeployment, self)._add_relations(relations) def _configure_services(self): @@ -83,16 +84,16 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): openstack_origin_git = { 'repositories': [ {'name': 'requirements', - 'repository': 'git://github.com/openstack/requirements', + 'repository': 'git://github.com/openstack/requirements', # noqa 'branch': branch}, {'name': 'neutron-fwaas', - 'repository': 'git://github.com/openstack/neutron-fwaas', + 'repository': 'git://github.com/openstack/neutron-fwaas', # noqa 'branch': branch}, {'name': 'neutron-lbaas', - 'repository': 'git://github.com/openstack/neutron-lbaas', + 'repository': 'git://github.com/openstack/neutron-lbaas', # noqa 'branch': branch}, {'name': 'neutron-vpnaas', - 'repository': 'git://github.com/openstack/neutron-vpnaas', + 'repository': 'git://github.com/openstack/neutron-vpnaas', # noqa 'branch': branch}, {'name': 'neutron', 'repository': 'git://github.com/openstack/neutron', @@ -122,7 +123,10 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): 'http_proxy': amulet_http_proxy, 'https_proxy': amulet_http_proxy, } - neutron_gateway_config['openstack-origin-git'] = yaml.dump(openstack_origin_git) + + neutron_gateway_config['openstack-origin-git'] = \ + yaml.dump(openstack_origin_git) + keystone_config = {'admin-password': 'openstack', 'admin-token': 'ubuntutesting'} nova_cc_config = {'network-manager': 'Quantum', @@ -137,9 +141,10 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): # Access the sentries for inspecting service units self.mysql_sentry = self.d.sentry.unit['mysql/0'] self.keystone_sentry = self.d.sentry.unit['keystone/0'] - self.rabbitmq_sentry = self.d.sentry.unit['rabbitmq-server/0'] + self.rmq_sentry = self.d.sentry.unit['rabbitmq-server/0'] self.nova_cc_sentry = self.d.sentry.unit['nova-cloud-controller/0'] self.neutron_gateway_sentry = self.d.sentry.unit['neutron-gateway/0'] + self.neutron_api_sentry = self.d.sentry.unit['neutron-api/0'] # Let things settle a bit before moving forward time.sleep(30) @@ -150,7 +155,6 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): password='openstack', tenant='admin') - # Authenticate admin with neutron ep = self.keystone.service_catalog.url_for(service_type='identity', endpoint_type='publicURL') @@ -160,40 +164,121 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): tenant_name='admin', region_name='RegionOne') - def test_services(self): + def test_100_services(self): """Verify the expected services are running on the corresponding service units.""" - neutron_services = ['status neutron-dhcp-agent', - 'status neutron-lbaas-agent', - 'status neutron-metadata-agent', - 'status neutron-metering-agent', - 'status neutron-ovs-cleanup', - 'status neutron-plugin-openvswitch-agent'] + neutron_services = ['neutron-dhcp-agent', + 'neutron-lbaas-agent', + 'neutron-metadata-agent', + 'neutron-metering-agent', + 'neutron-ovs-cleanup', + 'neutron-plugin-openvswitch-agent'] if self._get_openstack_release() <= self.trusty_juno: - neutron_services.append('status neutron-vpn-agent') + neutron_services.append('neutron-vpn-agent') - nova_cc_services = ['status nova-api-ec2', - 'status nova-api-os-compute', - 'status nova-objectstore', - 'status nova-cert', - 'status nova-scheduler'] - if self._get_openstack_release() >= self.precise_grizzly: - nova_cc_services.append('status nova-conductor') + nova_cc_services = ['nova-api-ec2', + 'nova-api-os-compute', + 'nova-objectstore', + 'nova-cert', + 'nova-scheduler', + 'nova-conductor'] commands = { - self.mysql_sentry: ['status mysql'], - self.keystone_sentry: ['status keystone'], + self.mysql_sentry: ['mysql'], + self.keystone_sentry: ['keystone'], self.nova_cc_sentry: nova_cc_services, self.neutron_gateway_sentry: neutron_services } - ret = u.validate_services(commands) + ret = u.validate_services_by_name(commands) if ret: amulet.raise_status(amulet.FAIL, msg=ret) - def test_neutron_gateway_shared_db_relation(self): + def test_102_service_catalog(self): + """Verify that the service catalog endpoint data is valid.""" + u.log.debug('Checking keystone service catalog...') + endpoint_check = { + 'adminURL': u.valid_url, + 'id': u.not_null, + 'region': 'RegionOne', + 'publicURL': u.valid_url, + 'internalURL': u.valid_url + } + expected = { + 'network': [endpoint_check], + 'compute': [endpoint_check], + 'identity': [endpoint_check] + } + actual = self.keystone.service_catalog.get_endpoints() + + ret = u.validate_svc_catalog_endpoint_data(expected, actual) + if ret: + amulet.raise_status(amulet.FAIL, msg=ret) + + def test_104_network_endpoint(self): + """Verify the neutron network endpoint data.""" + u.log.debug('Checking neutron network api endpoint data...') + endpoints = self.keystone.endpoints.list() + admin_port = internal_port = public_port = '9696' + expected = { + 'id': u.not_null, + 'region': 'RegionOne', + 'adminurl': u.valid_url, + 'internalurl': u.valid_url, + 'publicurl': u.valid_url, + 'service_id': u.not_null + } + ret = u.validate_endpoint_data(endpoints, admin_port, internal_port, + public_port, expected) + + if ret: + amulet.raise_status(amulet.FAIL, + msg='glance endpoint: {}'.format(ret)) + + def test_110_users(self): + """Verify expected users.""" + u.log.debug('Checking keystone users...') + expected = [ + {'name': 'admin', + 'enabled': True, + 'tenantId': u.not_null, + 'id': u.not_null, + 'email': 'juju@localhost'}, + {'name': 'quantum', + 'enabled': True, + 'tenantId': u.not_null, + 'id': u.not_null, + 'email': 'juju@localhost'} + ] + + if self._get_openstack_release() >= self.trusty_kilo: + # Kilo or later + expected.append({ + 'name': 'nova', + 'enabled': True, + 'tenantId': u.not_null, + 'id': u.not_null, + 'email': 'juju@localhost' + }) + else: + # Juno and earlier + expected.append({ + 'name': 's3_ec2_nova', + 'enabled': True, + 'tenantId': u.not_null, + 'id': u.not_null, + 'email': 'juju@localhost' + }) + + actual = self.keystone.users.list() + ret = u.validate_user_data(expected, actual) + if ret: + amulet.raise_status(amulet.FAIL, msg=ret) + + def test_200_neutron_gateway_mysql_shared_db_relation(self): """Verify the neutron-gateway to mysql shared-db relation data""" + u.log.debug('Checking neutron-gateway:mysql db relation data...') unit = self.neutron_gateway_sentry relation = ['shared-db', 'mysql:shared-db'] expected = { @@ -208,8 +293,9 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = u.relation_error('neutron-gateway shared-db', ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_mysql_shared_db_relation(self): + def test_201_mysql_neutron_gateway_shared_db_relation(self): """Verify the mysql to neutron-gateway shared-db relation data""" + u.log.debug('Checking mysql:neutron-gateway db relation data...') unit = self.mysql_sentry relation = ['shared-db', 'neutron-gateway:shared-db'] expected = { @@ -223,8 +309,9 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = u.relation_error('mysql shared-db', ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_neutron_gateway_amqp_relation(self): + def test_202_neutron_gateway_rabbitmq_amqp_relation(self): """Verify the neutron-gateway to rabbitmq-server amqp relation data""" + u.log.debug('Checking neutron-gateway:rmq amqp relation data...') unit = self.neutron_gateway_sentry relation = ['amqp', 'rabbitmq-server:amqp'] expected = { @@ -238,9 +325,10 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = u.relation_error('neutron-gateway amqp', ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_rabbitmq_amqp_relation(self): + def test_203_rabbitmq_neutron_gateway_amqp_relation(self): """Verify the rabbitmq-server to neutron-gateway amqp relation data""" - unit = self.rabbitmq_sentry + u.log.debug('Checking rmq:neutron-gateway amqp relation data...') + unit = self.rmq_sentry relation = ['amqp', 'neutron-gateway:amqp'] expected = { 'private-address': u.valid_ip, @@ -253,9 +341,11 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = u.relation_error('rabbitmq amqp', ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_neutron_gateway_network_service_relation(self): + def test_204_neutron_gateway_network_service_relation(self): """Verify the neutron-gateway to nova-cc quantum-network-service relation data""" + u.log.debug('Checking neutron-gateway:nova-cc net svc ' + 'relation data...') unit = self.neutron_gateway_sentry relation = ['quantum-network-service', 'nova-cloud-controller:quantum-network-service'] @@ -268,9 +358,11 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = u.relation_error('neutron-gateway network-service', ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_nova_cc_network_service_relation(self): + def test_205_nova_cc_network_service_relation(self): """Verify the nova-cc to neutron-gateway quantum-network-service relation data""" + u.log.debug('Checking nova-cc:neutron-gateway net svc ' + 'relation data...') unit = self.nova_cc_sentry relation = ['quantum-network-service', 'neutron-gateway:quantum-network-service'] @@ -289,56 +381,178 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): 'keystone_host': u.valid_ip, 'quantum_plugin': 'ovs', 'auth_host': u.valid_ip, - 'service_username': 'quantum_s3_ec2_nova', 'service_tenant_name': 'services' } + if self._get_openstack_release() >= self.trusty_kilo: + # Kilo or later expected['service_username'] = 'nova' + else: + # Juno or earlier + expected['service_username'] = 's3_ec2_nova' ret = u.validate_relation_data(unit, relation, expected) if ret: message = u.relation_error('nova-cc network-service', ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_z_restart_on_config_change(self): - """Verify that the specified services are restarted when the config - is changed. + def test_206_neutron_api_shared_db_relation(self): + """Verify the neutron-api to mysql shared-db relation data""" + u.log.debug('Checking neutron-api:mysql db relation data...') + unit = self.neutron_api_sentry + relation = ['shared-db', 'mysql:shared-db'] + expected = { + 'private-address': u.valid_ip, + 'database': 'neutron', + 'username': 'neutron', + 'hostname': u.valid_ip + } - Note(coreycb): The method name with the _z_ is a little odd - but it forces the test to run last. It just makes things - easier because restarting services requires re-authorization. - """ - conf = '/etc/neutron/neutron.conf' + ret = u.validate_relation_data(unit, relation, expected) + if ret: + message = u.relation_error('neutron-api shared-db', ret) + amulet.raise_status(amulet.FAIL, msg=message) - services = ['neutron-dhcp-agent', - 'neutron-lbaas-agent', - 'neutron-metadata-agent', - 'neutron-metering-agent', - 'neutron-openvswitch-agent'] + def test_207_shared_db_neutron_api_relation(self): + """Verify the mysql to neutron-api shared-db relation data""" + u.log.debug('Checking mysql:neutron-api db relation data...') + unit = self.mysql_sentry + relation = ['shared-db', 'neutron-api:shared-db'] + expected = { + 'db_host': u.valid_ip, + 'private-address': u.valid_ip, + 'password': u.not_null + } - if self._get_openstack_release() <= self.trusty_juno: - services.append('neutron-vpn-agent') + if self._get_openstack_release() == self.precise_icehouse: + # Precise + expected['allowed_units'] = 'nova-cloud-controller/0 neutron-api/0' + else: + # Not Precise + expected['allowed_units'] = 'neutron-api/0' - u.log.debug("Making config change on neutron-gateway...") - self.d.configure('neutron-gateway', {'debug': 'True'}) + ret = u.validate_relation_data(unit, relation, expected) + if ret: + message = u.relation_error('mysql shared-db', ret) + amulet.raise_status(amulet.FAIL, msg=message) - time = 60 - for s in services: - u.log.debug("Checking that service restarted: {}".format(s)) - if not u.service_restarted(self.neutron_gateway_sentry, s, conf, - pgrep_full=True, sleep_time=time): - self.d.configure('neutron-gateway', {'debug': 'False'}) - msg = "service {} didn't restart after config change".format(s) - amulet.raise_status(amulet.FAIL, msg=msg) - time = 0 + def test_208_neutron_api_amqp_relation(self): + """Verify the neutron-api to rabbitmq-server amqp relation data""" + u.log.debug('Checking neutron-api:amqp relation data...') + unit = self.neutron_api_sentry + relation = ['amqp', 'rabbitmq-server:amqp'] + expected = { + 'username': 'neutron', + 'private-address': u.valid_ip, + 'vhost': 'openstack' + } - self.d.configure('neutron-gateway', {'debug': 'False'}) + ret = u.validate_relation_data(unit, relation, expected) + if ret: + message = u.relation_error('neutron-api amqp', ret) + amulet.raise_status(amulet.FAIL, msg=message) - def test_neutron_config(self): + def test_209_amqp_neutron_api_relation(self): + """Verify the rabbitmq-server to neutron-api amqp relation data""" + u.log.debug('Checking amqp:neutron-api relation data...') + unit = self.rmq_sentry + relation = ['amqp', 'neutron-api:amqp'] + expected = { + 'hostname': u.valid_ip, + 'private-address': u.valid_ip, + 'password': u.not_null + } + + ret = u.validate_relation_data(unit, relation, expected) + if ret: + message = u.relation_error('rabbitmq amqp', ret) + amulet.raise_status(amulet.FAIL, msg=message) + + def test_210_neutron_api_keystone_identity_relation(self): + """Verify the neutron-api to keystone identity-service relation data""" + u.log.debug('Checking neutron-api:keystone id relation data...') + unit = self.neutron_api_sentry + relation = ['identity-service', 'keystone:identity-service'] + api_ip = unit.relation('identity-service', + 'keystone:identity-service')['private-address'] + api_endpoint = 'http://{}:9696'.format(api_ip) + expected = { + 'private-address': u.valid_ip, + 'quantum_region': 'RegionOne', + 'quantum_service': 'quantum', + 'quantum_admin_url': api_endpoint, + 'quantum_internal_url': api_endpoint, + 'quantum_public_url': api_endpoint, + } + + ret = u.validate_relation_data(unit, relation, expected) + if ret: + message = u.relation_error('neutron-api identity-service', ret) + amulet.raise_status(amulet.FAIL, msg=message) + + def test_211_keystone_neutron_api_identity_relation(self): + """Verify the keystone to neutron-api identity-service relation data""" + u.log.debug('Checking keystone:neutron-api id relation data...') + unit = self.keystone_sentry + relation = ['identity-service', 'neutron-api:identity-service'] + rel_ks_id = unit.relation('identity-service', + 'neutron-api:identity-service') + id_ip = rel_ks_id['private-address'] + expected = { + 'admin_token': 'ubuntutesting', + 'auth_host': id_ip, + 'auth_port': "35357", + 'auth_protocol': 'http', + 'private-address': id_ip, + 'service_host': id_ip, + } + ret = u.validate_relation_data(unit, relation, expected) + if ret: + message = u.relation_error('neutron-api identity-service', ret) + amulet.raise_status(amulet.FAIL, msg=message) + + def test_212_neutron_api_novacc_relation(self): + """Verify the neutron-api to nova-cloud-controller relation data""" + u.log.debug('Checking neutron-api:novacc relation data...') + unit = self.neutron_api_sentry + relation = ['neutron-api', 'nova-cloud-controller:neutron-api'] + api_ip = unit.relation('identity-service', + 'keystone:identity-service')['private-address'] + api_endpoint = 'http://{}:9696'.format(api_ip) + expected = { + 'private-address': api_ip, + 'neutron-plugin': 'ovs', + 'neutron-security-groups': "no", + 'neutron-url': api_endpoint, + } + ret = u.validate_relation_data(unit, relation, expected) + if ret: + message = u.relation_error('neutron-api neutron-api', ret) + amulet.raise_status(amulet.FAIL, msg=message) + + def test_213_novacc_neutron_api_relation(self): + """Verify the nova-cloud-controller to neutron-api relation data""" + u.log.debug('Checking novacc:neutron-api relation data...') + unit = self.nova_cc_sentry + relation = ['neutron-api', 'neutron-api:neutron-api'] + cc_ip = unit.relation('neutron-api', + 'neutron-api:neutron-api')['private-address'] + cc_endpoint = 'http://{}:8774/v2'.format(cc_ip) + expected = { + 'private-address': cc_ip, + 'nova_url': cc_endpoint, + } + ret = u.validate_relation_data(unit, relation, expected) + if ret: + message = u.relation_error('nova-cc neutron-api', ret) + amulet.raise_status(amulet.FAIL, msg=message) + + def test_300_neutron_config(self): """Verify the data in the neutron config file.""" + u.log.debug('Checking neutron gateway config file data...') unit = self.neutron_gateway_sentry - rabbitmq_relation = self.rabbitmq_sentry.relation('amqp', - 'neutron-gateway:amqp') + rmq_ng_rel = self.rmq_sentry.relation( + 'amqp', 'neutron-gateway:amqp') conf = '/etc/neutron/neutron.conf' expected = { @@ -350,35 +564,34 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): 'notification_driver': 'neutron.openstack.common.notifier.' 'list_notifier', 'list_notifier_drivers': 'neutron.openstack.common.' - 'notifier.rabbit_notifier' + 'notifier.rabbit_notifier', }, 'agent': { 'root_helper': 'sudo /usr/bin/neutron-rootwrap ' '/etc/neutron/rootwrap.conf' } } + if self._get_openstack_release() >= self.trusty_kilo: - oslo_concurrency = { - 'oslo_concurrency': { - 'lock_path':'/var/lock/neutron' - } + # Kilo or later + expected['oslo_messaging_rabbit'] = { + 'rabbit_userid': 'neutron', + 'rabbit_virtual_host': 'openstack', + 'rabbit_password': rmq_ng_rel['password'], + 'rabbit_host': rmq_ng_rel['hostname'], } - oslo_messaging_rabbit = { - 'oslo_messaging_rabbit': { - 'rabbit_userid': 'neutron', - 'rabbit_virtual_host': 'openstack', - 'rabbit_password': rabbitmq_relation['password'], - 'rabbit_host': rabbitmq_relation['hostname'], - } + expected['oslo_concurrency'] = { + 'lock_path': '/var/lock/neutron' } - expected.update(oslo_concurrency) - expected.update(oslo_messaging_rabbit) else: - expected['DEFAULT']['lock_path'] = '/var/lock/neutron' - expected['DEFAULT']['rabbit_userid'] = 'neutron' - expected['DEFAULT']['rabbit_virtual_host'] = 'openstack' - expected['DEFAULT']['rabbit_password'] = rabbitmq_relation['password'] - expected['DEFAULT']['rabbit_host'] = rabbitmq_relation['hostname'] + # Juno or earlier + expected['DEFAULT'].update({ + 'rabbit_userid': 'neutron', + 'rabbit_virtual_host': 'openstack', + 'rabbit_password': rmq_ng_rel['password'], + 'rabbit_host': rmq_ng_rel['hostname'], + 'lock_path': '/var/lock/neutron', + }) for section, pairs in expected.iteritems(): ret = u.validate_config_data(unit, conf, section, pairs) @@ -386,15 +599,17 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = "neutron config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_ml2_config(self): + def test_301_neutron_ml2_config(self): """Verify the data in the ml2 config file. This is only available since icehouse.""" + u.log.debug('Checking neutron gateway ml2 config file data...') if self._get_openstack_release() < self.precise_icehouse: return unit = self.neutron_gateway_sentry conf = '/etc/neutron/plugins/ml2/ml2_conf.ini' - neutron_gateway_relation = unit.relation('shared-db', 'mysql:shared-db') + ng_db_rel = unit.relation('shared-db', 'mysql:shared-db') + expected = { 'ml2': { 'type_drivers': 'gre,vxlan,vlan,flat', @@ -409,7 +624,7 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): }, 'ovs': { 'enable_tunneling': 'True', - 'local_ip': neutron_gateway_relation['private-address'] + 'local_ip': ng_db_rel['private-address'] }, 'agent': { 'tunnel_types': 'gre', @@ -427,8 +642,9 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = "ml2 config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_dhcp_agent_config(self): + def test_302_neutron_dhcp_agent_config(self): """Verify the data in the dhcp agent config file.""" + u.log.debug('Checking neutron gateway dhcp agent config file data...') unit = self.neutron_gateway_sentry conf = '/etc/neutron/dhcp_agent.ini' expected = { @@ -440,44 +656,45 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): '/etc/neutron/rootwrap.conf', 'ovs_use_veth': 'True' } + section = 'DEFAULT' - ret = u.validate_config_data(unit, conf, 'DEFAULT', expected) + ret = u.validate_config_data(unit, conf, section, expected) if ret: message = "dhcp agent config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_fwaas_driver_config(self): + def test_303_neutron_fwaas_driver_config(self): """Verify the data in the fwaas driver config file. This is only available since havana.""" - if self._get_openstack_release() < self.precise_havana: - return - + u.log.debug('Checking neutron gateway fwaas config file data...') unit = self.neutron_gateway_sentry conf = '/etc/neutron/fwaas_driver.ini' - if self._get_openstack_release() >= self.trusty_kilo: - expected = { - 'driver': 'neutron_fwaas.services.firewall.drivers.' - 'linux.iptables_fwaas.IptablesFwaasDriver', - 'enabled': 'True' - } - else: - expected = { - 'driver': 'neutron.services.firewall.drivers.' - 'linux.iptables_fwaas.IptablesFwaasDriver', - 'enabled': 'True' - } + expected = { + 'enabled': 'True' + } + section = 'fwaas' - ret = u.validate_config_data(unit, conf, 'fwaas', expected) + if self._get_openstack_release() >= self.trusty_kilo: + # Kilo or later + expected['driver'] = ('neutron_fwaas.services.firewall.drivers.' + 'linux.iptables_fwaas.IptablesFwaasDriver') + else: + # Juno or earlier + expected['driver'] = ('neutron.services.firewall.drivers.linux.' + 'iptables_fwaas.IptablesFwaasDriver') + + ret = u.validate_config_data(unit, conf, section, expected) if ret: message = "fwaas driver config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_l3_agent_config(self): + def test_304_neutron_l3_agent_config(self): """Verify the data in the l3 agent config file.""" + u.log.debug('Checking neutron gateway l3 agent config file data...') unit = self.neutron_gateway_sentry - nova_cc_relation = self.nova_cc_sentry.relation(\ - 'quantum-network-service', - 'neutron-gateway:quantum-network-service') + ncc_ng_rel = self.nova_cc_sentry.relation( + 'quantum-network-service', + 'neutron-gateway:quantum-network-service') ep = self.keystone.service_catalog.url_for(service_type='identity', endpoint_type='publicURL') @@ -488,24 +705,30 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): 'auth_url': ep, 'auth_region': 'RegionOne', 'admin_tenant_name': 'services', - 'admin_user': 'quantum_s3_ec2_nova', - 'admin_password': nova_cc_relation['service_password'], + 'admin_password': ncc_ng_rel['service_password'], 'root_helper': 'sudo /usr/bin/neutron-rootwrap ' '/etc/neutron/rootwrap.conf', 'ovs_use_veth': 'True', 'handle_internal_only_routers': 'True' } - if self._get_openstack_release() >= self.trusty_kilo: - expected['admin_user'] = 'nova' + section = 'DEFAULT' - ret = u.validate_config_data(unit, conf, 'DEFAULT', expected) + if self._get_openstack_release() >= self.trusty_kilo: + # Kilo or later + expected['admin_user'] = 'nova' + else: + # Juno or earlier + expected['admin_user'] = 's3_ec2_nova' + + ret = u.validate_config_data(unit, conf, section, expected) if ret: message = "l3 agent config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_lbaas_agent_config(self): + def test_305_neutron_lbaas_agent_config(self): """Verify the data in the lbaas agent config file. This is only available since havana.""" + u.log.debug('Checking neutron gateway lbaas config file data...') if self._get_openstack_release() < self.precise_havana: return @@ -513,21 +736,27 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): conf = '/etc/neutron/lbaas_agent.ini' expected = { 'DEFAULT': { - 'periodic_interval': '10', 'interface_driver': 'neutron.agent.linux.interface.' 'OVSInterfaceDriver', + 'periodic_interval': '10', 'ovs_use_veth': 'False', - 'device_driver': 'neutron.services.loadbalancer.drivers.' - 'haproxy.namespace_driver.HaproxyNSDriver' }, 'haproxy': { 'loadbalancer_state_path': '$state_path/lbaas', 'user_group': 'nogroup' } } + if self._get_openstack_release() >= self.trusty_kilo: - expected['DEFAULT']['device_driver'] = ('neutron_lbaas.services.' + - 'loadbalancer.drivers.haproxy.namespace_driver.HaproxyNSDriver') + # Kilo or later + expected['DEFAULT']['device_driver'] = \ + ('neutron_lbaas.services.loadbalancer.drivers.haproxy.' + 'namespace_driver.HaproxyNSDriver') + else: + # Juno or earlier + expected['DEFAULT']['device_driver'] = \ + ('neutron.services.loadbalancer.drivers.haproxy.' + 'namespace_driver.HaproxyNSDriver') for section, pairs in expected.iteritems(): ret = u.validate_config_data(unit, conf, section, pairs) @@ -535,46 +764,51 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = "lbaas agent config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_metadata_agent_config(self): + def test_306_neutron_metadata_agent_config(self): """Verify the data in the metadata agent config file.""" + u.log.debug('Checking neutron gateway metadata agent ' + 'config file data...') unit = self.neutron_gateway_sentry ep = self.keystone.service_catalog.url_for(service_type='identity', endpoint_type='publicURL') - neutron_gateway_relation = unit.relation('shared-db', 'mysql:shared-db') - nova_cc_relation = self.nova_cc_sentry.relation(\ - 'quantum-network-service', - 'neutron-gateway:quantum-network-service') + ng_db_rel = unit.relation('shared-db', + 'mysql:shared-db') + nova_cc_relation = self.nova_cc_sentry.relation( + 'quantum-network-service', + 'neutron-gateway:quantum-network-service') conf = '/etc/neutron/metadata_agent.ini' expected = { 'auth_url': ep, 'auth_region': 'RegionOne', 'admin_tenant_name': 'services', - 'admin_user': 'quantum_s3_ec2_nova', 'admin_password': nova_cc_relation['service_password'], 'root_helper': 'sudo neutron-rootwrap ' - '/etc/neutron/rootwrap.conf', + '/etc/neutron/rootwrap.conf', 'state_path': '/var/lib/neutron', - 'nova_metadata_ip': neutron_gateway_relation['private-address'], - 'nova_metadata_port': '8775' + 'nova_metadata_ip': ng_db_rel['private-address'], + 'nova_metadata_port': '8775', + 'cache_url': 'memory://?default_ttl=5' } + section = 'DEFAULT' + if self._get_openstack_release() >= self.trusty_kilo: + # Kilo or later expected['admin_user'] = 'nova' + else: + # Juno or earlier + expected['admin_user'] = 's3_ec2_nova' - if self._get_openstack_release() >= self.precise_icehouse: - expected['cache_url'] = 'memory://?default_ttl=5' - - ret = u.validate_config_data(unit, conf, 'DEFAULT', expected) + ret = u.validate_config_data(unit, conf, section, expected) if ret: message = "metadata agent config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_metering_agent_config(self): + def test_307_neutron_metering_agent_config(self): """Verify the data in the metering agent config file. This is only available since havana.""" - if self._get_openstack_release() < self.precise_havana: - return - + u.log.debug('Checking neutron gateway metering agent ' + 'config file data...') unit = self.neutron_gateway_sentry conf = '/etc/neutron/metering_agent.ini' expected = { @@ -586,26 +820,24 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): 'OVSInterfaceDriver', 'use_namespaces': 'True' } + section = 'DEFAULT' - ret = u.validate_config_data(unit, conf, 'DEFAULT', expected) + ret = u.validate_config_data(unit, conf, section, expected) if ret: message = "metering agent config error: {}".format(ret) + amulet.raise_status(amulet.FAIL, msg=message) - def test_nova_config(self): + def test_308_neutron_nova_config(self): """Verify the data in the nova config file.""" + u.log.debug('Checking neutron gateway nova config file data...') unit = self.neutron_gateway_sentry conf = '/etc/nova/nova.conf' - mysql_relation = self.mysql_sentry.relation('shared-db', - 'neutron-gateway:shared-db') - db_uri = "mysql://{}:{}@{}/{}".format('nova', - mysql_relation['password'], - mysql_relation['db_host'], - 'nova') - rabbitmq_relation = self.rabbitmq_sentry.relation('amqp', - 'neutron-gateway:amqp') - nova_cc_relation = self.nova_cc_sentry.relation(\ - 'quantum-network-service', - 'neutron-gateway:quantum-network-service') + + rabbitmq_relation = self.rmq_sentry.relation( + 'amqp', 'neutron-gateway:amqp') + nova_cc_relation = self.nova_cc_sentry.relation( + 'quantum-network-service', + 'neutron-gateway:quantum-network-service') ep = self.keystone.service_catalog.url_for(service_type='identity', endpoint_type='publicURL') @@ -622,49 +854,44 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): 'network_api_class': 'nova.network.neutronv2.api.API', } } + if self._get_openstack_release() >= self.trusty_kilo: - neutron = { - 'neutron': { - 'auth_strategy': 'keystone', - 'url': nova_cc_relation['quantum_url'], - 'admin_tenant_name': 'services', - 'admin_username': 'nova', - 'admin_password': nova_cc_relation['service_password'], - 'admin_auth_url': ep, - 'service_metadata_proxy': 'True', - } + # Kilo or later + expected['oslo_messaging_rabbit'] = { + 'rabbit_userid': 'neutron', + 'rabbit_virtual_host': 'openstack', + 'rabbit_password': rabbitmq_relation['password'], + 'rabbit_host': rabbitmq_relation['hostname'], } - oslo_concurrency = { - 'oslo_concurrency': { - 'lock_path':'/var/lock/nova' - } + expected['oslo_concurrency'] = { + 'lock_path': '/var/lock/nova' } - oslo_messaging_rabbit = { - 'oslo_messaging_rabbit': { - 'rabbit_userid': 'neutron', - 'rabbit_virtual_host': 'openstack', - 'rabbit_password': rabbitmq_relation['password'], - 'rabbit_host': rabbitmq_relation['hostname'], - } + expected['neutron'] = { + 'auth_strategy': 'keystone', + 'url': nova_cc_relation['quantum_url'], + 'admin_tenant_name': 'services', + 'admin_username': 'nova', + 'admin_password': nova_cc_relation['service_password'], + 'admin_auth_url': ep, + 'service_metadata_proxy': 'True', + 'metadata_proxy_shared_secret': u.not_null } - expected.update(neutron) - expected.update(oslo_concurrency) - expected.update(oslo_messaging_rabbit) else: - d = 'DEFAULT' - expected[d]['lock_path'] = '/var/lock/nova' - expected[d]['rabbit_userid'] = 'neutron' - expected[d]['rabbit_virtual_host'] = 'openstack' - expected[d]['rabbit_password'] = rabbitmq_relation['password'] - expected[d]['rabbit_host'] = rabbitmq_relation['hostname'] - expected[d]['service_neutron_metadata_proxy'] = 'True' - expected[d]['neutron_auth_strategy'] = 'keystone' - expected[d]['neutron_url'] = nova_cc_relation['quantum_url'] - expected[d]['neutron_admin_tenant_name'] = 'services' - expected[d]['neutron_admin_username'] = 'quantum_s3_ec2_nova' - expected[d]['neutron_admin_password'] = \ - nova_cc_relation['service_password'] - expected[d]['neutron_admin_auth_url'] = ep + # Juno or earlier + expected['DEFAULT'].update({ + 'rabbit_userid': 'neutron', + 'rabbit_virtual_host': 'openstack', + 'rabbit_password': rabbitmq_relation['password'], + 'rabbit_host': rabbitmq_relation['hostname'], + 'lock_path': '/var/lock/nova', + 'neutron_auth_strategy': 'keystone', + 'neutron_url': nova_cc_relation['quantum_url'], + 'neutron_admin_tenant_name': 'services', + 'neutron_admin_username': 's3_ec2_nova', + 'neutron_admin_password': nova_cc_relation['service_password'], + 'neutron_admin_auth_url': ep, + 'service_neutron_metadata_proxy': 'True', + }) for section, pairs in expected.iteritems(): ret = u.validate_config_data(unit, conf, section, pairs) @@ -672,56 +899,30 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = "nova config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_ovs_neutron_plugin_config(self): - """Verify the data in the ovs neutron plugin config file. The ovs - plugin is not used by default since icehouse.""" - if self._get_openstack_release() >= self.precise_icehouse: - return - - unit = self.neutron_gateway_sentry - neutron_gateway_relation = unit.relation('shared-db', 'mysql:shared-db') - - conf = '/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini' - expected = { - 'ovs': { - 'local_ip': neutron_gateway_relation['private-address'], - 'tenant_network_type': 'gre', - 'enable_tunneling': 'True', - 'tunnel_id_ranges': '1:1000' - }, - 'agent': { - 'polling_interval': '10', - 'root_helper': 'sudo /usr/bin/neutron-rootwrap ' - '/etc/neutron/rootwrap.conf' - } - } - - for section, pairs in expected.iteritems(): - ret = u.validate_config_data(unit, conf, section, pairs) - if ret: - message = "ovs neutron plugin config error: {}".format(ret) - amulet.raise_status(amulet.FAIL, msg=message) - - def test_vpn_agent_config(self): + def test_309_neutron_vpn_agent_config(self): """Verify the data in the vpn agent config file. This isn't available prior to havana.""" - if self._get_openstack_release() < self.precise_havana: - return - + u.log.debug('Checking neutron gateway vpn agent config file data...') unit = self.neutron_gateway_sentry conf = '/etc/neutron/vpn_agent.ini' expected = { - 'vpnagent': { - 'vpn_device_driver': 'neutron.services.vpn.device_drivers.' - 'ipsec.OpenSwanDriver' - }, 'ipsec': { 'ipsec_status_check_interval': '60' } } + if self._get_openstack_release() >= self.trusty_kilo: - expected['vpnagent']['vpn_device_driver'] = ('neutron_vpnaas.' + - 'services.vpn.device_drivers.ipsec.OpenSwanDriver') + # Kilo or later + expected['vpnagent'] = { + 'vpn_device_driver': 'neutron_vpnaas.services.vpn.' + 'device_drivers.ipsec.OpenSwanDriver' + } + else: + # Juno or earlier + expected['vpnagent'] = { + 'vpn_device_driver': 'neutron.services.vpn.device_drivers.' + 'ipsec.OpenSwanDriver' + } for section, pairs in expected.iteritems(): ret = u.validate_config_data(unit, conf, section, pairs) @@ -729,8 +930,9 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): message = "vpn agent config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def test_create_network(self): + def test_400_create_network(self): """Create a network, verify that it exists, and then delete it.""" + u.log.debug('Creating neutron network...') self.neutron.format = 'json' net_name = 'ext_net' @@ -743,7 +945,7 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): # Create a network and verify that it exists network = {'name': net_name} - self.neutron.create_network({'network':network}) + self.neutron.create_network({'network': network}) networks = self.neutron.list_networks(name=net_name) net_len = len(networks['networks']) @@ -751,9 +953,57 @@ class NeutronGatewayBasicDeployment(OpenStackAmuletDeployment): msg = "Expected 1 network, found {}".format(net_len) amulet.raise_status(amulet.FAIL, msg=msg) + u.log.debug('Confirming new neutron network...') network = networks['networks'][0] if network['name'] != net_name: amulet.raise_status(amulet.FAIL, msg="network ext_net not found") #Cleanup + u.log.debug('Deleting neutron network...') self.neutron.delete_network(network['id']) + + def test_900_restart_on_config_change(self): + """Verify that the specified services are restarted when the + config is changed.""" + + sentry = self.neutron_gateway_sentry + juju_service = 'neutron-gateway' + + # Expected default and alternate values + set_default = {'debug': 'False'} + set_alternate = {'debug': 'True'} + + # Services which are expected to restart upon config change, + # and corresponding config files affected by the change + conf_file = '/etc/neutron/neutron.conf' + services = { + 'neutron-dhcp-agent': conf_file, + 'neutron-lbaas-agent': conf_file, + 'neutron-metadata-agent': conf_file, + 'neutron-metering-agent': conf_file, + 'neutron-openvswitch-agent': conf_file, + } + + if self._get_openstack_release() <= self.trusty_juno: + services.update({'neutron-vpn-agent': conf_file}) + + # Make config change, check for svc restart, conf file mod time change + u.log.debug('Making config change on {}...'.format(juju_service)) + mtime = u.get_sentry_time(sentry) + self.d.configure(juju_service, set_alternate) + +# sleep_time = 90 + for s, conf_file in services.iteritems(): + u.log.debug("Checking that service restarted: {}".format(s)) + if not u.validate_service_config_changed(sentry, mtime, s, + conf_file): +# conf_file, +# sleep_time=sleep_time): + self.d.configure(juju_service, set_default) + msg = "service {} didn't restart after config change".format(s) + amulet.raise_status(amulet.FAIL, msg=msg) + + # Only do initial sleep on first service check +# sleep_time = 0 + + self.d.configure(juju_service, set_default) diff --git a/tests/tests.yaml b/tests/tests.yaml new file mode 100644 index 00000000..b288149b --- /dev/null +++ b/tests/tests.yaml @@ -0,0 +1,20 @@ +bootstrap: true +reset: true +virtualenv: true +makefile: + - lint + - test +sources: + - ppa:juju/stable +packages: + - amulet + - python-amulet + - python-cinderclient + - python-distro-info + - python-glanceclient + - python-heatclient + - python-keystoneclient + - python-neutronclient + - python-novaclient + - python-pika + - python-swiftclient