Add replication slave info to instance show

Update the 'show' response for an instance:

 - If the instance is a replication slave, include the slave_of id.
 - If the instance is a replication master, include the list of slave
   instance ids.

Update the 'list' response to include the slave_of id.
Add CheckInstance tests for slave/slave_of info.

(Unrelated: remove a #TODO comment that has already been addressed.)

Partially Implements: blueprint replication-v1
Closes-Bug: #1340359
Change-Id: If8a154083d0095606fb3ee115cc9b66ab788cbb4
This commit is contained in:
Greg Lucas 2014-07-07 14:12:19 -04:00
parent 315c20cc8c
commit 87b621b2d9
5 changed files with 82 additions and 2 deletions

View File

@ -162,6 +162,8 @@ class SimpleInstance(object):
self.ds = (datastore_models.Datastore.
load(self.ds_version.datastore_id))
self.slave_list = None
@property
def addresses(self):
#TODO(tim.simpson): This code attaches two parts of the Nova server to
@ -239,6 +241,10 @@ class SimpleInstance(object):
def server_id(self):
return self.db_info.compute_instance_id
@property
def slave_of_id(self):
return self.db_info.slave_of_id
@property
def datastore_status(self):
"""
@ -352,6 +358,14 @@ class SimpleInstance(object):
return Configuration.load(self.context,
self.db_info.configuration_id)
@property
def slaves(self):
if self.slave_list is None:
self.slave_list = DBInstance.find_all(tenant_id=self.tenant_id,
slave_of_id=self.id,
deleted=False).all()
return self.slave_list
class DetailInstance(SimpleInstance):
"""A detailed view of an Instance.
@ -1006,8 +1020,6 @@ class Instances(object):
class DBInstance(dbmodels.DatabaseModelBase):
"""Defines the task being executed plus the start time."""
#TODO(tim.simpson): Add start time.
_data_fields = ['name', 'created', 'compute_instance_id',
'task_id', 'task_description', 'task_start_time',
'volume_id', 'deleted', 'tenant_id',

View File

@ -49,6 +49,9 @@ class InstanceView(object):
if ip:
instance_dict['ip'] = ip
if self.instance.slave_of_id is not None:
instance_dict['slave_of'] = self._build_master_info()
LOG.debug(instance_dict)
return {"instance": instance_dict}
@ -65,6 +68,13 @@ class InstanceView(object):
return create_links("flavors", self.req,
self.instance.flavor_id)
def _build_master_info(self):
return {
"id": self.instance.slave_of_id,
"links": create_links("instances", self.req,
self.instance.slave_of_id)
}
class InstanceDetailView(InstanceView):
"""Works with a full-blown instance."""
@ -81,6 +91,9 @@ class InstanceDetailView(InstanceView):
result['instance']['datastore']['version'] = (self.instance.
datastore_version.name)
if self.instance.slaves:
result['instance']['slaves'] = self._build_slaves_info()
if self.instance.configuration is not None:
result['instance']['configuration'] = (self.
_build_configuration_info())
@ -99,6 +112,15 @@ class InstanceDetailView(InstanceView):
return result
def _build_slaves_info(self):
data = []
for slave in self.instance.slaves:
data.append({
"id": slave.id,
"links": create_links("instances", self.req, slave.id)
})
return data
def _build_configuration_info(self):
return {
"id": self.instance.configuration.id,

View File

@ -1459,6 +1459,24 @@ class CheckInstance(AttrCheck):
self.attrs_exist(self.instance['volume'], expected_attrs,
msg="Volume")
def slave_of(self):
if 'slave_of' not in self.instance:
self.fail("'slave_of' not found in instance.")
else:
expected_attrs = ['id', 'links']
self.attrs_exist(self.instance['slave_of'], expected_attrs,
msg="Slave Of")
self.links(self.instance['slave_of']['links'])
def slaves(self):
if 'slaves' not in self.instance:
self.fail("'slaves' not found in instance.")
else:
expected_attrs = ['id', 'links']
for slave in self.instance['slaves']:
self.attrs_exist(slave, expected_attrs, msg="Slave")
self.links(slave['links'])
@test(groups=[GROUP])
class BadInstanceStatusBug():

View File

@ -18,6 +18,7 @@ from proboscis.asserts import assert_raises
from proboscis.decorators import time_out
from trove.common.utils import generate_uuid
from trove.common.utils import poll_until
from trove.tests.api.instances import CheckInstance
from trove.tests.api.instances import instance_info
from trove.tests.api.instances import TIMEOUT_INSTANCE_CREATE
from trove.tests.api.instances import TIMEOUT_INSTANCE_DELETE
@ -111,6 +112,31 @@ class VerifySlave(object):
poll_until(db_is_found)
@test(groups=[GROUP],
depends_on=[WaitForCreateSlaveToFinish],
runs_after=[VerifySlave])
class TestInstanceListing(object):
"""Test replication information in instance listing."""
@test
def test_get_slave_instance(self):
instance = instance_info.dbaas.instances.get(slave_instance.id)
assert_equal(200, instance_info.dbaas.last_http_code)
instance_dict = instance._info
print("instance_dict=%s" % instance_dict)
CheckInstance(instance_dict).slave_of()
assert_equal(instance_info.id, instance_dict['slave_of']['id'])
@test
def test_get_master_instance(self):
instance = instance_info.dbaas.instances.get(instance_info.id)
assert_equal(200, instance_info.dbaas.last_http_code)
instance_dict = instance._info
print("instance_dict=%s" % instance_dict)
CheckInstance(instance_dict).slaves()
assert_equal(slave_instance.id, instance_dict['slaves'][0]['id'])
@test(groups=[GROUP],
depends_on=[WaitForCreateSlaveToFinish],
runs_after=[VerifySlave])

View File

@ -60,6 +60,8 @@ class InstanceDetailViewTest(TestCase):
self.instance.volume_used = '3'
self.instance.root_password = 'iloveyou'
self.instance.get_visible_ip_addresses = lambda: ["1.2.3.4"]
self.instance.slave_of_id = None
self.instance.slaves = []
def tearDown(self):
super(InstanceDetailViewTest, self).tearDown()