Skip to content

Commit e024a0a

Browse files
authored
Merge pull request #737 from QualiSystems/hotfix/release_1.1_3
Hotfix/release 1.1 3
2 parents 5ea0c48 + 5afb7d5 commit e024a0a

20 files changed

Lines changed: 591 additions & 116 deletions

File tree

deployment_drivers/deploy_clone_from_vm/drivermetadata.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Driver Description="Deploy App From VM" MainClass="driver.DeployCloneFromVMDriver" Name="Deploy Clone From VM Driver" Version="1.0.0">
1+
<Driver Description="Deploy App From VM" MainClass="driver.DeployCloneFromVMDriver" Name="Deploy Clone From VM Driver" Version="1.1.0">
22
<Layout>
33
<Category Name="App Management">
44
<Command Description="" DisplayName="Deploy" Name="Deploy" Tags="allow_shared" />

deployment_drivers/deploy_from_image/drivermetadata.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Driver Description="Deploy App From Image" MainClass="driver.DeployFromImage" Name="VM Deployment From Image" Version="1.0.0">
1+
<Driver Description="Deploy App From Image" MainClass="driver.DeployFromImage" Name="VM Deployment From Image" Version="1.1.0">
22
<Layout>
33
<Category Name="App Management">
44
<Command Description="" DisplayName="Deploy" Name="Deploy" Tags="allow_shared" />

deployment_drivers/deploy_from_linked_clone/drivermetadata.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Driver Description="Deploy App From VM" MainClass="driver.DeployCloneFromVMDriver" Name="Deploy Clone From VM Driver" Version="1.0.0">
1+
<Driver Description="Deploy App From VM" MainClass="driver.DeployCloneFromVMDriver" Name="Deploy Clone From VM Driver" Version="1.1.0">
22
<Layout>
33
<Category Name="App Management">
44
<Command Description="" DisplayName="Deploy" Name="Deploy" Tags="allow_shared" />

deployment_drivers/deploy_from_template/drivermetadata.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Driver Description="Deploy App From Template" MainClass="driver.DeployFromTemplateDriver" Name="VM Deployment From Template" Version="1.0.0">
1+
<Driver Description="Deploy App From Template" MainClass="driver.DeployFromTemplateDriver" Name="VM Deployment From Template" Version="1.1.0">
22
<Layout>
33
<Category Name="App Management">
44
<Command Description="" DisplayName="Deploy" Name="Deploy" Tags="allow_shared" />

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from cloudshell.cp.vcenter.commands.deploy_vm import DeployCommand
66
from cloudshell.cp.vcenter.commands.destroy_vm import DestroyVirtualMachineCommand
77
from cloudshell.cp.vcenter.commands.disconnect_dvswitch import VirtualSwitchToMachineDisconnectCommand
8+
from cloudshell.cp.vcenter.commands.load_vm import VMLoader
89
from cloudshell.cp.vcenter.commands.power_manager_vm import VirtualMachinePowerManagementCommand
910
from cloudshell.cp.vcenter.commands.refresh_ip import RefreshIpCommand
1011
from cloudshell.cp.vcenter.common.cloud_shell.driver_helper import CloudshellDriverHelper
@@ -27,6 +28,7 @@
2728
from cloudshell.cp.vcenter.network.vnic.vnic_service import VNicService
2829
from cloudshell.cp.vcenter.vm.deploy import VirtualMachineDeployer
2930
from cloudshell.cp.vcenter.vm.dvswitch_connector import VirtualSwitchToMachineConnector
31+
from cloudshell.cp.vcenter.vm.ip_manager import VMIPManager
3032
from cloudshell.cp.vcenter.vm.portgroup_configurer import VirtualMachinePortGroupConfigurer
3133
from cloudshell.cp.vcenter.vm.vnic_to_network_mapper import VnicToNetworkMapper
3234
from pyVim.connect import SmartConnect, Disconnect
@@ -51,6 +53,9 @@ def __init__(self):
5153
resource_remover = CloudshellResourceRemover()
5254
ovf_service = OvfImageDeployerService(self.resource_model_parser)
5355

56+
57+
self.vm_loader = VMLoader(pv_service)
58+
5459
vm_deployer = VirtualMachineDeployer(pv_service=pv_service,
5560
name_generator=generate_unique_name,
5661
ovf_service=ovf_service,
@@ -104,9 +109,13 @@ def __init__(self):
104109
self.vm_power_management_command = \
105110
VirtualMachinePowerManagementCommand(pyvmomi_service=pv_service,
106111
synchronous_task_waiter=synchronous_task_waiter)
112+
113+
ip_manager = VMIPManager()
114+
107115
# Refresh IP command
108116
self.refresh_ip_command = RefreshIpCommand(pyvmomi_service=pv_service,
109-
resource_model_parser=ResourceModelParser())
117+
resource_model_parser=ResourceModelParser(),
118+
ip_manager=ip_manager)
110119

111120
def connect_bulk(self, context, request):
112121
results = self.command_wrapper.execute_command_with_connection(
@@ -387,3 +396,9 @@ def power_on_not_roemote(self, context, vm_uuid, resource_fullname):
387396
vm_uuid,
388397
resource_fullname)
389398
return set_command_result(result=res, unpicklable=False)
399+
400+
def get_vm_uuid_by_name(self, context, vm_name):
401+
res = self.command_wrapper.execute_command_with_connection(context,
402+
self.vm_loader.load_vm_uuid_by_name,
403+
vm_name)
404+
return set_command_result(result=res, unpicklable=False)

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

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -143,25 +143,34 @@ def _get_remove_mappings(self, req_to_modes, vm):
143143
if ACTION_TYPE_REMOVE_VLAN in req_to_modes:
144144
for mode, actions in req_to_modes[ACTION_TYPE_REMOVE_VLAN].items():
145145
for action in actions:
146-
interface_attributes = \
147-
[attr.attributeValue for attr in action.connectorAttributes
148-
if attr.attributeName == INTERFACE]
149-
for interface_attribute in interface_attributes:
146+
macs = self._get_macs_from_action(action)
147+
for mac in macs:
150148
vm_network_remove_mapping = VmNetworkRemoveMapping()
151-
vm_network_remove_mapping.mac_address = interface_attribute
149+
vm_network_remove_mapping.mac_address = mac
152150
vm_network_remove_mapping.vm_uuid = vm
153151
remove_mappings.append(vm_network_remove_mapping)
154152
return remove_mappings
155153

154+
def _get_macs_from_action(self, action):
155+
interface_attributes = \
156+
[attr.attributeValue for attr in action.connectorAttributes
157+
if attr.attributeName == INTERFACE]
158+
macs = []
159+
for interface_attribute in interface_attributes:
160+
macs += self._split_names(interface_attribute)
161+
return macs
162+
156163
def _get_set_mappings(self, req_to_modes):
157164
set_mappings = []
158165
if ACTION_TYPE_SET_VLAN in req_to_modes:
159166
set_requests = req_to_modes[ACTION_TYPE_SET_VLAN]
160167
for mode, actions in set_requests.items():
161168
for action in actions:
162169
vnic_name = self._get_vnic_name(action)
163-
vnic_to_network = self._create_map(action.connectionParams.vlanId, mode, vnic_name)
164-
set_mappings.append(vnic_to_network)
170+
vnic_names = self._split_names(vnic_name)
171+
for name in vnic_names:
172+
vnic_to_network = self._create_map(action.connectionParams.vlanId, mode, name)
173+
set_mappings.append(vnic_to_network)
165174

166175
# this line makes sure that the vNICS with names are first
167176
return sorted(set_mappings, key=lambda x: x.vnic_name, reverse=True)
@@ -220,7 +229,7 @@ def _set_vlan(self, action_mappings, si, vm_uuid, logger):
220229
for action in actions:
221230
error_result = self._create_error_action_res(action, e)
222231
results.append(error_result)
223-
232+
results = self._consolidate_duplicate_results(results)
224233
return results
225234

226235
def _prepare_connection_results_for_extraction(self, connection_results):
@@ -256,7 +265,20 @@ def _get_set_vlan_result_suc(self, act_by_mode_by_vlan_by_nic, connection_res_ma
256265
result.type = ACTION_TYPE_SET_VLAN
257266
result.updatedInterface = res.mac_address
258267
results.append(result)
259-
return results
268+
final_res = self._consolidate_duplicate_results(results)
269+
return final_res
270+
271+
def _consolidate_duplicate_results(self, results):
272+
mapping = dict()
273+
final_res = []
274+
for result in results:
275+
self._add_safely_to_dict(value=result, dictionary=mapping, key=result.actionId)
276+
for action_id, actions in mapping.items():
277+
final_act = actions[0]
278+
for action in actions[1:]:
279+
final_act.updatedInterface = '{0},{1}'.format(final_act.updatedInterface, action.updatedInterface)
280+
final_res.append(final_act)
281+
return final_res
260282

261283
def _group_actions_by_vlan_by_vnic(self, set_actions_grouped_by_vlan_id):
262284
set_act_group_by_mode_by_vlan_by_requsted_vnic = dict()
@@ -266,12 +288,21 @@ def _group_actions_by_vlan_by_vnic(self, set_actions_grouped_by_vlan_id):
266288
set_act_group_by_mode_by_vlan_by_requsted_vnic[mode][vlan_id] = dict()
267289
for action in actions:
268290
name = self._get_vnic_name(action)
269-
self._add_safely_to_dict(
270-
dictionary=set_act_group_by_mode_by_vlan_by_requsted_vnic[mode][vlan_id],
271-
key=name,
272-
value=action)
291+
names = self._split_names(name)
292+
for v_name in names:
293+
self._add_safely_to_dict(
294+
dictionary=set_act_group_by_mode_by_vlan_by_requsted_vnic[mode][vlan_id],
295+
key=v_name,
296+
value=action)
273297
return set_act_group_by_mode_by_vlan_by_requsted_vnic
274298

299+
@staticmethod
300+
def _split_names(name):
301+
if not name:
302+
return [name]
303+
304+
return [v_name for v_name in name.strip().split(',') if v_name]
305+
275306
def _group_action_by_vlan_id(self, set_vlan_actions):
276307
set_actions_grouped_by_vlan_id = dict()
277308
for mode, actions in set_vlan_actions.items():
@@ -282,8 +313,7 @@ def _group_action_by_vlan_id(self, set_vlan_actions):
282313
return set_actions_grouped_by_vlan_id
283314

284315
def _remove_vlan(self, action_mappings, si, vm_uuid, logger):
285-
286-
results = []
316+
final_res = []
287317
mode_to_actions = action_mappings.action_tree[ACTION_TYPE_REMOVE_VLAN]
288318
try:
289319
self.logger.info('disconnecting vm({0})'.format(vm_uuid))
@@ -299,9 +329,10 @@ def _remove_vlan(self, action_mappings, si, vm_uuid, logger):
299329
interface_to_action = dict()
300330
for mode, actions in mode_to_actions.items():
301331
for action in actions:
302-
name = self._get_mac(action)
303-
interface_to_action[name] = action
304-
332+
names = self._get_macs_from_action(action)
333+
for name in names:
334+
interface_to_action[name] = action
335+
results = []
305336
for res in connection_results:
306337
action = interface_to_action[res.vnic_mac]
307338
action_result = ActionResult()
@@ -312,13 +343,15 @@ def _remove_vlan(self, action_mappings, si, vm_uuid, logger):
312343
action_result.errorMessage = None
313344
action_result.updatedInterface = res.vnic_mac
314345
results.append(action_result)
346+
final_res = self._consolidate_duplicate_results(results)
315347
except Exception as e:
316348
self.logger.error('Exception raised while disconnecting vm({0}) with exception: {1}'.format(vm_uuid, e))
317349
for mode, actions in mode_to_actions.items():
318350
for action in actions:
319351
error_result = self._create_error_action_res(action, e)
320-
results.append(error_result)
321-
return results
352+
final_res.append(error_result)
353+
final_res = self._consolidate_duplicate_results(final_res)
354+
return final_res
322355

323356
@staticmethod
324357
def _create_error_action_res(action, e):

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
class IpReason(Enum):
55
Success = 0,
66
Timeout = 1,
7-
Cancelled = 2
8-
9-
7+
Cancelled = 2,
8+
NotFound = 3
9+
10+
1011
class IpResult(object):
1112
def __init__(self, ip_address, reason):
1213
self.ip_address = ip_address
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from cloudshell.cp.vcenter.common.vcenter.vm_location import VMLocation
2+
from pyVmomi import vim
3+
4+
5+
class VMLoader(object):
6+
def __init__(self, pv_service):
7+
"""
8+
:type pv_service: cloudshell.cp.vcenter.common.vcenter.vmomi_service.pyVmomiService
9+
"""
10+
self.pv_service = pv_service
11+
12+
def load_vm_uuid_by_name(self, si, vcenter_data_model, vm_name):
13+
"""
14+
Returns the vm uuid
15+
:param si: Service instance to the vcenter
16+
:param vcenter_data_model: vcenter data model
17+
:param vm_name: the vm name
18+
:return: str uuid
19+
"""
20+
path = VMLocation.combine([vcenter_data_model.default_datacenter, vm_name])
21+
paths = path.split('/')
22+
name = paths[len(paths) - 1]
23+
path = VMLocation.combine(paths[:len(paths) - 1])
24+
vm = self.pv_service.find_vm_by_name(si, path, name)
25+
if not vm:
26+
raise ValueError('Could not find the vm in the given path: {0}/{1}'.format(path, name))
27+
28+
if isinstance(vm, vim.VirtualMachine):
29+
return vm.config.uuid
30+
31+
raise ValueError('The given object is not a vm: {0}/{1}'.format(path, name))

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

Lines changed: 14 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ class RefreshIpCommand(object):
1010
INTERVAL = 5
1111
IP_V4_PATTERN = re.compile('^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$')
1212

13-
def __init__(self, pyvmomi_service, resource_model_parser):
13+
def __init__(self, pyvmomi_service, resource_model_parser, ip_manager):
1414
self.pyvmomi_service = pyvmomi_service
1515
self.resource_model_parser = resource_model_parser
16+
self.ip_manager = ip_manager
1617

1718
def refresh_ip(self, si, logger, session, vcenter_data_model, vm_uuid, resource_name, cancellation_context):
1819
"""
@@ -30,36 +31,22 @@ def refresh_ip(self, si, logger, session, vcenter_data_model, vm_uuid, resource_
3031

3132
resource = session.GetResourceDetails(resource_name)
3233

33-
match_function = self._get_ip_match_function(resource)
34+
match_function = self.ip_manager.get_ip_match_function(self._get_ip_refresh_ip_regex(resource))
3435

3536
timeout = self._get_ip_refresh_timeout(resource)
3637

3738
vm = self.pyvmomi_service.find_by_uuid(si, vm_uuid)
3839

39-
if vm.guest.toolsStatus == 'toolsNotInstalled':
40-
logger.warning('VMWare Tools status on virtual machine \'{0}\' are not installed'.format(resource_name))
41-
return None
40+
ip_res = self.ip_manager.get_ip(vm, default_network, match_function, cancellation_context, timeout, logger)
4241

43-
ip_result = self._obtain_ip(vm, default_network, match_function, cancellation_context, timeout, logger)
44-
45-
if ip_result.reason == IpReason.Timeout:
42+
if ip_res.reason == IpReason.Timeout:
4643
raise ValueError('IP address of VM \'{0}\' could not be obtained during {1} seconds'
4744
.format(resource_name, timeout))
4845

49-
if ip_result.reason == IpReason.Success:
50-
session.UpdateResourceAddress(resource_name, ip_result.ip_address)
51-
52-
return ip_result.ip_address
53-
54-
@staticmethod
55-
def _get_ip_match_function(resource):
56-
ip_regex = RefreshIpCommand._get_custom_param(
57-
resource=resource,
58-
custom_param_name='ip_regex')
59-
60-
ip_regex = ip_regex or '.*'
46+
if ip_res.reason == IpReason.Success:
47+
session.UpdateResourceAddress(resource_name, ip_res.ip_address)
6148

62-
return re.compile(ip_regex).match
49+
return ip_res.ip_address
6350

6451
@staticmethod
6552
def _get_ip_refresh_timeout(resource):
@@ -72,6 +59,12 @@ def _get_ip_refresh_timeout(resource):
7259

7360
return float(timeout)
7461

62+
@staticmethod
63+
def _get_ip_refresh_ip_regex(resource):
64+
return RefreshIpCommand._get_custom_param(
65+
resource=resource,
66+
custom_param_name='ip_regex')
67+
7568
@staticmethod
7669
def _get_custom_param(resource, custom_param_name):
7770
custom_param_values = []
@@ -87,40 +80,4 @@ def _get_custom_param(resource, custom_param_name):
8780
return custom_param_values[0]
8881
return None
8982

90-
def _obtain_ip(self, vm, default_network, match_function, cancellation_context, timeout, logger):
91-
time_elapsed = 0
92-
ip = None
93-
interval = self.INTERVAL
94-
while time_elapsed < timeout and ip is None:
95-
if cancellation_context.is_cancelled:
96-
return IpResult(ip, IpReason.Cancelled)
97-
ips = RefreshIpCommand._get_ip_addresses(vm, default_network)
98-
if ips:
99-
logger.debug('Filtering IP adresses to limit to IP V4 \'{0}\''.format(','.join(ips)))
100-
ips = RefreshIpCommand._select_ip_by_match(ips, RefreshIpCommand.IP_V4_PATTERN.match)
101-
if ips:
102-
logger.debug('Filtering IP adresses by custom IP Regex'.format(','.join(ips)))
103-
ips = RefreshIpCommand._select_ip_by_match(ips, match_function)
104-
if ips:
105-
ip = ips[0]
106-
if ip:
107-
return IpResult(ip, IpReason.Success)
108-
time_elapsed += interval
109-
time.sleep(interval)
110-
return IpResult(ip, IpReason.Timeout)
11183

112-
@staticmethod
113-
def _get_ip_addresses(vm, default_network):
114-
ips = []
115-
if vm.guest.ipAddress:
116-
ips.append(vm.guest.ipAddress)
117-
for nic in vm.guest.net:
118-
if nic.network != default_network:
119-
for addr in nic.ipAddress:
120-
if addr:
121-
ips.append(addr)
122-
return ips
123-
124-
@staticmethod
125-
def _select_ip_by_match(ips, match_function):
126-
return [ip for ip in ips if match_function(ip)]

0 commit comments

Comments
 (0)