Support to update instance access
Change-Id: I640cd8b50fd0e0f80a1a45399b8bfdac437ae2b9
This commit is contained in:
parent
680a43002b
commit
4de40cb514
@ -334,7 +334,7 @@ Request Example
|
|||||||
Update instance name
|
Update instance name
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. rest_method:: PATCH /v1.0/{project_id}/instances/{instanceId}
|
.. rest_method:: PUT /v1.0/{project_id}/instances/{instanceId}
|
||||||
|
|
||||||
Update the instance name.
|
Update the instance name.
|
||||||
|
|
||||||
@ -362,7 +362,7 @@ Request Example
|
|||||||
Upgrade datastore version for instance
|
Upgrade datastore version for instance
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. rest_method:: PATCH /v1.0/{project_id}/instances/{instanceId}
|
.. rest_method:: PUT /v1.0/{project_id}/instances/{instanceId}
|
||||||
|
|
||||||
Upgrade datastore version.
|
Upgrade datastore version.
|
||||||
|
|
||||||
@ -394,7 +394,7 @@ Request Example
|
|||||||
Detach replica
|
Detach replica
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. rest_method:: PATCH /v1.0/{project_id}/instances/{instanceId}
|
.. rest_method:: PUT /v1.0/{project_id}/instances/{instanceId}
|
||||||
|
|
||||||
Detaches a replica from its replication source.
|
Detaches a replica from its replication source.
|
||||||
|
|
||||||
@ -426,6 +426,38 @@ Request Example
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Update instance accessbility
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. rest_method:: PUT /v1.0/{project_id}/instances/{instanceId}
|
||||||
|
|
||||||
|
The following operations are supported:
|
||||||
|
|
||||||
|
* If the instance should be exposed to public or not. Not providing
|
||||||
|
``is_public`` means private.
|
||||||
|
* The list of CIDRs that are allowed to access the database service. Not
|
||||||
|
providing ``allowed_cidrs`` means allowing everything.
|
||||||
|
|
||||||
|
Normal response codes: 202
|
||||||
|
|
||||||
|
Request
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters.yaml
|
||||||
|
|
||||||
|
- project_id: project_id
|
||||||
|
- instanceId: instanceId
|
||||||
|
- instance: instance
|
||||||
|
- access: access
|
||||||
|
- access.is_public: access_is_public
|
||||||
|
- access.allowed_cidrs: access_allowed_cidrs
|
||||||
|
|
||||||
|
Request Example
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. literalinclude:: samples/instance-update-access-request.json
|
||||||
|
:language: javascript
|
||||||
|
|
||||||
|
|
||||||
Delete database instance
|
Delete database instance
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"instance": {
|
||||||
|
"access": {
|
||||||
|
"is_public": true,
|
||||||
|
"allowed_cidrs": ["10.0.0.0/24"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added support to show and update the access configuration for the instance.
|
@ -456,6 +456,41 @@ instance = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"update": {
|
||||||
|
"name": "instance:update",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["instance"],
|
||||||
|
"properties": {
|
||||||
|
"instance": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [],
|
||||||
|
"additionalProperties": False,
|
||||||
|
"properties": {
|
||||||
|
"name": non_empty_string,
|
||||||
|
"replica_of": {},
|
||||||
|
"configuration": configuration_id,
|
||||||
|
"datastore_version": non_empty_string,
|
||||||
|
"access": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": False,
|
||||||
|
"properties": {
|
||||||
|
"is_public": {"type": "boolean"},
|
||||||
|
"allowed_cidrs": {
|
||||||
|
"type": "array",
|
||||||
|
"uniqueItems": True,
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}"
|
||||||
|
"(\\/([0-9]|[1-2][0-9]|3[0-2]))"
|
||||||
|
"?$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"resize": {
|
"resize": {
|
||||||
"volume": {
|
"volume": {
|
||||||
|
@ -77,17 +77,7 @@ def create_port(client, name, description, network_id, security_groups,
|
|||||||
port_id = port['port']['id']
|
port_id = port['port']['id']
|
||||||
|
|
||||||
if is_public:
|
if is_public:
|
||||||
public_network_id = get_public_network(client)
|
make_port_public(client, port_id)
|
||||||
if not public_network_id:
|
|
||||||
raise exception.PublicNetworkNotFound()
|
|
||||||
|
|
||||||
fip_body = {
|
|
||||||
"floatingip": {
|
|
||||||
'floating_network_id': public_network_id,
|
|
||||||
'port_id': port_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client.create_floatingip(fip_body)
|
|
||||||
|
|
||||||
return port_id
|
return port_id
|
||||||
|
|
||||||
@ -107,6 +97,26 @@ def delete_port(client, id):
|
|||||||
client.delete_port(id)
|
client.delete_port(id)
|
||||||
|
|
||||||
|
|
||||||
|
def make_port_public(client, port_id):
|
||||||
|
public_network_id = get_public_network(client)
|
||||||
|
if not public_network_id:
|
||||||
|
raise exception.PublicNetworkNotFound()
|
||||||
|
|
||||||
|
fip_body = {
|
||||||
|
"floatingip": {
|
||||||
|
'floating_network_id': public_network_id,
|
||||||
|
'port_id': port_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
client.create_floatingip(fip_body)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(f"Failed to associate public IP with port {port_id}: "
|
||||||
|
f"{str(e)}")
|
||||||
|
raise exception.TroveError('Failed to expose instance port to public.')
|
||||||
|
|
||||||
|
|
||||||
def get_public_network(client):
|
def get_public_network(client):
|
||||||
"""Get public network ID.
|
"""Get public network ID.
|
||||||
|
|
||||||
@ -125,6 +135,24 @@ def get_public_network(client):
|
|||||||
return ret['networks'][0].get('id')
|
return ret['networks'][0].get('id')
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_port_access(client, port_id, is_public):
|
||||||
|
fips = client.list_floatingips(port_id=port_id)["floatingips"]
|
||||||
|
|
||||||
|
if is_public and not fips:
|
||||||
|
# Associate floating IP
|
||||||
|
LOG.debug(f"Associate public IP with port {port_id}")
|
||||||
|
make_port_public(client, port_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not is_public and fips:
|
||||||
|
# Disassociate floating IP
|
||||||
|
for fip in fips:
|
||||||
|
LOG.debug(f"Disassociate public IP {fip['floating_ip_address']} "
|
||||||
|
f"from port {port_id}")
|
||||||
|
client.delete_floatingip(fip["id"])
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def create_security_group(client, name, instance_id):
|
def create_security_group(client, name, instance_id):
|
||||||
body = {
|
body = {
|
||||||
'security_group': {
|
'security_group': {
|
||||||
@ -159,6 +187,15 @@ def create_security_group_rule(client, sg_id, protocol, ports, remote_ips):
|
|||||||
client.create_security_group_rule(body)
|
client.create_security_group_rule(body)
|
||||||
|
|
||||||
|
|
||||||
|
def clear_ingress_security_group_rules(client, sg_id):
|
||||||
|
rules = client.list_security_group_rules(
|
||||||
|
security_group_id=sg_id)['security_group_rules']
|
||||||
|
|
||||||
|
for rule in rules:
|
||||||
|
if rule['direction'] == 'ingress':
|
||||||
|
client.delete_security_group_rule(rule['id'])
|
||||||
|
|
||||||
|
|
||||||
def get_subnet_cidrs(client, network_id=None, subnet_id=None):
|
def get_subnet_cidrs(client, network_id=None, subnet_id=None):
|
||||||
cidrs = []
|
cidrs = []
|
||||||
|
|
||||||
|
@ -1698,6 +1698,10 @@ class Instance(BuiltInstance):
|
|||||||
self.update_db(task_status=InstanceTasks.BUILDING)
|
self.update_db(task_status=InstanceTasks.BUILDING)
|
||||||
task_api.API(self.context).rebuild(self.id, image_id)
|
task_api.API(self.context).rebuild(self.id, image_id)
|
||||||
|
|
||||||
|
def update_access(self, access):
|
||||||
|
self.update_db(task_status=InstanceTasks.UPDATING)
|
||||||
|
task_api.API(self.context).update_access(self.id, access)
|
||||||
|
|
||||||
|
|
||||||
def create_server_list_matcher(server_list):
|
def create_server_list_matcher(server_list):
|
||||||
# Returns a method which finds a server from the given list.
|
# Returns a method which finds a server from the given list.
|
||||||
|
@ -502,7 +502,9 @@ class InstanceController(wsgi.Controller):
|
|||||||
context, request=req)
|
context, request=req)
|
||||||
with StartNotification(context, instance_id=instance.id):
|
with StartNotification(context, instance_id=instance.id):
|
||||||
instance.detach_replica()
|
instance.detach_replica()
|
||||||
if 'configuration_id' in kwargs:
|
|
||||||
|
instance.update_db(**kwargs)
|
||||||
|
elif 'configuration_id' in kwargs:
|
||||||
if kwargs['configuration_id']:
|
if kwargs['configuration_id']:
|
||||||
context.notification = (
|
context.notification = (
|
||||||
notification.DBaaSInstanceAttachConfiguration(context,
|
notification.DBaaSInstanceAttachConfiguration(context,
|
||||||
@ -517,7 +519,9 @@ class InstanceController(wsgi.Controller):
|
|||||||
request=req))
|
request=req))
|
||||||
with StartNotification(context, instance_id=instance.id):
|
with StartNotification(context, instance_id=instance.id):
|
||||||
instance.detach_configuration()
|
instance.detach_configuration()
|
||||||
if 'datastore_version' in kwargs:
|
|
||||||
|
instance.update_db(**kwargs)
|
||||||
|
elif 'datastore_version' in kwargs:
|
||||||
datastore_version = ds_models.DatastoreVersion.load(
|
datastore_version = ds_models.DatastoreVersion.load(
|
||||||
instance.datastore, kwargs['datastore_version'])
|
instance.datastore, kwargs['datastore_version'])
|
||||||
context.notification = (
|
context.notification = (
|
||||||
@ -525,11 +529,17 @@ class InstanceController(wsgi.Controller):
|
|||||||
with StartNotification(context, instance_id=instance.id,
|
with StartNotification(context, instance_id=instance.id,
|
||||||
datastore_version_id=datastore_version.id):
|
datastore_version_id=datastore_version.id):
|
||||||
instance.upgrade(datastore_version)
|
instance.upgrade(datastore_version)
|
||||||
if kwargs:
|
|
||||||
instance.update_db(**kwargs)
|
instance.update_db(**kwargs)
|
||||||
|
elif 'access' in kwargs:
|
||||||
|
instance.update_access(kwargs['access'])
|
||||||
|
|
||||||
def update(self, req, id, body, tenant_id):
|
def update(self, req, id, body, tenant_id):
|
||||||
"""Updates the instance to attach/detach configuration."""
|
"""Updates the instance.
|
||||||
|
|
||||||
|
- attach/detach configuration.
|
||||||
|
- access information.
|
||||||
|
"""
|
||||||
LOG.info("Updating database instance '%(instance_id)s' for tenant "
|
LOG.info("Updating database instance '%(instance_id)s' for tenant "
|
||||||
"'%(tenant_id)s'",
|
"'%(tenant_id)s'",
|
||||||
{'instance_id': id, 'tenant_id': tenant_id})
|
{'instance_id': id, 'tenant_id': tenant_id})
|
||||||
@ -537,12 +547,31 @@ class InstanceController(wsgi.Controller):
|
|||||||
LOG.debug("body: %s", body)
|
LOG.debug("body: %s", body)
|
||||||
context = req.environ[wsgi.CONTEXT_KEY]
|
context = req.environ[wsgi.CONTEXT_KEY]
|
||||||
|
|
||||||
|
name = body['instance'].get('name')
|
||||||
|
if ((name and len(body['instance']) != 2) or
|
||||||
|
(not name and len(body['instance']) == 2)):
|
||||||
|
raise exception.BadRequest("Only one attribute (except 'name') is "
|
||||||
|
"allowed to update.")
|
||||||
|
|
||||||
instance = models.Instance.load(context, id)
|
instance = models.Instance.load(context, id)
|
||||||
self.authorize_instance_action(context, 'update', instance)
|
self.authorize_instance_action(context, 'update', instance)
|
||||||
|
|
||||||
# Make sure args contains a 'configuration_id' argument,
|
|
||||||
args = {}
|
args = {}
|
||||||
args['configuration_id'] = self._configuration_parse(context, body)
|
if name:
|
||||||
|
instance.update_db(name=name)
|
||||||
|
|
||||||
|
detach_replica = ('replica_of' in body['instance'] or
|
||||||
|
'slave_of' in body['instance'])
|
||||||
|
if detach_replica:
|
||||||
|
args['detach_replica'] = detach_replica
|
||||||
|
|
||||||
|
configuration_id = self._configuration_parse(context, body)
|
||||||
|
if configuration_id:
|
||||||
|
args['configuration_id'] = configuration_id
|
||||||
|
|
||||||
|
if 'access' in body['instance']:
|
||||||
|
args['access'] = body['instance']['access']
|
||||||
|
|
||||||
self._modify_instance(context, req, instance, **args)
|
self._modify_instance(context, req, instance, **args)
|
||||||
return wsgi.Result(None, 202)
|
return wsgi.Result(None, 202)
|
||||||
|
|
||||||
|
@ -82,6 +82,7 @@ class InstanceTasks(object):
|
|||||||
LOGGING = InstanceTask(0x0a, 'LOGGING', 'Transferring guest logs.')
|
LOGGING = InstanceTask(0x0a, 'LOGGING', 'Transferring guest logs.')
|
||||||
DETACHING = InstanceTask(0x0b, 'DETACHING',
|
DETACHING = InstanceTask(0x0b, 'DETACHING',
|
||||||
'Detaching the instance from replica source.')
|
'Detaching the instance from replica source.')
|
||||||
|
UPDATING = InstanceTask(0x0c, 'UPDATING', 'Updating the instance.')
|
||||||
|
|
||||||
BUILDING_ERROR_DNS = InstanceTask(0x50, 'BUILDING', 'Build error: DNS.',
|
BUILDING_ERROR_DNS = InstanceTask(0x50, 'BUILDING', 'Build error: DNS.',
|
||||||
is_error=True)
|
is_error=True)
|
||||||
@ -121,6 +122,9 @@ class InstanceTasks(object):
|
|||||||
BUILDING_ERROR_PORT = InstanceTask(0x5c, 'BUILDING',
|
BUILDING_ERROR_PORT = InstanceTask(0x5c, 'BUILDING',
|
||||||
'Build error: Port.',
|
'Build error: Port.',
|
||||||
is_error=True)
|
is_error=True)
|
||||||
|
UPDATING_ERROR_ACCESS = InstanceTask(0x5d, 'UPDATING',
|
||||||
|
'Update error: Access.',
|
||||||
|
is_error=True)
|
||||||
|
|
||||||
|
|
||||||
# Dissuade further additions at run-time.
|
# Dissuade further additions at run-time.
|
||||||
|
@ -179,6 +179,13 @@ class API(object):
|
|||||||
self._cast("delete_instance", version=version,
|
self._cast("delete_instance", version=version,
|
||||||
instance_id=instance_id)
|
instance_id=instance_id)
|
||||||
|
|
||||||
|
def update_access(self, instance_id, access):
|
||||||
|
LOG.debug(f"Making async call to update instance: {instance_id}")
|
||||||
|
version = self.API_BASE_VERSION
|
||||||
|
|
||||||
|
self._cast("update_access", version=version,
|
||||||
|
instance_id=instance_id, access=access)
|
||||||
|
|
||||||
def create_backup(self, backup_info, instance_id):
|
def create_backup(self, backup_info, instance_id):
|
||||||
LOG.debug("Making async call to create a backup for instance: %s",
|
LOG.debug("Making async call to create a backup for instance: %s",
|
||||||
instance_id)
|
instance_id)
|
||||||
|
@ -458,6 +458,16 @@ class Manager(periodic_task.PeriodicTasks):
|
|||||||
with EndNotification(context):
|
with EndNotification(context):
|
||||||
instance_tasks.upgrade(datastore_version)
|
instance_tasks.upgrade(datastore_version)
|
||||||
|
|
||||||
|
def update_access(self, context, instance_id, access):
|
||||||
|
instance_tasks = models.BuiltInstanceTasks.load(context, instance_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
instance_tasks.update_access(access)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(f"Failed to update access configuration for "
|
||||||
|
f"{instance_id}: {str(e)}")
|
||||||
|
self.update_db(task_status=InstanceTasks.UPDATING_ERROR_ACCESS)
|
||||||
|
|
||||||
def create_cluster(self, context, cluster_id):
|
def create_cluster(self, context, cluster_id):
|
||||||
with EndNotification(context, cluster_id=cluster_id):
|
with EndNotification(context, cluster_id=cluster_id):
|
||||||
cluster_tasks = models.load_cluster_tasks(context, cluster_id)
|
cluster_tasks = models.load_cluster_tasks(context, cluster_id)
|
||||||
|
@ -1348,6 +1348,60 @@ class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
|
|||||||
else:
|
else:
|
||||||
return "/dev/%s" % device
|
return "/dev/%s" % device
|
||||||
|
|
||||||
|
def update_access(self, access):
|
||||||
|
LOG.info(f"Updating access for instance {self.id}, access {access}")
|
||||||
|
|
||||||
|
new_is_public = access.get('is_public', False)
|
||||||
|
new_allowed_cidrs = access.get('allowed_cidrs', [])
|
||||||
|
is_public = (self.access.get('is_public', False) if self.access
|
||||||
|
else None)
|
||||||
|
allowed_cidrs = (self.access.get('allowed_cidrs', []) if self.access
|
||||||
|
else None)
|
||||||
|
|
||||||
|
ports = self.neutron_client.list_ports(
|
||||||
|
name='trove-%s' % self.id)['ports']
|
||||||
|
|
||||||
|
if is_public != new_is_public:
|
||||||
|
for port in ports:
|
||||||
|
if 'User port' in port['description']:
|
||||||
|
LOG.debug(f"Updating port {port['id']}, is_public: "
|
||||||
|
f"{new_is_public}")
|
||||||
|
neutron.ensure_port_access(self.neutron_client, port['id'],
|
||||||
|
new_is_public)
|
||||||
|
|
||||||
|
if CONF.trove_security_groups_support:
|
||||||
|
if allowed_cidrs != new_allowed_cidrs:
|
||||||
|
name = f"{CONF.trove_security_group_name_prefix}-{self.id}"
|
||||||
|
sgs = self.neutron_client.list_security_groups(
|
||||||
|
name=name)['security_groups']
|
||||||
|
|
||||||
|
LOG.debug(f"Updating security group rules for instance "
|
||||||
|
f"{self.id}")
|
||||||
|
for sg in sgs:
|
||||||
|
neutron.clear_ingress_security_group_rules(
|
||||||
|
self.neutron_client,
|
||||||
|
sg['id'])
|
||||||
|
|
||||||
|
if new_allowed_cidrs:
|
||||||
|
tcp_ports = CONF.get(self.datastore.name).tcp_ports
|
||||||
|
udp_ports = CONF.get(self.datastore.name).udp_ports
|
||||||
|
|
||||||
|
neutron.create_security_group_rule(
|
||||||
|
self.neutron_client, sg['id'], 'tcp', tcp_ports,
|
||||||
|
new_allowed_cidrs)
|
||||||
|
neutron.create_security_group_rule(
|
||||||
|
self.neutron_client, sg['id'], 'udp', udp_ports,
|
||||||
|
new_allowed_cidrs)
|
||||||
|
else:
|
||||||
|
LOG.warning('Security group not supported.')
|
||||||
|
|
||||||
|
LOG.info(f"Finished to update access for instance {self.id}")
|
||||||
|
self.update_db(
|
||||||
|
task_status=InstanceTasks.NONE,
|
||||||
|
access={'is_public': new_is_public,
|
||||||
|
'allowed_cidrs': new_allowed_cidrs}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BackupTasks(object):
|
class BackupTasks(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -304,6 +304,7 @@ class TestInstanceController(trove_testtools.TestCase):
|
|||||||
instance.attach_configuration = Mock()
|
instance.attach_configuration = Mock()
|
||||||
instance.detach_configuration = Mock()
|
instance.detach_configuration = Mock()
|
||||||
instance.update_db = Mock()
|
instance.update_db = Mock()
|
||||||
|
instance.update_access = Mock()
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def test_modify_instance_with_empty_args(self):
|
def test_modify_instance_with_empty_args(self):
|
||||||
@ -318,16 +319,6 @@ class TestInstanceController(trove_testtools.TestCase):
|
|||||||
self.assertEqual(0, instance.attach_configuration.call_count)
|
self.assertEqual(0, instance.attach_configuration.call_count)
|
||||||
self.assertEqual(0, instance.update_db.call_count)
|
self.assertEqual(0, instance.update_db.call_count)
|
||||||
|
|
||||||
def test_modify_instance_with_nonempty_args_calls_update_db(self):
|
|
||||||
instance = self._setup_modify_instance_mocks()
|
|
||||||
args = {}
|
|
||||||
args['any'] = 'anything'
|
|
||||||
|
|
||||||
self.controller._modify_instance(self.context, self.req,
|
|
||||||
instance, **args)
|
|
||||||
|
|
||||||
instance.update_db.assert_called_once_with(**args)
|
|
||||||
|
|
||||||
def test_modify_instance_with_False_detach_replica_arg(self):
|
def test_modify_instance_with_False_detach_replica_arg(self):
|
||||||
instance = self._setup_modify_instance_mocks()
|
instance = self._setup_modify_instance_mocks()
|
||||||
args = {}
|
args = {}
|
||||||
@ -368,15 +359,12 @@ class TestInstanceController(trove_testtools.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(1, instance.detach_configuration.call_count)
|
self.assertEqual(1, instance.detach_configuration.call_count)
|
||||||
|
|
||||||
def test_modify_instance_with_all_args(self):
|
def test_modify_instance_with_access(self):
|
||||||
instance = self._setup_modify_instance_mocks()
|
instance = self._setup_modify_instance_mocks()
|
||||||
args = {}
|
args = {}
|
||||||
args['detach_replica'] = True
|
args['access'] = {'is_public': True}
|
||||||
args['configuration_id'] = 'some_id'
|
|
||||||
|
|
||||||
self.controller._modify_instance(self.context, self.req,
|
self.controller._modify_instance(self.context, self.req,
|
||||||
instance, **args)
|
instance, **args)
|
||||||
|
|
||||||
self.assertEqual(1, instance.detach_replica.call_count)
|
instance.update_access.assert_called_once_with({'is_public': True})
|
||||||
self.assertEqual(1, instance.attach_configuration.call_count)
|
|
||||||
instance.update_db.assert_called_once_with(**args)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user