diff --git a/etc/tests/localhost.test.conf b/etc/tests/localhost.test.conf index 74f06be944..72eb8b578a 100644 --- a/etc/tests/localhost.test.conf +++ b/etc/tests/localhost.test.conf @@ -12,7 +12,8 @@ "trove_version":"v1.0", "trove_api_updated":"2012-08-01T00:00:00Z", - "trove_dns_support":false, + "trove_dns_support":true, + "trove_dns_checker":"trove.tests.fakes.dns.FakeDnsChecker", "trove_ip_support":false, "nova_client": null, diff --git a/etc/trove/trove.conf.test b/etc/trove/trove.conf.test index 5ba77af906..68691b8b59 100644 --- a/etc/trove/trove.conf.test +++ b/etc/trove/trove.conf.test @@ -9,6 +9,12 @@ remote_cinder_client = trove.tests.fakes.nova.fake_create_cinder_client # Fake out the RPC implementation rpc_backend = trove.common.rpc.impl_fake +# Fake out DNS. +trove_dns_support = True +dns_driver = trove.tests.fakes.dns.FakeDnsDriver +dns_instance_entry_factory = trove.tests.fakes.dns.FakeDnsInstanceEntryFactory + + # This will remove some of the verbose logging when trying to diagnose tox issues default_log_levels=routes.middleware=ERROR,trove.common.auth=WARN diff --git a/trove/dns/designate/driver.py b/trove/dns/designate/driver.py index fc0d3d5ba7..3b93f816b3 100644 --- a/trove/dns/designate/driver.py +++ b/trove/dns/designate/driver.py @@ -75,7 +75,7 @@ class DesignateDriver(driver.DnsDriver): self.default_dns_zone = DesignateDnsZone(id=DNS_DOMAIN_ID, name=DNS_DOMAIN_NAME) - def create_entry(self, entry): + def create_entry(self, entry, content): """Creates the entry in the driver at the given dns zone.""" dns_zone = entry.dns_zone or self.default_dns_zone if not dns_zone.id: @@ -86,7 +86,7 @@ class DesignateDriver(driver.DnsDriver): # Record name has to end with a '.' by dns standard record = Record(name=entry.name + '.', type=entry.type, - data=entry.content, + data=content, ttl=entry.ttl, priority=entry.priority) client.records.create(dns_zone.id, record) @@ -137,10 +137,10 @@ class DesignateDriver(driver.DnsDriver): class DesignateInstanceEntryFactory(driver.DnsInstanceEntryFactory): """Defines how instance DNS entries are created for instances.""" - def create_entry(self, instance): + def create_entry(self, instance_id): zone = DesignateDnsZone(id=DNS_DOMAIN_ID, name=DNS_DOMAIN_NAME) # Constructing the hostname by hashing the instance ID. - name = base64.b32encode(hashlib.md5(instance).digest())[:11] + name = base64.b32encode(hashlib.md5(instance_id).digest())[:11] hostname = ("%s.%s" % (name, zone.name)) #Removing the leading dot if present if hostname.endswith('.'): diff --git a/trove/dns/manager.py b/trove/dns/manager.py index 75aa1fa421..64a6d9f153 100644 --- a/trove/dns/manager.py +++ b/trove/dns/manager.py @@ -52,9 +52,8 @@ class DnsManager(object): """ entry = self.entry_factory.create_entry(instance_id) if entry: - entry.content = content LOG.debug("Creating entry address %s." % str(entry)) - self.driver.create_entry(entry) + self.driver.create_entry(entry, content) else: LOG.debug("Entry address not found for instance %s" % instance_id) diff --git a/trove/dns/rsdns/driver.py b/trove/dns/rsdns/driver.py index 7a9bc90486..e05f43bbc9 100644 --- a/trove/dns/rsdns/driver.py +++ b/trove/dns/rsdns/driver.py @@ -117,7 +117,7 @@ class RsDnsDriver(object): msg = "TTL value '--dns_ttl=%s' should be greater than 300" raise Exception(msg % DNS_TTL) - def create_entry(self, entry): + def create_entry(self, entry, content): dns_zone = entry.dns_zone or self.default_dns_zone if dns_zone.id is None: raise TypeError("The entry's dns_zone must have an ID specified.") @@ -127,7 +127,7 @@ class RsDnsDriver(object): future = self.dns_client.records.create( domain=dns_zone.id, record_name=name, - record_data=entry.content, + record_data=content, record_type=entry.type, record_ttl=entry.ttl) try: diff --git a/trove/instance/models.py b/trove/instance/models.py index 5277ac08db..f92f540b8b 100644 --- a/trove/instance/models.py +++ b/trove/instance/models.py @@ -17,6 +17,7 @@ """Model classes that form the core of instances functionality.""" +import re from datetime import datetime from novaclient import exceptions as nova_exceptions from trove.common import cfg @@ -44,6 +45,11 @@ CONF = cfg.CONF LOG = logging.getLogger(__name__) +def filter_ips(ips, regex): + """Filter out IPs not matching regex.""" + return [ip for ip in ips if re.search(regex, ip)] + + def load_server(context, instance_id, server_id): """Loads a server or raises an exception.""" client = create_nova_client(context) @@ -116,26 +122,45 @@ class SimpleInstance(object): """ - def __init__(self, context, db_info, service_status, root_password=None): + def __init__(self, context, db_info, service_status, root_password=None, + ds_version=None, ds=None): self.context = context self.db_info = db_info self.service_status = service_status self.root_pass = root_password - self.ds_version = (datastore_models.DatastoreVersion. - load_by_uuid(self.db_info.datastore_version_id)) - self.ds = (datastore_models.Datastore. - load(self.ds_version.datastore_id)) + if ds_version is None: + self.ds_version = (datastore_models.DatastoreVersion. + load_by_uuid(self.db_info.datastore_version_id)) + if ds is None: + self.ds = (datastore_models.Datastore. + load(self.ds_version.datastore_id)) @property def addresses(self): - #TODO(tim.simpson): Review whether we should keep this... its a mess. + #TODO(tim.simpson): This code attaches two parts of the Nova server to + # db_info: "status" and "addresses". The idea + # originally was to listen to events to update this + # data and store it in the Trove database. + # However, it may have been unwise as a year and a + # half later we still have to load the server anyway + # and this makes the code confusing. if hasattr(self.db_info, 'addresses'): return self.db_info.addresses + else: + return None @property def created(self): return self.db_info.created + @property + def dns_ip_address(self): + """Returns the IP address to be used with DNS.""" + ips = self.get_visible_ip_addresses() + if ips is None or len(ips) < 1: + return None + return ips[0] + @property def flavor_id(self): # Flavor ID is a str in the 1.0 API. @@ -145,6 +170,21 @@ class SimpleInstance(object): def hostname(self): return self.db_info.hostname + def get_visible_ip_addresses(self): + """Returns IPs that will be visible to the user.""" + if self.addresses is None: + return None + IPs = [] + for label in self.addresses: + if (re.search(CONF.network_label_regex, label) and + len(self.addresses[label]) > 0): + IPs.extend([addr.get('addr') + for addr in self.addresses[label]]) + # Includes ip addresses that match the regexp pattern + if CONF.ip_regex: + IPs = filter_ips(IPs, CONF.ip_regex) + return IPs + @property def id(self): return self.db_info.id diff --git a/trove/instance/views.py b/trove/instance/views.py index ea38e314aa..c8511eedf1 100644 --- a/trove/instance/views.py +++ b/trove/instance/views.py @@ -15,35 +15,15 @@ # License for the specific language governing permissions and limitations # under the License. -import re from trove.openstack.common import log as logging from trove.common import cfg from trove.common.views import create_links from trove.instance import models LOG = logging.getLogger(__name__) - CONF = cfg.CONF -def get_ip_address(addresses): - if addresses is None: - return None - IPs = [] - for label in addresses: - if (re.search(CONF.network_label_regex, label) and - len(addresses[label]) > 0): - IPs.extend([addr.get('addr') for addr in addresses[label]]) - # Includes ip addresses that match the regexp pattern - if CONF.ip_regex: - IPs = filter_ips(IPs, CONF.ip_regex) - return IPs - - -def filter_ips(ips, regex): - return [ip for ip in ips if re.search(regex, ip)] - - class InstanceView(object): """Uses a SimpleInstance.""" @@ -98,7 +78,7 @@ class InstanceDetailView(InstanceView): if self.instance.hostname: result['instance']['hostname'] = self.instance.hostname else: - ip = get_ip_address(self.instance.addresses) + ip = self.instance.get_visible_ip_addresses() if ip is not None and len(ip) > 0: result['instance']['ip'] = ip diff --git a/trove/taskmanager/models.py b/trove/taskmanager/models.py index 692782c8f2..53c358610b 100644 --- a/trove/taskmanager/models.py +++ b/trove/taskmanager/models.py @@ -44,7 +44,6 @@ from trove.instance.models import FreshInstance from trove.instance.tasks import InstanceTasks from trove.instance.models import InstanceStatus from trove.instance.models import InstanceServiceStatus -from trove.instance.views import get_ip_address from trove.openstack.common import log as logging from trove.openstack.common.gettextutils import _ from trove.openstack.common.notifier import api as notifier @@ -597,11 +596,12 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): sleep_time=1, time_out=DNS_TIME_OUT) server = self.nova_client.servers.get( self.db_info.compute_instance_id) + self.db_info.addresses = server.addresses LOG.info(_("Creating dns entry...")) - ip = get_ip_address(server.addresses) + ip = self.dns_ip_address if not ip: raise TroveError('Error creating DNS. No IP available.') - dns_client.create_instance_entry(self.id, ip.pop) + dns_client.create_instance_entry(self.id, ip) else: LOG.debug(_("%(gt)s: DNS not enabled for instance: %(id)s") % {'gt': greenthread.getcurrent(), 'id': self.id}) diff --git a/trove/tests/api/instances.py b/trove/tests/api/instances.py index 27f5923d3b..1bf9a4704d 100644 --- a/trove/tests/api/instances.py +++ b/trove/tests/api/instances.py @@ -12,7 +12,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -import hashlib import os import re @@ -53,6 +52,7 @@ from trove import tests from trove.tests.config import CONFIG from trove.tests.util import create_dbaas_client from trove.tests.util.usage import create_usage_verifier +from trove.tests.util import dns_checker from trove.tests.util import iso_time from trove.tests.util.users import Requirements from trove.common.utils import poll_until @@ -114,7 +114,10 @@ class InstanceTestInfo(object): def get_address(self): result = self.dbaas_admin.mgmt.instances.show(self.id) - return result.ip[0] + if not hasattr(result, 'hostname'): + return result.ip[0] + else: + return result.server['addresses'] def get_local_id(self): mgmt_instance = self.dbaas_admin.management.show(self.id) @@ -820,6 +823,19 @@ class TestGuestProcess(object): diagnostic_tests_helper(diagnostics) +@test(depends_on_classes=[WaitForGuestInstallationToFinish], + groups=[GROUP, GROUP_TEST, "dbaas.dns"]) +class DnsTests(object): + + @test + def test_dns_entries_are_found(self): + """Talk to DNS system to ensure entries were created.""" + print("Instance name=%s" % instance_info.name) + client = instance_info.dbaas_admin + mgmt_instance = client.mgmt.instances.show(instance_info.id) + dns_checker(mgmt_instance) + + @test(depends_on_classes=[WaitForGuestInstallationToFinish], groups=[GROUP, GROUP_TEST, "dbaas.guest.start.test"]) class TestAfterInstanceCreatedGuestData(object): @@ -896,14 +912,6 @@ class TestInstanceListing(object): check.links(instance_dict['links']) check.used_volume() - @test(enabled=CONFIG.trove_dns_support) - def test_instance_hostname(self): - instance = dbaas.instances.get(instance_info.id) - assert_equal(200, dbaas.last_http_code) - hostname_prefix = ("%s" % (hashlib.sha1(instance.id).hexdigest())) - instance_hostname_prefix = instance.hostname.split('.')[0] - assert_equal(hostname_prefix, instance_hostname_prefix) - @test def test_get_instance_status(self): result = dbaas.instances.get(instance_info.id) diff --git a/trove/tests/api/instances_delete.py b/trove/tests/api/instances_delete.py index 100720cc4b..93df23e0e4 100644 --- a/trove/tests/api/instances_delete.py +++ b/trove/tests/api/instances_delete.py @@ -18,11 +18,13 @@ import time +from proboscis import after_class from proboscis import before_class from proboscis import test from proboscis import asserts from proboscis.decorators import time_out +from trove.common import cfg from troveclient.compat import exceptions from trove.tests.util import create_dbaas_client from trove.common.utils import poll_until @@ -31,6 +33,8 @@ from trove.tests.util.users import Requirements from trove.tests.api.instances import instance_info from trove.tests.api.instances import VOLUME_SUPPORT +CONF = cfg.CONF + class TestBase(object): @@ -51,12 +55,12 @@ class TestBase(object): def wait_for_instance_status(self, instance_id, status="ACTIVE"): poll_until(lambda: self.dbaas.instances.get(instance_id), lambda instance: instance.status == status, - time_out=10) + time_out=3, sleep_time=1) def wait_for_instance_task_status(self, instance_id, description): poll_until(lambda: self.dbaas.management.show(instance_id), lambda instance: instance.task_description == description, - time_out=10) + time_out=3, sleep_time=1) def is_instance_deleted(self, instance_id): while True: @@ -83,7 +87,7 @@ class TestBase(object): self.delete_instance(instance_id) -@test(runs_after_groups=["services.initialize"], +@test(runs_after_groups=["services.initialize", "dbaas.guest.shutdown"], groups=['dbaas.api.instances.delete']) class ErroredInstanceDelete(TestBase): """ @@ -92,8 +96,12 @@ class ErroredInstanceDelete(TestBase): """ @before_class - def set_up(self): + def set_up_err(self): """Create some flawed instances.""" + from trove.taskmanager.models import CONF + self.old_dns_support = CONF.trove_dns_support + CONF.trove_dns_support = False + super(ErroredInstanceDelete, self).set_up() # Create an instance that fails during server prov. self.server_error = self.create_instance('test_SERVER_ERROR') @@ -108,6 +116,11 @@ class ErroredInstanceDelete(TestBase): # Create an instance that fails while it's been deleted the first time. self.delete_error = self.create_instance('test_ERROR_ON_DELETE') + @after_class(always_run=True) + def clean_up(self): + from trove.taskmanager.models import CONF + CONF.trove_dns_support = self.old_dns_support + @test @time_out(30) def delete_server_error(self): diff --git a/trove/tests/api/mgmt/accounts.py b/trove/tests/api/mgmt/accounts.py index 1cbb4c6ecf..654c60e44f 100644 --- a/trove/tests/api/mgmt/accounts.py +++ b/trove/tests/api/mgmt/accounts.py @@ -180,11 +180,16 @@ class AllAccounts(object): @test(groups=["fake.%s.broken" % GROUP], - depends_on_groups=["services.initialize"]) + depends_on_groups=["services.initialize"], + runs_after_groups=["dbaas.guest.shutdown"]) class AccountWithBrokenInstance(object): @before_class - def setUp(self): + def setUpACCR(self): + from trove.taskmanager.models import CONF + self.old_dns_support = CONF.trove_dns_support + CONF.trove_dns_support = False + self.user = test_config.users.find_user(Requirements(is_admin=True)) self.client = create_dbaas_client(self.user) self.name = 'test_SERVER_ERROR' @@ -224,3 +229,8 @@ class AccountWithBrokenInstance(object): @after_class def tear_down(self): self.client.instances.delete(self.response.id) + + @after_class + def restore_dns(self): + from trove.taskmanager.models import CONF + CONF.trove_dns_support = self.old_dns_support diff --git a/trove/tests/fakes/dns.py b/trove/tests/fakes/dns.py new file mode 100644 index 0000000000..6d1f3b31d3 --- /dev/null +++ b/trove/tests/fakes/dns.py @@ -0,0 +1,85 @@ +# Copyright 2014 Rackspace +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from trove.dns import driver +from proboscis.asserts import fail +from proboscis.asserts import assert_equal +from proboscis.asserts import assert_true +from trove.openstack.common import log as logging + + +LOG = logging.getLogger(__name__) +ENTRIES = {} + + +class FakeDnsDriver(driver.DnsDriver): + + def create_entry(self, entry, content): + """Pretend to create a DNS entry somewhere. + + Since nothing else tests that this works, there's nothing more to do + here. + + """ + entry.content = content + assert_true(entry.name not in ENTRIES) + LOG.debug("Adding fake DNS entry for hostname %s." % entry.name) + ENTRIES[entry.name] = entry + + def delete_entry(self, name, type, dns_zone=None): + LOG.debug("Deleting fake DNS entry for hostname %s" % name) + ENTRIES.pop(name, None) + + +class FakeDnsInstanceEntryFactory(driver.DnsInstanceEntryFactory): + + def create_entry(self, instance_id): + # Construct hostname using pig-latin. + hostname = "%s-lay" % instance_id + LOG.debug("Mapping instance_id %s to hostname %s" + % (instance_id, hostname)) + return driver.DnsEntry(name=hostname, content=None, + type="A", ttl=42, dns_zone=None) + + +class FakeDnsChecker(object): + """Used by tests to make sure a DNS record was written in fake mode.""" + + def __call__(self, mgmt_instance): + """ + Given an instance ID and ip address, confirm that the proper DNS + record was stored in Designate or some other DNS system. + """ + entry = FakeDnsInstanceEntryFactory().create_entry(mgmt_instance.id) + # Confirm DNS entry shown to user is what we expect. + assert_equal(entry.name, mgmt_instance.hostname) + hostname = entry.name + for i in ENTRIES: + print(i) + print("\t%s" % ENTRIES[i]) + assert_true(hostname in ENTRIES, + "Hostname %s not found in DNS entries!" % hostname) + entry = ENTRIES[hostname] + # See if the ip address assigned to the record is what we expect. + # This isn't perfect, but for Fake Mode its good enough. If we + # really want to know exactly what it should be then we should restore + # the ability to return the IP from the API as well as a hostname, + # since that lines up to the DnsEntry's content field. + ip_addresses = mgmt_instance.server['addresses'] + for network_name, ip_list in ip_addresses.items(): + for ip in ip_list: + if entry.content == ip['addr']: + return + fail("Couldn't find IP address %s among these values: %s" + % (entry.content, ip_addresses)) diff --git a/trove/tests/unittests/instance/test_instance_models.py b/trove/tests/unittests/instance/test_instance_models.py new file mode 100644 index 0000000000..ce9631ef95 --- /dev/null +++ b/trove/tests/unittests/instance/test_instance_models.py @@ -0,0 +1,77 @@ +# Copyright 2014 Rackspace Hosting +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from mock import Mock +from testtools import TestCase +from trove.common import cfg +from trove.instance.models import filter_ips +from trove.instance.models import DBInstance +from trove.instance.models import SimpleInstance +from trove.instance.tasks import InstanceTasks + +CONF = cfg.CONF + + +class SimpleInstanceTest(TestCase): + + def setUp(self): + super(SimpleInstanceTest, self).setUp() + db_info = DBInstance(InstanceTasks.BUILDING, name="TestInstance") + self.instance = SimpleInstance(None, db_info, "BUILD", + ds_version=Mock(), ds=Mock()) + db_info.addresses = {"private": [{"addr": "123.123.123.123"}], + "internal": [{"addr": "10.123.123.123"}], + "public": [{"addr": "15.123.123.123"}]} + self.orig_conf = CONF.network_label_regex + self.orig_ip_regex = CONF.ip_regex + + def tearDown(self): + super(SimpleInstanceTest, self).tearDown() + CONF.network_label_regex = self.orig_conf + CONF.ip_start = None + CONF.ip_regex = self.orig_ip_regex + + def test_filter_ips(self): + CONF.network_label_regex = '.*' + CONF.ip_regex = '^(15.|123.)' + ip = self.instance.get_visible_ip_addresses() + ip = filter_ips(ip, CONF.ip_regex) + self.assertTrue(len(ip) == 2) + self.assertTrue('123.123.123.123' in ip) + self.assertTrue('15.123.123.123' in ip) + + def test_one_network_label_exact(self): + CONF.network_label_regex = '^internal$' + ip = self.instance.get_visible_ip_addresses() + self.assertEqual(['10.123.123.123'], ip) + + def test_one_network_label(self): + CONF.network_label_regex = 'public' + ip = self.instance.get_visible_ip_addresses() + self.assertEqual(['15.123.123.123'], ip) + + def test_two_network_labels(self): + CONF.network_label_regex = '^(private|public)$' + ip = self.instance.get_visible_ip_addresses() + self.assertTrue(len(ip) == 2) + self.assertTrue('123.123.123.123' in ip) + self.assertTrue('15.123.123.123' in ip) + + def test_all_network_labels(self): + CONF.network_label_regex = '.*' + ip = self.instance.get_visible_ip_addresses() + self.assertTrue(len(ip) == 3) + self.assertTrue('10.123.123.123' in ip) + self.assertTrue('123.123.123.123' in ip) + self.assertTrue('15.123.123.123' in ip) diff --git a/trove/tests/unittests/instance/test_instance_views.py b/trove/tests/unittests/instance/test_instance_views.py index ff28ec19c2..fc778f63cb 100644 --- a/trove/tests/unittests/instance/test_instance_views.py +++ b/trove/tests/unittests/instance/test_instance_views.py @@ -16,8 +16,6 @@ from mock import Mock from testtools import TestCase from trove.common import cfg -from trove.instance.views import get_ip_address -from trove.instance.views import filter_ips from trove.instance.views import InstanceView from trove.instance.views import InstanceDetailView @@ -39,40 +37,6 @@ class InstanceViewsTest(TestCase): CONF.network_label_regex = self.orig_label_regex CONF.ip_regex = self.orig_ip_regex - def test_one_network_label_exact(self): - CONF.network_label_regex = '^internal$' - ip = get_ip_address(self.addresses) - self.assertEqual(['10.123.123.123'], ip) - - def test_one_network_label(self): - CONF.network_label_regex = 'public' - ip = get_ip_address(self.addresses) - self.assertEqual(['15.123.123.123'], ip) - - def test_two_network_labels(self): - CONF.network_label_regex = '^(private|public)$' - ip = get_ip_address(self.addresses) - self.assertTrue(len(ip) == 2) - self.assertTrue('123.123.123.123' in ip) - self.assertTrue('15.123.123.123' in ip) - - def test_all_network_labels(self): - CONF.network_label_regex = '.*' - ip = get_ip_address(self.addresses) - self.assertTrue(len(ip) == 3) - self.assertTrue('10.123.123.123' in ip) - self.assertTrue('123.123.123.123' in ip) - self.assertTrue('15.123.123.123' in ip) - - def test_filter_ips(self): - CONF.network_label_regex = '.*' - CONF.ip_regex = '^(15.|123.)' - ip = get_ip_address(self.addresses) - ip = filter_ips(ip, CONF.ip_regex) - self.assertTrue(len(ip) == 2) - self.assertTrue('123.123.123.123' in ip) - self.assertTrue('15.123.123.123' in ip) - class InstanceDetailViewTest(TestCase): @@ -92,6 +56,7 @@ class InstanceDetailViewTest(TestCase): self.instance.addresses = {"private": [{"addr": self.ip}]} self.instance.volume_used = '3' self.instance.root_password = 'iloveyou' + self.instance.get_visible_ip_addresses = lambda: ["1.2.3.4"] def tearDown(self): super(InstanceDetailViewTest, self).tearDown() diff --git a/trove/tests/util/__init__.py b/trove/tests/util/__init__.py index 96f3e15ed9..81ee908a6b 100644 --- a/trove/tests/util/__init__.py +++ b/trove/tests/util/__init__.py @@ -177,6 +177,20 @@ def create_nova_client(user, service_type=None): return TestClient(openstack) +def dns_checker(mgmt_instance): + """Given a MGMT instance, ensures DNS provisioning worked. + + Uses a helper class which, given a mgmt instance (returned by the mgmt + API) can confirm that the DNS record provisioned correctly. + """ + skip_if_xml() # The mgmt instance won't look the same, so skip this. + if CONFIG.values.get('trove_dns_checker') is not None: + checker = import_class(CONFIG.trove_dns_checker) + checker()(mgmt_instance) + else: + raise SkipTest("Can't access DNS system to check if DNS provisioned.") + + def process(cmd): process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)