Skip to content

Commit f87ff5d

Browse files
authored
Merge pull request #908 from QualiSystems/develop
Merge dev to master for 1.11.0 release
2 parents c6e7723 + fcc0489 commit f87ff5d

File tree

26 files changed

+536
-36
lines changed

26 files changed

+536
-36
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ install:
77
- pip install "cloudshell-core>=2.2.0,<2.3.0" --extra-index-url https://testpypi.python.org/simple
88
- chmod 777 ./cloudshell_shell_core_install.sh
99
- ./cloudshell_shell_core_install.sh
10-
- pip install "cloudshell-automation-api>=8.2.0.0,<8.3.0.0" --extra-index-url https://testpypi.python.org/simple
10+
- pip install "cloudshell-automation-api>=8.3.0.0,<8.3.1.0" --extra-index-url https://testpypi.python.org/simple
1111
language: python
1212
notifications:
1313
webhools: "https://qualisystems.getbadges.io/api/app/webhook/63350e33-4119-49c3-8127-075aaa022926"

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ A repository for projects providing out of the box capabilities within CloudShel
2828
The CloudShell driver for controlling vCenter via CloudShell.
2929

3030
## Installation
31-
* [QualiSystems CloudShell 7.0](http://www.qualisystems.com/products/cloudshell/cloudshell-overview/)
31+
* [QualiSystems CloudShell](http://www.qualisystems.com/products/cloudshell/cloudshell-overview/)
3232
* [pyvmomi 6.0](https://github.com/vmware/pyvmomi)
3333
* [jsonpickle latest](https://jsonpickle.github.io/)
3434

@@ -47,7 +47,8 @@ A repository for projects providing out of the box capabilities within CloudShel
4747
9. Run Connect All command
4848

4949
## Links
50-
[VmWare vCenter] (https://github.com/vmware/pyvmomi/tree/master/docs)
50+
* [Offline Package] (https://support.quali.com/hc/en-us/articles/231613247)
51+
* [VmWare vCenter] (https://github.com/vmware/pyvmomi/tree/master/docs)
5152

5253
## License
5354
[Apache License 2.0](https://github.com/QualiSystems/vCenterShell/blob/master/LICENSE)

integration/integration_commands/integration_test_refresh_ip.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
from pyVim.connect import SmartConnect, Disconnect
55

66
from cloudshell.cp.vcenter.commands.refresh_ip import RefreshIpCommand
7+
from cloudshell.cp.vcenter.commands.vm_details import VmDetailsCommand
8+
from cloudshell.cp.vcenter.common.vcenter.task_waiter import SynchronousTaskWaiter
79
from cloudshell.cp.vcenter.common.vcenter.vmomi_service import pyVmomiService
810
from cloudshell.cp.vcenter.models.VCenterConnectionDetails import VCenterConnectionDetails
11+
from cloudshell.cp.vcenter.vm.ip_manager import VMIPManager
12+
from cloudshell.cp.vcenter.vm.vm_details_provider import VmDetailsProvider
913
from cloudshell.tests.utils.testing_credentials import TestCredentials
1014
from cloudshell.tests.utils import helpers
1115

1216

1317
class TestRefreshIpCommand(TestCase):
18+
1419
def integrationtest_refresh_ip(self):
1520
resource_context = Mock()
1621
resource_context.attributes = {"vCenter Template": "vCenter/Alex/test"}

package/cloudshell/cp/vcenter/commands/command_orchestrator.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
from datetime import datetime, date
33
import jsonpickle
44

5+
from cloudshell.cp.vcenter.commands.vm_details import VmDetailsCommand
56
from cloudshell.cp.vcenter.models.DeployFromImageDetails import DeployFromImageDetails
67
from cloudshell.shell.core.context import ResourceRemoteCommandContext
8+
79
from cloudshell.cp.vcenter.models.OrchestrationSaveResult import OrchestrationSaveResult
810
from cloudshell.cp.vcenter.models.OrchestrationSavedArtifactsInfo import OrchestrationSavedArtifactsInfo
911
from cloudshell.cp.vcenter.models.OrchestrationSavedArtifact import OrchestrationSavedArtifact
@@ -46,6 +48,7 @@
4648
from cloudshell.cp.vcenter.vm.dvswitch_connector import VirtualSwitchToMachineConnector
4749
from cloudshell.cp.vcenter.vm.ip_manager import VMIPManager
4850
from cloudshell.cp.vcenter.vm.portgroup_configurer import VirtualMachinePortGroupConfigurer
51+
from cloudshell.cp.vcenter.vm.vm_details_provider import VmDetailsProvider
4952
from cloudshell.cp.vcenter.vm.vnic_to_network_mapper import VnicToNetworkMapper
5053
from cloudshell.cp.vcenter.models.DeployFromTemplateDetails import DeployFromTemplateDetails
5154

@@ -67,10 +70,15 @@ def __init__(self):
6770

6871
self.vm_loader = VMLoader(pv_service)
6972

73+
ip_manager = VMIPManager()
74+
vm_details_provider = VmDetailsProvider(pyvmomi_service=pv_service,
75+
ip_manager=ip_manager)
76+
7077
vm_deployer = VirtualMachineDeployer(pv_service=pv_service,
7178
name_generator=generate_unique_name,
7279
ovf_service=ovf_service,
73-
resource_model_parser=ResourceModelParser())
80+
resource_model_parser=ResourceModelParser(),
81+
vm_details_provider=vm_details_provider)
7482

7583
dv_port_group_creator = DvPortGroupCreator(pyvmomi_service=pv_service,
7684
synchronous_task_waiter=synchronous_task_waiter)
@@ -82,6 +90,7 @@ def __init__(self):
8290
name_gen=port_group_name_generator)
8391
virtual_switch_to_machine_connector = VirtualSwitchToMachineConnector(dv_port_group_creator,
8492
virtual_machine_port_group_configurer)
93+
8594
# Command Wrapper
8695
self.command_wrapper = CommandWrapper(pv_service=pv_service,
8796
resource_model_parser=self.resource_model_parser,
@@ -119,13 +128,15 @@ def __init__(self):
119128
VirtualMachinePowerManagementCommand(pyvmomi_service=pv_service,
120129
synchronous_task_waiter=synchronous_task_waiter)
121130

122-
ip_manager = VMIPManager()
123-
124131
# Refresh IP command
125132
self.refresh_ip_command = RefreshIpCommand(pyvmomi_service=pv_service,
126133
resource_model_parser=ResourceModelParser(),
127134
ip_manager=ip_manager)
128135

136+
# Get Vm Details command
137+
self.vm_details = VmDetailsCommand(pyvmomi_service=pv_service,
138+
vm_details_provider=vm_details_provider)
139+
129140
# Save Snapshot
130141
self.snapshot_saver = SaveSnapshotCommand(pyvmomi_service=pv_service,
131142
task_waiter=synchronous_task_waiter)
@@ -391,20 +402,32 @@ def get_vm_uuid_by_name(self, context, vm_name):
391402
vm_name)
392403
return set_command_result(result=res, unpicklable=False)
393404

394-
def save_snapshot(self, context, snapshot_name):
405+
def get_vm_details(self, context,cancellation_context, requests_json):
406+
requests = DeployDataHolder(jsonpickle.decode(requests_json)).items
407+
res = self.command_wrapper.execute_command_with_connection(context,
408+
self.vm_details.get_vm_details,
409+
context.resource,
410+
requests,
411+
cancellation_context)
412+
return set_command_result(result=res, unpicklable=False)
413+
414+
def save_snapshot(self, context, snapshot_name, save_memory='No'):
395415
"""
396416
Saves virtual machine to a snapshot
397417
:param context: resource context of the vCenterShell
398418
:type context: models.QualiDriverModels.ResourceCommandContext
399419
:param snapshot_name: snapshot name to save to
400420
:type snapshot_name: str
421+
:param save_memory: Snapshot the virtual machine's memory. Lookup, Yes / No
422+
:type save_memory: str
401423
:return:
402424
"""
403425
resource_details = self._parse_remote_model(context)
404426
created_snapshot_path = self.command_wrapper.execute_command_with_connection(context,
405427
self.snapshot_saver.save_snapshot,
406428
resource_details.vm_uuid,
407-
snapshot_name)
429+
snapshot_name,
430+
save_memory)
408431
return set_command_result(created_snapshot_path)
409432

410433
def restore_snapshot(self, context, snapshot_name):

package/cloudshell/cp/vcenter/commands/refresh_ip.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,4 @@ def _get_custom_param(custom_params, custom_param_name):
9090

9191
if custom_param_values:
9292
return custom_param_values[0]
93-
return None
93+
return None

package/cloudshell/cp/vcenter/commands/save_snapshot.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def __init__(self, pyvmomi_service, task_waiter):
1919
self.pyvmomi_service = pyvmomi_service
2020
self.task_waiter = task_waiter
2121

22-
def save_snapshot(self, si, logger, vm_uuid, snapshot_name):
22+
def save_snapshot(self, si, logger, vm_uuid, snapshot_name, save_memory):
2323
"""
2424
Creates a snapshot of the current state of the virtual machine
2525
@@ -31,21 +31,33 @@ def save_snapshot(self, si, logger, vm_uuid, snapshot_name):
3131
:type vm_uuid: str
3232
:param snapshot_name: Snapshot name to save the snapshot to
3333
:type snapshot_name: str
34+
:param save_memory: Snapshot the virtual machine's memory. Lookup, Yes / No
35+
:type save_memory: str
3436
"""
3537
vm = self.pyvmomi_service.find_by_uuid(si, vm_uuid)
3638

3739
snapshot_path_to_be_created = SaveSnapshotCommand._get_snapshot_name_to_be_created(snapshot_name, vm)
40+
41+
save_vm_memory_to_snapshot = SaveSnapshotCommand._get_save_vm_memory_to_snapshot(save_memory)
42+
3843
SaveSnapshotCommand._verify_snapshot_uniquness(snapshot_path_to_be_created, vm)
3944

40-
task = self._create_snapshot(logger, snapshot_name, vm)
45+
task = self._create_snapshot(logger, snapshot_name, vm, save_vm_memory_to_snapshot)
4146

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

4550
@staticmethod
46-
def _create_snapshot(logger, snapshot_name, vm):
51+
def _get_save_vm_memory_to_snapshot(save_memory):
52+
return True if save_memory is not None and save_memory.lower() == 'yes' else False
53+
54+
@staticmethod
55+
def _create_snapshot(logger, snapshot_name, vm, save_vm_memory_to_snapshot):
56+
"""
57+
:type save_vm_memory_to_snapshot: bool
58+
"""
4759
logger.info("Create virtual machine snapshot")
48-
dump_memory = False
60+
dump_memory = save_vm_memory_to_snapshot
4961
quiesce = True
5062
task = vm.CreateSnapshot(snapshot_name, 'Created by CloudShell vCenterShell', dump_memory, quiesce)
5163
return task
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import traceback
2+
import time
3+
4+
from cloudshell.cp.vcenter.vm.vm_details_provider import VmDetails, VmDataField
5+
6+
7+
class VmDetailsCommand(object):
8+
def __init__(self, pyvmomi_service, vm_details_provider):
9+
self.pyvmomi_service = pyvmomi_service
10+
self.vm_details_provider = vm_details_provider
11+
self.timeout = 30
12+
self.delay = 1
13+
14+
def get_vm_details(self, si, logger, resource_context, requests, cancellation_context):
15+
results = []
16+
for request in requests:
17+
if cancellation_context.is_cancelled:
18+
break
19+
app_name = request.deployedAppJson.name
20+
try:
21+
vm = self.pyvmomi_service.find_by_uuid(si, request.deployedAppJson.vmdetails.uid)
22+
self._wait_for_vm_to_be_ready(vm, request, logger)
23+
result = self.vm_details_provider.create(
24+
vm=vm,
25+
name=app_name,
26+
reserved_networks=resource_context.attributes.get('Reserved Networks', '').split(';'),
27+
ip_regex=next((p.value for p in request.deployedAppJson.vmdetails.vmCustomParams if p.name=='ip_regex'), None),
28+
deployment_details_provider=DeploymentDetailsProviderFromAppJson(request.appRequestJson.deploymentService),
29+
logger=logger)
30+
except Exception as e:
31+
logger.error("Error getting vm details for '{0}': {1}".format(app_name, traceback.format_exc()))
32+
result = VmDetails(app_name)
33+
result.error = e.message
34+
35+
results.append(result)
36+
37+
return results
38+
39+
def _wait_for_vm_to_be_ready(self, vm, request, logger):
40+
start_time = time.time()
41+
while time.time()-start_time<self.timeout and (self._not_guest_net(vm) or self._no_guest_ip(vm, request)):
42+
time.sleep(self.delay)
43+
logger.info('_wait_for_vm_to_be_ready: '+str(time.time()-start_time)+' sec')
44+
45+
@staticmethod
46+
def _not_guest_net(vm):
47+
return not vm.guest.net
48+
49+
@staticmethod
50+
def _no_guest_ip(vm, request):
51+
wait_for_ip = next((p.value for p in request.deployedAppJson.vmdetails.vmCustomParams if p.name == 'wait_for_ip'), False)
52+
return wait_for_ip and not vm.guest.ipAddress
53+
54+
55+
class DeploymentDetailsProviderFromAppJson(object):
56+
def __init__(self, deployment_service):
57+
self.deployment = deployment_service.model
58+
self.dep_attributes = dict((att.name, att.value) for att in deployment_service.attributes)
59+
60+
def get_details(self):
61+
"""
62+
:rtype list[VmDataField]
63+
"""
64+
data = []
65+
if self.deployment == 'vCenter Clone VM From VM':
66+
data.append(VmDataField('Cloned VM Name', self.dep_attributes.get('vCenter VM', '')))
67+
68+
if self.deployment == 'VCenter Deploy VM From Linked Clone':
69+
template = self.dep_attributes.get('vCenter VM', '')
70+
snapshot = self.dep_attributes.get('vCenter VM Snapshot', '')
71+
data.append(VmDataField('Cloned VM Name', '{0} (snapshot: {1})'.format(template, snapshot)))
72+
73+
if self.deployment == 'vCenter VM From Image':
74+
data.append(VmDataField('Base Image Name', self.dep_attributes.get('vCenter Image', '').split('/')[-1]))
75+
76+
if self.deployment == 'vCenter VM From Template':
77+
data.append(VmDataField('Template Name', self.dep_attributes.get('vCenter Template', '')))
78+
79+
return data

package/cloudshell/cp/vcenter/common/vcenter/model_auto_discovery.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
from cloudshell.cp.vcenter.common.vcenter.task_waiter import SynchronousTaskWaiter
1515
from cloudshell.shell.core.session.cloudshell_session import CloudShellSessionContext
1616

17+
from cloudshell.cp.vcenter.common.utilites.common_utils import back_slash_to_front_converter
18+
1719
DOMAIN = 'Global'
1820
ADDRESS = 'address'
1921
USER = 'User'
@@ -30,6 +32,9 @@
3032
VM_STORAGE = 'VM Storage'
3133
SHUTDOWN_METHODS = ['soft', 'hard']
3234

35+
ATTRIBUTE_NAMES_THAT_ARE_SLASH_BACKSLASH_AGNOSTIC = [DEFAULT_DVSWITCH, DEFAULT_DATACENTER, VM_LOCATION, VM_STORAGE,
36+
VM_RESOURCE_POOL, VM_CLUSTER]
37+
3338

3439
class VCenterAutoModelDiscovery(object):
3540
def __init__(self):
@@ -59,6 +64,8 @@ def validate_and_discover(self, context):
5964
resource = context.resource
6065
si = self._check_if_vcenter_user_pass_valid(context, cloudshell_session, resource.attributes)
6166

67+
resource.attributes = VCenterAutoModelDiscovery._make_attributes_slash_backslash_agnostic(resource.attributes)
68+
6269
auto_attr = []
6370
if not si:
6471
error_message = 'Could not connect to the vCenter: {0}, with given credentials'\
@@ -298,3 +305,20 @@ def get_full_name(dc_name, managed_object, name=''):
298305
if name:
299306
curr_path = '{0}/{1}'.format(managed_object.name, name)
300307
return VCenterAutoModelDiscovery.get_full_name(dc_name, managed_object.parent, curr_path)
308+
309+
@staticmethod
310+
def _make_attributes_slash_backslash_agnostic(attributes_with_slash_or_backslash):
311+
"""
312+
:param attributes_with_slash_or_backslash: resource attributes from
313+
cloudshell.cp.vcenter.models.QualiDriverModels.ResourceContextDetails
314+
:type attributes_with_slash_or_backslash: dict[str,str]
315+
:return: attributes_with_slash
316+
:rtype attributes_with_slash: dict[str,str]
317+
"""
318+
attributes_with_slash = dict()
319+
for key, value in attributes_with_slash_or_backslash.items():
320+
if key in ATTRIBUTE_NAMES_THAT_ARE_SLASH_BACKSLASH_AGNOSTIC:
321+
value = back_slash_to_front_converter(value)
322+
attributes_with_slash[key] = value
323+
324+
return attributes_with_slash

package/cloudshell/cp/vcenter/common/wrappers/command_wrapper.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,9 @@ def execute_command_with_connection(self, context, command, *args):
125125
logger.debug(DEBUG_COMMAND_RESULT.format(str(results)))
126126

127127
return results
128-
except Exception:
128+
except Exception as ex:
129129
logger.exception(COMMAND_ERROR.format(command_name))
130+
logger.exception(str(type(ex)) + ': '+ str(ex))
130131
raise
131132
finally:
132133
logger.info(LOG_FORMAT.format(END, command_name))

package/cloudshell/cp/vcenter/models/DeployResultModel.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class DeployResult(object):
22
def __init__(self, vm_name, vm_uuid, cloud_provider_resource_name, ip_regex, refresh_ip_timeout, auto_power_on,
3-
auto_power_off, wait_for_ip, auto_delete, autoload):
3+
auto_power_off, wait_for_ip, auto_delete, autoload, vm_details_data):
44
"""
55
:param str vm_name: The name of the virtual machine
66
:param uuid uuid: The UUID
@@ -24,3 +24,4 @@ def __init__(self, vm_name, vm_uuid, cloud_provider_resource_name, ip_regex, ref
2424
self.wait_for_ip = str(wait_for_ip)
2525
self.auto_delete = str(auto_delete)
2626
self.autoload = str(autoload)
27+
self.vm_details_data = vm_details_data

0 commit comments

Comments
 (0)