Merge "Allow updates for OS::Trove::Instance"
This commit is contained in:
commit
a4851dad45
@ -12,6 +12,7 @@
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
@ -91,6 +92,7 @@ class OSDBInstance(resource.Resource):
|
||||
NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Name of the DB instance to create.'),
|
||||
update_allowed=True,
|
||||
constraints=[
|
||||
constraints.Length(max=255),
|
||||
]
|
||||
@ -99,6 +101,7 @@ class OSDBInstance(resource.Resource):
|
||||
properties.Schema.STRING,
|
||||
_('Reference to a flavor for creating DB instance.'),
|
||||
required=True,
|
||||
update_allowed=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('trove.flavor')
|
||||
]
|
||||
@ -123,6 +126,7 @@ class OSDBInstance(resource.Resource):
|
||||
properties.Schema.INTEGER,
|
||||
_('Database volume size in GB.'),
|
||||
required=True,
|
||||
update_allowed=True,
|
||||
constraints=[
|
||||
constraints.Range(1, 150),
|
||||
]
|
||||
@ -167,6 +171,7 @@ class OSDBInstance(resource.Resource):
|
||||
properties.Schema.LIST,
|
||||
_('List of databases to be created on DB instance creation.'),
|
||||
default=[],
|
||||
update_allowed=True,
|
||||
schema=properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
schema={
|
||||
@ -200,6 +205,7 @@ class OSDBInstance(resource.Resource):
|
||||
properties.Schema.LIST,
|
||||
_('List of users to be created on DB instance creation.'),
|
||||
default=[],
|
||||
update_allowed=True,
|
||||
schema=properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
schema={
|
||||
@ -208,6 +214,7 @@ class OSDBInstance(resource.Resource):
|
||||
_('User name to create a user on instance '
|
||||
'creation.'),
|
||||
required=True,
|
||||
update_allowed=True,
|
||||
constraints=[
|
||||
constraints.Length(max=16),
|
||||
constraints.AllowedPattern(r'[a-zA-Z0-9_]+'
|
||||
@ -220,6 +227,7 @@ class OSDBInstance(resource.Resource):
|
||||
_('Password for those users on instance '
|
||||
'creation.'),
|
||||
required=True,
|
||||
update_allowed=True,
|
||||
constraints=[
|
||||
constraints.AllowedPattern(r'[a-zA-Z0-9_]+'
|
||||
r'[a-zA-Z0-9_@?#\s]*'
|
||||
@ -230,7 +238,8 @@ class OSDBInstance(resource.Resource):
|
||||
properties.Schema.STRING,
|
||||
_('The host from which a user is allowed to '
|
||||
'connect to the database.'),
|
||||
default='%'
|
||||
default='%',
|
||||
update_allowed=True
|
||||
),
|
||||
USER_DATABASES: properties.Schema(
|
||||
properties.Schema.LIST,
|
||||
@ -240,6 +249,7 @@ class OSDBInstance(resource.Resource):
|
||||
properties.Schema.STRING,
|
||||
),
|
||||
required=True,
|
||||
update_allowed=True,
|
||||
constraints=[
|
||||
constraints.Length(min=1),
|
||||
]
|
||||
@ -414,6 +424,170 @@ class OSDBInstance(resource.Resource):
|
||||
]
|
||||
self._verify_check_conditions(checks)
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
updates = {}
|
||||
if prop_diff:
|
||||
instance = self.client().instances.get(self.resource_id)
|
||||
if self.NAME in prop_diff:
|
||||
updates.update({self.NAME: prop_diff[self.NAME]})
|
||||
if self.FLAVOR in prop_diff:
|
||||
flvid = prop_diff[self.FLAVOR]
|
||||
flv = self.client_plugin().get_flavor_id(flvid)
|
||||
updates.update({self.FLAVOR: flv})
|
||||
if self.SIZE in prop_diff:
|
||||
updates.update({self.SIZE: prop_diff[self.SIZE]})
|
||||
if self.DATABASES in prop_diff:
|
||||
current = [d.name
|
||||
for d in self.client().databases.list(instance)]
|
||||
desired = [d[self.DATABASE_NAME]
|
||||
for d in prop_diff[self.DATABASES]]
|
||||
for db in prop_diff[self.DATABASES]:
|
||||
dbname = db[self.DATABASE_NAME]
|
||||
if dbname not in current:
|
||||
db['ACTION'] = self.CREATE
|
||||
for dbname in current:
|
||||
if dbname not in desired:
|
||||
deleted = {self.DATABASE_NAME: dbname,
|
||||
'ACTION': self.DELETE}
|
||||
prop_diff[self.DATABASES].append(deleted)
|
||||
updates.update({self.DATABASES: prop_diff[self.DATABASES]})
|
||||
if self.USERS in prop_diff:
|
||||
current = [u.name
|
||||
for u in self.client().users.list(instance)]
|
||||
desired = [u[self.USER_NAME] for u in prop_diff[self.USERS]]
|
||||
for usr in prop_diff[self.USERS]:
|
||||
if usr[self.USER_NAME] not in current:
|
||||
usr['ACTION'] = self.CREATE
|
||||
for usr in current:
|
||||
if usr not in desired:
|
||||
prop_diff[self.USERS].append({self.USER_NAME: usr,
|
||||
'ACTION': self.DELETE})
|
||||
updates.update({self.USERS: prop_diff[self.USERS]})
|
||||
return updates
|
||||
|
||||
def check_update_complete(self, updates):
|
||||
instance = self.client().instances.get(self.resource_id)
|
||||
if instance.status in self.BAD_STATUSES:
|
||||
raise exception.ResourceInError(
|
||||
resource_status=instance.status,
|
||||
status_reason=self.TROVE_STATUS_REASON.get(instance.status,
|
||||
_("Unknown")))
|
||||
if updates:
|
||||
if instance.status != self.ACTIVE:
|
||||
dmsg = ("Instance is in status %(now)s. Waiting on status"
|
||||
" %(stat)s")
|
||||
LOG.debug(dmsg % {"now": instance.status,
|
||||
"stat": self.ACTIVE})
|
||||
return False
|
||||
try:
|
||||
return (
|
||||
self._update_name(instance, updates.get(self.NAME)) and
|
||||
self._update_flavor(instance, updates.get(self.FLAVOR)) and
|
||||
self._update_size(instance, updates.get(self.SIZE)) and
|
||||
self._update_databases(instance,
|
||||
updates.get(self.DATABASES)) and
|
||||
self._update_users(instance, updates.get(self.USERS))
|
||||
)
|
||||
except Exception as exc:
|
||||
if self.client_plugin().is_client_exception(exc):
|
||||
# the instance could have updated between the time
|
||||
# we retrieve it and try to update it so check again
|
||||
if self.client_plugin().is_over_limit(exc):
|
||||
LOG.debug("API rate limit: %(ex)s. Retrying." %
|
||||
{'ex': six.text_type(exc)})
|
||||
return False
|
||||
if "No change was requested" in six.text_type(exc):
|
||||
LOG.warn(_LW("Unexpected instance state change "
|
||||
"during update. Retrying."))
|
||||
return False
|
||||
raise exc
|
||||
return True
|
||||
|
||||
def _update_name(self, instance, name):
|
||||
if name and instance.name != name:
|
||||
self.client().instances.edit(instance, name=name)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _update_flavor(self, instance, new_flavor):
|
||||
if new_flavor:
|
||||
current_flav = six.text_type(instance.flavor['id'])
|
||||
new_flav = six.text_type(new_flavor)
|
||||
if new_flav != current_flav:
|
||||
dmsg = "Resizing instance flavor from %(old)s to %(new)s"
|
||||
LOG.debug(dmsg % {"old": current_flav, "new": new_flav})
|
||||
self.client().instances.resize_instance(instance, new_flavor)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _update_size(self, instance, new_size):
|
||||
if new_size and instance.volume['size'] != new_size:
|
||||
dmsg = "Resizing instance storage from %(old)s to %(new)s"
|
||||
LOG.debug(dmsg % {"old": instance.volume['size'],
|
||||
"new": new_size})
|
||||
self.client().instances.resize_volume(instance, new_size)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _update_databases(self, instance, databases):
|
||||
if databases:
|
||||
for db in databases:
|
||||
if db.get("ACTION") == self.CREATE:
|
||||
db.pop("ACTION", None)
|
||||
dmsg = "Adding new database %(db)s to instance"
|
||||
LOG.debug(dmsg % {"db": db})
|
||||
self.client().databases.create(instance, [db])
|
||||
elif db.get("ACTION") == self.DELETE:
|
||||
dmsg = ("Deleting existing database %(db)s from "
|
||||
"instance")
|
||||
LOG.debug(dmsg % {"db": db['name']})
|
||||
self.client().databases.delete(instance, db['name'])
|
||||
return True
|
||||
|
||||
def _update_users(self, instance, users):
|
||||
if users:
|
||||
for usr in users:
|
||||
dbs = [{'name': db} for db in usr.get(self.USER_DATABASES,
|
||||
[])]
|
||||
usr[self.USER_DATABASES] = dbs
|
||||
if usr.get("ACTION") == self.CREATE:
|
||||
usr.pop("ACTION", None)
|
||||
dmsg = "Adding new user %(u)s to instance"
|
||||
LOG.debug(dmsg % {"u": usr})
|
||||
self.client().users.create(instance, [usr])
|
||||
elif usr.get("ACTION") == self.DELETE:
|
||||
dmsg = ("Deleting existing user %(u)s from "
|
||||
"instance")
|
||||
LOG.debug(dmsg % {"u": usr['name']})
|
||||
self.client().users.delete(instance, usr['name'])
|
||||
else:
|
||||
newattrs = {}
|
||||
if usr.get(self.USER_HOST):
|
||||
newattrs[self.USER_HOST] = usr[self.USER_HOST]
|
||||
if usr.get(self.USER_PASSWORD):
|
||||
newattrs[self.USER_PASSWORD] = usr[self.USER_PASSWORD]
|
||||
if newattrs:
|
||||
self.client().users.update_attributes(
|
||||
instance,
|
||||
usr['name'], newuserattr=newattrs,
|
||||
hostname=instance.hostname)
|
||||
current = self.client().users.get(instance,
|
||||
usr[self.USER_NAME])
|
||||
dbs = [db['name'] for db in current.databases]
|
||||
desired = [db['name'] for db in
|
||||
usr.get(self.USER_DATABASES, [])]
|
||||
grants = [db for db in desired if db not in dbs]
|
||||
revokes = [db for db in dbs if db not in desired]
|
||||
if grants:
|
||||
self.client().users.grant(instance,
|
||||
usr[self.USER_NAME],
|
||||
grants)
|
||||
if revokes:
|
||||
self.client().users.revoke(instance,
|
||||
usr[self.USER_NAME],
|
||||
revokes)
|
||||
return True
|
||||
|
||||
def handle_delete(self):
|
||||
"""Delete a cloud database instance."""
|
||||
if not self.resource_id:
|
||||
|
@ -11,17 +11,20 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import testtools
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
import six
|
||||
from troveclient.openstack.common.apiclient import exceptions as troveexc
|
||||
from troveclient.v1 import users
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.engine.clients.os import neutron
|
||||
from heat.engine.clients.os import nova
|
||||
from heat.engine.clients.os import trove
|
||||
from heat.engine import resource
|
||||
from heat.engine.resources.openstack.trove import os_database
|
||||
from heat.engine import rsrc_defn
|
||||
from heat.engine import scheduler
|
||||
@ -742,3 +745,274 @@ class OSDBInstanceTest(common.HeatTestCase):
|
||||
scheduler.TaskRunner(instance.create)()
|
||||
self.assertEqual((instance.CREATE, instance.COMPLETE), instance.state)
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
@mock.patch.object(resource.Resource, "client_plugin")
|
||||
@mock.patch.object(resource.Resource, "client")
|
||||
class InstanceUpdateTests(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(InstanceUpdateTests, self).setUp()
|
||||
resource._register_class("OS::Trove::Instance",
|
||||
os_database.OSDBInstance)
|
||||
self._stack = mock.Mock()
|
||||
self._stack.has_cache_data.return_value = False
|
||||
self._stack.db_resource_get.return_value = None
|
||||
testprops = {
|
||||
"name": "testinstance",
|
||||
"flavor": "foo",
|
||||
"datastore_type": "database",
|
||||
"datastore_version": "1",
|
||||
"size": 10,
|
||||
"databases": [
|
||||
{"name": "bar"},
|
||||
{"name": "biff"}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"name": "baz",
|
||||
"password": "password",
|
||||
"databases": ["bar"]
|
||||
},
|
||||
{
|
||||
"name": "deleted",
|
||||
"password": "password",
|
||||
"databases": ["biff"]
|
||||
}
|
||||
]
|
||||
}
|
||||
self._rdef = rsrc_defn.ResourceDefinition('test',
|
||||
os_database.OSDBInstance,
|
||||
properties=testprops)
|
||||
|
||||
def test_handle_no_update(self, mock_client, mock_plugin):
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
self.assertEqual({}, trove.handle_update(None, None, {}))
|
||||
|
||||
def test_handle_update_name(self, mock_client, mock_plugin):
|
||||
prop_diff = {
|
||||
"name": "changed"
|
||||
}
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
self.assertEqual(prop_diff, trove.handle_update(None, None, prop_diff))
|
||||
|
||||
def test_handle_update_databases(self, mock_client, mock_plugin):
|
||||
prop_diff = {
|
||||
"databases": [
|
||||
{"name": "bar",
|
||||
"character_set": "ascii"},
|
||||
{'name': "baz"}
|
||||
]
|
||||
}
|
||||
mget = mock_client().databases.list
|
||||
mbar = mock.Mock(name='bar')
|
||||
mbar.name = 'bar'
|
||||
mbiff = mock.Mock(name='biff')
|
||||
mbiff.name = 'biff'
|
||||
mget.return_value = [mbar, mbiff]
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
expected = {
|
||||
'databases': [
|
||||
{'character_set': 'ascii', 'name': 'bar'},
|
||||
{'ACTION': 'CREATE', 'name': 'baz'},
|
||||
{'ACTION': 'DELETE', 'name': 'biff'}
|
||||
]}
|
||||
self.assertEqual(expected, trove.handle_update(None, None, prop_diff))
|
||||
|
||||
def test_handle_update_users(self, mock_client, mock_plugin):
|
||||
prop_diff = {
|
||||
"users": [
|
||||
{"name": "baz",
|
||||
"password": "changed",
|
||||
"databases": ["bar", "biff"]},
|
||||
{'name': "user2",
|
||||
"password": "password",
|
||||
"databases": ["biff", "bar"]}
|
||||
]
|
||||
}
|
||||
uget = mock_client().users
|
||||
mbaz = mock.Mock(name='baz')
|
||||
mbaz.name = 'baz'
|
||||
mdel = mock.Mock(name='deleted')
|
||||
mdel.name = 'deleted'
|
||||
uget.list.return_value = [mbaz, mdel]
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
expected = {
|
||||
'users': [{
|
||||
'databases': ['bar', 'biff'],
|
||||
'name': 'baz',
|
||||
'password': 'changed'
|
||||
}, {
|
||||
'ACTION': 'CREATE',
|
||||
'databases': ['biff', 'bar'],
|
||||
'name': 'user2',
|
||||
'password': 'password'
|
||||
}, {
|
||||
'ACTION': 'DELETE',
|
||||
'name': 'deleted'
|
||||
}]}
|
||||
self.assertEqual(expected, trove.handle_update(None, None, prop_diff))
|
||||
|
||||
def test_handle_update_flavor(self, mock_client, mock_plugin):
|
||||
prop_diff = {
|
||||
"flavor": "changed"
|
||||
}
|
||||
mock_plugin().get_flavor_id.return_value = 1234
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
expected = {
|
||||
"flavor": 1234
|
||||
}
|
||||
self.assertEqual(expected, trove.handle_update(None, None, prop_diff))
|
||||
|
||||
def test_handle_update_size(self, mock_client, mock_plugin):
|
||||
prop_diff = {
|
||||
"size": 42
|
||||
}
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
expected = {
|
||||
"size": 42
|
||||
}
|
||||
self.assertEqual(expected, trove.handle_update(None, None, prop_diff))
|
||||
|
||||
def test_check_complete_none(self, mock_client, mock_plugin):
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
self.assertTrue(trove.check_update_complete({}))
|
||||
|
||||
def test_check_complete_error(self, mock_client, mock_plugin):
|
||||
mock_instance = mock.Mock(status="ERROR")
|
||||
mock_client().instances.get.return_value = mock_instance
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
exc = self.assertRaises(exception.ResourceInError,
|
||||
trove.check_update_complete,
|
||||
{"foo": "bar"})
|
||||
msg = "The last operation for the database instance failed"
|
||||
self.assertIn(msg, six.text_type(exc))
|
||||
|
||||
def test_check_client_exceptions(self, mock_client, mock_plugin):
|
||||
mock_instance = mock.Mock(status="ACTIVE")
|
||||
mock_client().instances.get.return_value = mock_instance
|
||||
mock_plugin().is_client_exception.return_value = True
|
||||
mock_plugin().is_over_limit.side_effect = [True, False]
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
with mock.patch.object(trove, "_update_flavor") as mupdate:
|
||||
mupdate.side_effect = [Exception("test"),
|
||||
Exception("No change was requested "
|
||||
"because I'm testing")]
|
||||
self.assertFalse(trove.check_update_complete({"foo": "bar"}))
|
||||
self.assertFalse(trove.check_update_complete({"foo": "bar"}))
|
||||
self.assertEqual(2, mupdate.call_count)
|
||||
self.assertEqual(2, mock_plugin().is_client_exception.call_count)
|
||||
|
||||
def test_check_complete_status(self, mock_client, mock_plugin):
|
||||
mock_instance = mock.Mock(status="RESIZING")
|
||||
mock_client().instances.get.return_value = mock_instance
|
||||
updates = {"foo": "bar"}
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
self.assertFalse(trove.check_update_complete(updates))
|
||||
|
||||
def test_check_complete_name(self, mock_client, mock_plugin):
|
||||
mock_instance = mock.Mock(status="ACTIVE", name="mock_instance")
|
||||
mock_client().instances.get.return_value = mock_instance
|
||||
updates = {"name": "changed"}
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
self.assertFalse(trove.check_update_complete(updates))
|
||||
mock_instance.name = "changed"
|
||||
self.assertTrue(trove.check_update_complete(updates))
|
||||
mock_client().instances.edit.assert_called_once_with(mock_instance,
|
||||
name="changed")
|
||||
|
||||
def test_check_complete_databases(self, mock_client, mock_plugin):
|
||||
mock_instance = mock.Mock(status="ACTIVE", name="mock_instance")
|
||||
mock_client().instances.get.return_value = mock_instance
|
||||
updates = {
|
||||
'databases': [
|
||||
{'name': 'bar', "character_set": "ascii"},
|
||||
{'ACTION': 'CREATE', 'name': 'baz'},
|
||||
{'ACTION': 'DELETE', 'name': 'biff'}
|
||||
]}
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
self.assertTrue(trove.check_update_complete(updates))
|
||||
mcreate = mock_client().databases.create
|
||||
mdelete = mock_client().databases.delete
|
||||
mcreate.assert_called_once_with(mock_instance, [{'name': 'baz'}])
|
||||
mdelete.assert_called_once_with(mock_instance, 'biff')
|
||||
|
||||
def test_check_complete_users(self, mock_client, mock_plugin):
|
||||
mock_instance = mock.Mock(status="ACTIVE", name="mock_instance")
|
||||
mock_client().instances.get.return_value = mock_instance
|
||||
mock_plugin().is_client_exception.return_value = False
|
||||
mock_client().users.get.return_value = users.User(None, {
|
||||
"databases": [{
|
||||
"name": "bar"
|
||||
}, {
|
||||
"name": "buzz"
|
||||
}],
|
||||
"name": "baz"
|
||||
}, loaded=True)
|
||||
updates = {
|
||||
'users': [{
|
||||
'databases': ['bar', 'biff'],
|
||||
'name': 'baz',
|
||||
'password': 'changed'
|
||||
}, {
|
||||
'ACTION': 'CREATE',
|
||||
'databases': ['biff', 'bar'],
|
||||
'name': 'user2',
|
||||
'password': 'password'
|
||||
}, {
|
||||
'ACTION': 'DELETE',
|
||||
'name': 'deleted'
|
||||
}]}
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
self.assertTrue(trove.check_update_complete(updates))
|
||||
create_calls = [
|
||||
mock.call(mock_instance, [{'password': 'password',
|
||||
'databases': [{'name': 'biff'},
|
||||
{'name': 'bar'}],
|
||||
'name': 'user2'}])
|
||||
]
|
||||
delete_calls = [
|
||||
mock.call(mock_instance, 'deleted')
|
||||
]
|
||||
mock_client().users.create.assert_has_calls(create_calls)
|
||||
mock_client().users.delete.assert_has_calls(delete_calls)
|
||||
self.assertEqual(1, mock_client().users.create.call_count)
|
||||
self.assertEqual(1, mock_client().users.delete.call_count)
|
||||
updateattr = mock_client().users.update_attributes
|
||||
updateattr.assert_called_once_with(
|
||||
mock_instance, 'baz', newuserattr={'password': 'changed'},
|
||||
hostname=mock.ANY)
|
||||
mock_client().users.grant.assert_called_once_with(
|
||||
mock_instance, 'baz', ['biff'])
|
||||
mock_client().users.revoke.assert_called_once_with(
|
||||
mock_instance, 'baz', ['buzz'])
|
||||
|
||||
def test_check_complete_flavor(self, mock_client, mock_plugin):
|
||||
mock_instance = mock.Mock(status="ACTIVE", flavor={'id': 4567},
|
||||
name="mock_instance")
|
||||
mock_client().instances.get.return_value = mock_instance
|
||||
updates = {
|
||||
"flavor": 1234
|
||||
}
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
self.assertFalse(trove.check_update_complete(updates))
|
||||
mock_instance.status = "RESIZING"
|
||||
self.assertFalse(trove.check_update_complete(updates))
|
||||
mock_instance.status = "ACTIVE"
|
||||
mock_instance.flavor = {'id': 1234}
|
||||
self.assertTrue(trove.check_update_complete(updates))
|
||||
|
||||
def test_check_complete_size(self, mock_client, mock_plugin):
|
||||
mock_instance = mock.Mock(status="ACTIVE", volume={'size': 24},
|
||||
name="mock_instance")
|
||||
mock_client().instances.get.return_value = mock_instance
|
||||
updates = {
|
||||
"size": 42
|
||||
}
|
||||
trove = os_database.OSDBInstance('test', self._rdef, self._stack)
|
||||
self.assertFalse(trove.check_update_complete(updates))
|
||||
mock_instance.status = "RESIZING"
|
||||
self.assertFalse(trove.check_update_complete(updates))
|
||||
mock_instance.status = "ACTIVE"
|
||||
mock_instance.volume = {'size': 42}
|
||||
self.assertTrue(trove.check_update_complete(updates))
|
||||
|
Loading…
x
Reference in New Issue
Block a user