Support snapshot/restore for remote stack

Support snapshot and restore actions for
OS::Heat::Stack.

Blueprint: support-actions-for-remote-stack

Change-Id: I3108e63ed35cb3b04f43fd992c2ca0986ee1a5aa
This commit is contained in:
huangtianhua 2015-05-28 10:13:29 +08:00
parent c03c9cfcbf
commit eaaa717fed
2 changed files with 88 additions and 0 deletions

View File

@ -12,6 +12,7 @@
# under the License.
from oslo_log import log as logging
from oslo_serialization import jsonutils
import six
from heat.common import context
@ -24,6 +25,7 @@ from heat.engine import environment
from heat.engine import function
from heat.engine import properties
from heat.engine import resource
from heat.engine import template
LOG = logging.getLogger(__name__)
@ -185,6 +187,24 @@ class RemoteStack(resource.Resource):
% self.name)
self.heat().actions.suspend(stack_id=self.resource_id)
def handle_snapshot(self):
snapshot = self.heat().stacks.snapshot(stack_id=self.resource_id)
self.data_set('snapshot_id', snapshot['id'])
def handle_restore(self, defn, restore_data):
snapshot_id = restore_data['resource_data']['snapshot_id']
snapshot = self.heat().stacks.snapshot_show(self.resource_id,
snapshot_id)
s_data = snapshot['snapshot']['data']
env = environment.Environment(s_data['environment'])
files = s_data['files']
tmpl = template.Template(s_data['template'], env=env, files=files)
props = function.resolve(self.properties.data)
props[self.TEMPLATE] = jsonutils.dumps(tmpl.t)
props[self.PARAMETERS] = env.params
return defn.freeze(properties=props)
def _needs_update(self, after, before, after_props, before_props,
prev_resource):
# Always issue an update to the remote stack and let the individual
@ -257,6 +277,9 @@ class RemoteStack(resource.Resource):
def check_update_complete(self, *args):
return self._check_action_complete(action=self.UPDATE)
def check_snapshot_complete(self, *args):
return self._check_action_complete(action=self.SNAPSHOT)
def _resolve_attribute(self, name):
try:
stack = self.heat().stacks.get(stack_id=self.resource_id)

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
import copy
from heatclient import exc
@ -392,6 +393,70 @@ class RemoteStackTest(tests_common.HeatTestCase):
'incorrect.',
six.text_type(error))
def test_snapshot(self):
stacks = [get_stack(stack_status='SNAPSHOT_IN_PROGRESS'),
get_stack(stack_status='SNAPSHOT_COMPLETE')]
snapshot = {
'id': 'a29bc9e25aa44f99a9a3d59cd5b0e263',
'status': 'IN_PROGRESS'
}
rsrc = self.create_remote_stack()
self.heat.stacks.get = mock.MagicMock(side_effect=stacks)
self.heat.stacks.snapshot = mock.MagicMock(return_value=snapshot)
scheduler.TaskRunner(rsrc.snapshot)()
self.assertEqual((rsrc.SNAPSHOT, rsrc.COMPLETE), rsrc.state)
self.assertEqual('a29bc9e25aa44f99a9a3d59cd5b0e263',
rsrc.data().get('snapshot_id'))
self.heat.stacks.snapshot.assert_called_with(
stack_id=rsrc.resource_id)
def test_restore(self):
snapshot = {
'id': 'a29bc9e25aa44f99a9a3d59cd5b0e263',
'status': 'IN_PROGRESS'
}
remote_stack = mock.MagicMock()
remote_stack.action = 'SNAPSHOT'
remote_stack.status = 'COMPLETE'
parent, rsrc = self.create_parent_stack()
rsrc.action = rsrc.SNAPSHOT
heat = rsrc._context().clients.client("heat")
heat.stacks.snapshot = mock.MagicMock(return_value=snapshot)
heat.stacks.get = mock.MagicMock(return_value=remote_stack)
scheduler.TaskRunner(parent.snapshot, None)()
self.assertEqual((parent.SNAPSHOT, parent.COMPLETE), parent.state)
data = parent.prepare_abandon()
remote_stack_snapshot = {
'snapshot': {
'id': 'a29bc9e25aa44f99a9a3d59cd5b0e263',
'status': 'COMPLETE',
'data': {
'files': data['files'],
'environment': data['environment'],
'template': template_format.parse(
data['files']['remote_template.yaml'])
}
}
}
fake_snapshot = collections.namedtuple(
'Snapshot', ('data', 'stack_id'))(data, parent.id)
heat.stacks.snapshot_show = mock.MagicMock(
return_value=remote_stack_snapshot)
self.patchobject(rsrc, 'update').return_value = None
rsrc.action = rsrc.UPDATE
rsrc.status = rsrc.COMPLETE
remote_stack.action = 'UPDATE'
parent.restore(fake_snapshot)
self.assertEqual((parent.RESTORE, parent.COMPLETE), parent.state)
def test_resume(self):
stacks = [get_stack(stack_status='RESUME_IN_PROGRESS'),
get_stack(stack_status='RESUME_COMPLETE')]