Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ python:
- "2.7"

install:
- pip install -r package/requirements.txt --extra-index-url https://testpypi.python.org/simple
- pip install cloudshell-shell-core --extra-index-url https://testpypi.python.org/simple
- pip install cloudshell-core --extra-index-url https://testpypi.python.org/simple
- pip install -r external_requirements.txt
- pip install -r test_requirements.txt
- pip install coveralls
- pip install "cloudshell-core>=2.0.0,<2.1.0" --extra-index-url https://testpypi.python.org/simple
- pip install "cloudshell-shell-core>=2.0.0,<2.1.0" --extra-index-url https://testpypi.python.org/simple
- pip install "cloudshell-automation-api>=7.1.0.0,<7.2.0.0" --extra-index-url https://testpypi.python.org/simple

script:
- pushd package
- pip install "cloudshell-automation-api>=7.1.0.0,<7.2.0.0" --extra-index-url https://testpypi.python.org/simple
- python setup.py develop
- popd
- python runtests.py --with-coverage --cover-package=package --exclude-dir=integration
Expand Down
3 changes: 3 additions & 0 deletions external_requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pyvmomi==6.0.0
jsonpickle==0.9.3
enum==0.4.6
63 changes: 57 additions & 6 deletions package/cloudshell/cp/vcenter/commands/command_orchestrator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import time

from datetime import date
import jsonpickle
from cloudshell.cp.vcenter.models.OrchestrationSaveResult import OrchestrationSaveResult
from cloudshell.cp.vcenter.models.OrchestrationSavedArtifactsInfo import OrchestrationSavedArtifactsInfo
from cloudshell.cp.vcenter.models.OrchestrationSavedArtifact import OrchestrationSavedArtifact
from pyVim.connect import SmartConnect, Disconnect

from cloudshell.cp.vcenter.commands.connect_dvswitch import VirtualSwitchConnectCommand
Expand All @@ -17,7 +20,7 @@
from cloudshell.cp.vcenter.common.cloud_shell.driver_helper import CloudshellDriverHelper
from cloudshell.cp.vcenter.common.cloud_shell.resource_remover import CloudshellResourceRemover
from cloudshell.cp.vcenter.common.model_factory import ResourceModelParser
from cloudshell.cp.vcenter.common.utilites.command_result import set_command_result
from cloudshell.cp.vcenter.common.utilites.command_result import set_command_result, get_result_from_command_output
from cloudshell.cp.vcenter.common.utilites.common_name import generate_unique_name
from cloudshell.cp.vcenter.common.utilites.common_utils import back_slash_to_front_converter
from cloudshell.cp.vcenter.common.utilites.context_based_logger_factory import ContextBasedLoggerFactory
Expand Down Expand Up @@ -406,10 +409,11 @@ def save_snapshot(self, context, snapshot_name):
:return:
"""
resource_details = self._parse_remote_model(context)
self.command_wrapper.execute_command_with_connection(context,
self.snapshot_saver.save_snapshot,
resource_details.vm_uuid,
snapshot_name)
created_snapshot_path = self.command_wrapper.execute_command_with_connection(context,
self.snapshot_saver.save_snapshot,
resource_details.vm_uuid,
snapshot_name)
return set_command_result(created_snapshot_path)

def restore_snapshot(self, context, snapshot_name):
"""
Expand Down Expand Up @@ -439,3 +443,50 @@ def get_snapshots(self, context):
self.snapshots_retriever.get_snapshots,
resource_details.vm_uuid)
return set_command_result(result=res, unpicklable=False)

def orchestration_save(self, context, mode="shallow", custom_params=None):
"""
Creates a snapshot with a unique name and returns SavedResults as JSON
:param context: resource context of the vCenterShell
:param mode: Snapshot save mode, default shallow. Currently not it use
:param custom_params: Set of custom parameter to be supported in the future
:return: SavedResults serialized as JSON
:rtype: SavedResults
"""
resource_details = self._parse_remote_model(context)
created_date = date.today()
snapshot_name = created_date.strftime('%y_%m_%d %H_%M_%S_%f')
created_snapshot_path = self.save_snapshot(context=context, snapshot_name=snapshot_name)

created_snapshot_path = self._strip_double_quotes(created_snapshot_path)

orchestration_saved_artifact = OrchestrationSavedArtifact()
orchestration_saved_artifact.artifact_type = 'vcenter_snapshot'
orchestration_saved_artifact.identifier = created_snapshot_path

saved_artifacts_info = OrchestrationSavedArtifactsInfo(
resource_name=resource_details.cloud_provider,
created_date=created_date,
restore_rules={'requires_same_resource': True},
saved_artifact=orchestration_saved_artifact)

orchestration_save_result = OrchestrationSaveResult(saved_artifacts_info)

return set_command_result(result=orchestration_save_result, unpicklable=False)

@staticmethod
def _strip_double_quotes(created_snapshot_path):
if created_snapshot_path.startswith('"') and created_snapshot_path.endswith('"'):
created_snapshot_path = created_snapshot_path[1:-1]
return created_snapshot_path

def orchestration_restore(self, context, saved_details):
"""

:param context:
:param saved_details:
:return:
"""
saved_artifacts_info = get_result_from_command_output(saved_details)
snapshot_name = saved_artifacts_info['saved_artifacts_info']['saved_artifact']['identifier']
return self.restore_snapshot(context=context, snapshot_name=snapshot_name)
19 changes: 12 additions & 7 deletions package/cloudshell/cp/vcenter/commands/save_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ def save_snapshot(self, si, logger, vm_uuid, snapshot_name):
"""
vm = self.pyvmomi_service.find_by_uuid(si, vm_uuid)

self._verify_snapshot_name_does_not_exists(snapshot_name, vm)
snapshot_path_to_be_created = SaveSnapshotCommand._get_snapshot_name_to_be_created(snapshot_name, vm)
SaveSnapshotCommand._verify_snapshot_uniquness(snapshot_path_to_be_created, vm)

task = self._create_snapshot(logger, snapshot_name, vm)

return self.task_waiter.wait_for_task(task=task, logger=logger, action_name='Create Snapshot')
self.task_waiter.wait_for_task(task=task, logger=logger, action_name='Create Snapshot')
return snapshot_path_to_be_created

@staticmethod
def _create_snapshot(logger, snapshot_name, vm):
Expand All @@ -49,11 +51,14 @@ def _create_snapshot(logger, snapshot_name, vm):
return task

@staticmethod
def _verify_snapshot_name_does_not_exists(snapshot_name, vm):
current_snapshot_name = SnapshotRetriever.get_current_snapshot_name(vm)
if not current_snapshot_name:
return
snapshot_path_to_be_created = SnapshotRetriever.combine(current_snapshot_name, snapshot_name)
def _verify_snapshot_uniquness(snapshot_path_to_be_created, vm):
all_snapshots = SnapshotRetriever.get_vm_snapshots(vm)
if snapshot_path_to_be_created in all_snapshots:
raise SnapshotAlreadyExistsException(SNAPSHOT_ALREADY_EXISTS)

@staticmethod
def _get_snapshot_name_to_be_created(snapshot_name, vm):
current_snapshot_name = SnapshotRetriever.get_current_snapshot_name(vm)
if not current_snapshot_name:
return ''
return SnapshotRetriever.combine(current_snapshot_name, snapshot_name)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class OrchestrationSaveResult(object):
def __init__(self, saved_artifacts_info):
"""
:type saved_artifacts_info: OrchestrationSavedArtifactsInfo
"""
self.saved_artifacts_info = saved_artifacts_info
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class OrchestrationSavedArtifact(object):
def __init__(self):
self.artifact_type = ''
self.identifier = ''
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class OrchestrationSavedArtifactsInfo(object):
def __init__(self, resource_name, created_date, restore_rules, saved_artifact):
"""
:type resource_name: str
:type created_date: date
:type restore_rules: dict
:type saved_artifact: OrchestrationSavedArtifact
"""
self.resource_name = resource_name
self.created_date = created_date
self.restore_rules = restore_rules
self.saved_artifact = saved_artifact
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
from unittest import TestCase

import jsonpickle
from cloudshell.api.cloudshell_api import ResourceInfo
from cloudshell.cp.vcenter.commands.command_orchestrator import CommandOrchestrator
from mock import Mock, create_autospec
from cloudshell.shell.core.driver_context import ResourceRemoteCommandContext, ResourceContextDetails, AppContext
from mock import Mock, create_autospec, patch

RESTORE_SNAPSHOT = 'cloudshell.cp.vcenter.commands.command_orchestrator.CommandOrchestrator.restore_snapshot'
SAVE_SNAPSHOT = 'cloudshell.cp.vcenter.commands.command_orchestrator.CommandOrchestrator.save_snapshot'


class TestCommandOrchestrator(TestCase):
Expand Down Expand Up @@ -124,4 +129,52 @@ def test_restore_snapshot(self):

def test_get_snapshots(self):
self.command_orchestrator.get_snapshots(self.context)
self.assertTrue(self.command_orchestrator.command_wrapper.execute_command_with_connection.called)
self.assertTrue(self.command_orchestrator.command_wrapper.execute_command_with_connection.called)

def test_orchestration_save(self):
# Arrange
with patch(SAVE_SNAPSHOT) as save_snapshot_mock:
save_snapshot_mock.return_value = '"new_snapshot"'

remote_command_context = create_autospec(ResourceRemoteCommandContext)
remote_command_context.resource = create_autospec(ResourceContextDetails)
remote_command_context.resource.fullname = 'vcenter'
endpoint = create_autospec(ResourceContextDetails)
endpoint.fullname = 'vm_111'
endpoint.app_context = create_autospec(AppContext)
endpoint.app_context.deployed_app_json = '{"vmdetails": {"uid": "vm_uuid1"}}'
remote_command_context.remote_endpoints = [endpoint]

# Act
saved_result = CommandOrchestrator().orchestration_save(context=remote_command_context,
mode='shallow',
custom_params=None)

# Assert
save_snapshot_mock.assert_called_once()
saved_result_dict = jsonpickle.decode(saved_result)
self.assertEqual(saved_result_dict['saved_artifacts_info']['saved_artifact']['artifact_type'],
'vcenter_snapshot')
self.assertEqual(saved_result_dict['saved_artifacts_info']['saved_artifact']['identifier'], 'new_snapshot')
self.assertEqual(saved_result_dict['saved_artifacts_info']['resource_name'], 'vcenter')
self.assertIsNotNone(saved_result_dict['saved_artifacts_info']['created_date'])

def test_orchestration_restore(self):
# Arrange
with patch(RESTORE_SNAPSHOT) as mock_restore_snapshot:
# Act
self.command_orchestrator.orchestration_restore(self.context, '''{
"saved_artifacts_info": {
"resource_name": "ex cillum sed laboris",
"created_date": "4313-10-12T18:03:15.053Z",
"restore_rules": {
"requires_same_resource": false
},
"saved_artifact": {
"artifact_type": "veniam in qui",
"identifier": "deserunt1"
}
}
}''')
# Assert
mock_restore_snapshot.assert_called_once_with(context=self.context, snapshot_name='deserunt1')
4 changes: 2 additions & 2 deletions test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ coverage
unittest2
mock
teamcity-messages
jsonpickle
nose-exclude
nose-exclude
coveralls
6 changes: 6 additions & 0 deletions vcentershell_driver/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,5 +105,11 @@ def remote_get_snapshots(self, context, ports):
"""
return self.command_orchestrator.get_snapshots(context)

def orchestration_save(self, context, ports, mode="shallow", custom_params=None):
return self.command_orchestrator.orchestration_save(context, mode, custom_params)

def orchestration_restore(self, context, ports, saved_details):
return self.command_orchestrator.orchestration_restore(context, saved_details)

def get_vm_uuid(self, context, vm_name):
return self.command_orchestrator.get_vm_uuid_by_name(context, vm_name)
2 changes: 2 additions & 0 deletions vcentershell_driver/drivermetadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
<Command Description="" DisplayName="Power Cycle" Name="PowerCycle" Tags="power" />
<Command Description="" DisplayName="Delete VM Only" Name="destroy_vm_only" Tags="remote_app_management,allow_shared" />
<Command Description="" DisplayName="Get VM Uuid" Name="get_vm_uuid" Tags="allow_shared" />
<Command Description="" DisplayName="Orchestration Save" Name="orchestration_save" Tags="remote_connectivity,allow_unreserved" />
<Command Description="" DisplayName="Orchestration Restore" Name="orchestration_restore" Tags="remote_connectivity,allow_unreserved" />
</Category>
<Category Name="Power">
<Command Description="" DisplayName="Power On" Name="PowerOn" Tags="power" />
Expand Down