1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
28 from progressbar
import Percentage
, Bar
, ETA
, FileTransferSpeed
, ProgressBar
38 from pyVmomi
import vim
, vmodl
39 from pyVim
.connect
import SmartConnect
, Disconnect
41 from xml
.etree
import ElementTree
as XmlElementTree
42 from lxml
import etree
as lxmlElementTree
45 from pyvcloud
import Http
46 from pyvcloud
.vcloudair
import VCA
47 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
import sessionType
, organizationType
, \
48 vAppType
, organizationListType
, vdcType
, catalogType
, queryRecordViewType
, \
49 networkType
, vcloudType
, taskType
, diskType
, vmsType
, vdcTemplateListType
, mediaType
50 from xml
.sax
.saxutils
import escape
52 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TaskType
53 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.taskType
import TaskType
as GenericTask
54 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.vAppType
import TaskType
as VappTask
55 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TasksInProgressType
68 # global variable for vcd connector type
69 STANDALONE
= 'standalone'
71 # key for flavor dicts
72 FLAVOR_RAM_KEY
= 'ram'
73 FLAVOR_VCPUS_KEY
= 'vcpus'
74 FLAVOR_DISK_KEY
= 'disk'
75 DEFAULT_IP_PROFILE
= {'dhcp_count':50,
79 # global variable for wait time
85 __author__
= "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
86 __date__
= "$12-Jan-2017 11:09:29$"
89 # -1: "Could not be created",
95 # 5: "Waiting for user input",
97 # 7: "Unrecognized state",
99 # 9: "Inconsistent state",
100 # 10: "Children do not all have the same status",
101 # 11: "Upload initiated, OVF descriptor pending",
102 # 12: "Upload initiated, copying contents",
103 # 13: "Upload initiated , disk contents pending",
104 # 14: "Upload has been quarantined",
105 # 15: "Upload quarantine period has expired"
107 # mapping vCD status to MANO
108 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
117 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
118 'ERROR': 'ERROR', 'DELETED': 'DELETED'
121 class vimconnector(vimconn
.vimconnector
):
122 # dict used to store flavor in memory
125 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
126 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}, persistent_info
={}):
128 Constructor create vmware connector to vCloud director.
130 By default construct doesn't validate connection state. So client can create object with None arguments.
131 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
133 a) It initialize organization UUID
134 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
137 uuid - is organization uuid.
138 name - is organization name that must be presented in vCloud director.
139 tenant_id - is VDC uuid it must be presented in vCloud director
140 tenant_name - is VDC name.
141 url - is hostname or ip address of vCloud director
142 url_admin - same as above.
143 user - is user that administrator for organization. Caller must make sure that
144 username has right privileges.
146 password - is password for a user.
148 VMware connector also requires PVDC administrative privileges and separate account.
149 This variables must be passed via config argument dict contains keys
151 dict['admin_username']
152 dict['admin_password']
153 config - Provide NSX and vCenter information
159 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
160 url_admin
, user
, passwd
, log_level
, config
)
162 self
.logger
= logging
.getLogger('openmano.vim.vmware')
163 self
.logger
.setLevel(10)
164 self
.persistent_info
= persistent_info
169 self
.url_admin
= url_admin
170 self
.tenant_id
= tenant_id
171 self
.tenant_name
= tenant_name
175 self
.admin_password
= None
176 self
.admin_user
= None
178 self
.nsx_manager
= None
180 self
.nsx_password
= None
182 if tenant_name
is not None:
183 orgnameandtenant
= tenant_name
.split(":")
184 if len(orgnameandtenant
) == 2:
185 self
.tenant_name
= orgnameandtenant
[1]
186 self
.org_name
= orgnameandtenant
[0]
188 self
.tenant_name
= tenant_name
189 if "orgname" in config
:
190 self
.org_name
= config
['orgname']
193 self
.logger
.setLevel(getattr(logging
, log_level
))
196 self
.admin_user
= config
['admin_username']
197 self
.admin_password
= config
['admin_password']
199 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
202 self
.nsx_manager
= config
['nsx_manager']
203 self
.nsx_user
= config
['nsx_user']
204 self
.nsx_password
= config
['nsx_password']
206 raise vimconn
.vimconnException(message
="Error: nsx manager or nsx user or nsx password is empty in Config")
208 self
.vcenter_ip
= config
.get("vcenter_ip", None)
209 self
.vcenter_port
= config
.get("vcenter_port", None)
210 self
.vcenter_user
= config
.get("vcenter_user", None)
211 self
.vcenter_password
= config
.get("vcenter_password", None)
213 # ############# Stub code for SRIOV #################
215 # self.dvs_name = config['dv_switch_name']
217 # raise vimconn.vimconnException(message="Error: distributed virtaul switch name is empty in Config")
219 # self.vlanID_range = config.get("vlanID_range", None)
225 raise vimconn
.vimconnException('url param can not be NoneType')
227 if not self
.url_admin
: # try to use normal url
228 self
.url_admin
= self
.url
230 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
231 self
.tenant_id
, self
.tenant_name
))
232 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
233 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
235 # initialize organization
236 if self
.user
is not None and self
.passwd
is not None and self
.url
:
237 self
.init_organization()
239 def __getitem__(self
, index
):
242 if index
== 'tenant_id':
243 return self
.tenant_id
244 if index
== 'tenant_name':
245 return self
.tenant_name
248 elif index
== 'org_name':
250 elif index
== 'org_uuid':
252 elif index
== 'user':
254 elif index
== 'passwd':
258 elif index
== 'url_admin':
259 return self
.url_admin
260 elif index
== "config":
263 raise KeyError("Invalid key '%s'" % str(index
))
265 def __setitem__(self
, index
, value
):
268 if index
== 'tenant_id':
269 self
.tenant_id
= value
270 if index
== 'tenant_name':
271 self
.tenant_name
= value
274 elif index
== 'org_name':
275 self
.org_name
= value
276 elif index
== 'org_uuid':
277 self
.org_uuid
= value
278 elif index
== 'user':
280 elif index
== 'passwd':
284 elif index
== 'url_admin':
285 self
.url_admin
= value
287 raise KeyError("Invalid key '%s'" % str(index
))
289 def connect_as_admin(self
):
290 """ Method connect as pvdc admin user to vCloud director.
291 There are certain action that can be done only by provider vdc admin user.
292 Organization creation / provider network creation etc.
295 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
298 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
300 vca_admin
= VCA(host
=self
.url
,
301 username
=self
.admin_user
,
302 service_type
=STANDALONE
,
306 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
308 raise vimconn
.vimconnConnectionException(
309 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
310 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
313 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
318 """ Method connect as normal user to vCloud director.
321 The return vca object that letter can be used to connect to vCloud director as admin for VDC
325 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
328 vca
= VCA(host
=self
.url
,
330 service_type
=STANDALONE
,
335 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
337 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
338 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
341 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
344 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
345 "{} as user: {}".format(self
.org_name
, self
.user
))
349 def init_organization(self
):
350 """ Method initialize organization UUID and VDC parameters.
352 At bare minimum client must provide organization name that present in vCloud director and VDC.
354 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
355 The Org - UUID will be initialized at the run time if data center present in vCloud director.
358 The return vca object that letter can be used to connect to vcloud direct as admin
361 if self
.org_uuid
is None:
362 org_dict
= self
.get_org_list()
364 # we set org UUID at the init phase but we can do it only when we have valid credential.
365 if org_dict
[org
] == self
.org_name
:
367 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
370 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
372 # if well good we require for org details
373 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
375 # we have two case if we want to initialize VDC ID or VDC name at run time
376 # tenant_name provided but no tenant id
377 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
378 vdcs_dict
= org_details_dict
['vdcs']
379 for vdc
in vdcs_dict
:
380 if vdcs_dict
[vdc
] == self
.tenant_name
:
382 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
386 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
387 # case two we have tenant_id but we don't have tenant name so we find and set it.
388 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
389 vdcs_dict
= org_details_dict
['vdcs']
390 for vdc
in vdcs_dict
:
391 if vdc
== self
.tenant_id
:
392 self
.tenant_name
= vdcs_dict
[vdc
]
393 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
397 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
398 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
400 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
401 self
.logger
.debug(traceback
.format_exc())
404 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
405 """ Method adds a new tenant to VIM with this name.
406 This action requires access to create VDC action in vCloud director.
409 tenant_name is tenant_name to be created.
410 tenant_description not used for this call
413 returns the tenant identifier in UUID format.
414 If action is failed method will throw vimconn.vimconnException method
416 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
417 if vdc_task
is not None:
418 vdc_uuid
, value
= vdc_task
.popitem()
419 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
422 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
424 def delete_tenant(self
, tenant_id
=None):
425 """Delete a tenant from VIM"""
426 'Returns the tenant identifier'
427 raise vimconn
.vimconnNotImplemented("Should have implemented this")
429 def get_tenant_list(self
, filter_dict
={}):
430 """Obtain tenants of VIM
431 filter_dict can contain the following keys:
432 name: filter by tenant name
433 id: filter by tenant uuid/id
435 Returns the tenant list of dictionaries:
436 [{'name':'<name>, 'id':'<id>, ...}, ...]
439 org_dict
= self
.get_org(self
.org_uuid
)
440 vdcs_dict
= org_dict
['vdcs']
445 entry
= {'name': vdcs_dict
[k
], 'id': k
}
446 # if caller didn't specify dictionary we return all tenants.
447 if filter_dict
is not None and filter_dict
:
448 filtered_entry
= entry
.copy()
449 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
450 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
451 if filter_dict
== entry
:
452 vdclist
.append(filtered_entry
)
454 vdclist
.append(entry
)
456 self
.logger
.debug("Error in get_tenant_list()")
457 self
.logger
.debug(traceback
.format_exc())
458 raise vimconn
.vimconnException("Incorrect state. {}")
462 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
463 """Adds a tenant network to VIM
465 net_type can be 'bridge','data'.'ptp'.
466 ip_profile is a dict containing the IP parameters of the network
468 Returns the network identifier"""
470 self
.logger
.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
471 .format(net_name
, net_type
, ip_profile
, shared
))
477 # ############# Stub code for SRIOV #################
478 # if net_type == "data" or net_type == "ptp":
479 # if self.config.get('dv_switch_name') == None:
480 # raise vimconn.vimconnConflictException("You must provide 'dv_switch_name' at config value")
481 # network_uuid = self.create_dvPort_group(net_name)
483 network_uuid
= self
.create_network(network_name
=net_name
, net_type
=net_type
,
484 ip_profile
=ip_profile
, isshared
=isshared
)
485 if network_uuid
is not None:
488 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
490 def get_vcd_network_list(self
):
491 """ Method available organization for a logged in tenant
494 The return vca object that letter can be used to connect to vcloud direct as admin
497 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
500 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
502 if not self
.tenant_name
:
503 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
505 vdc
= vca
.get_vdc(self
.tenant_name
)
507 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
509 vdc_uuid
= vdc
.get_id().split(":")[3]
510 networks
= vca
.get_networks(vdc
.get_name())
513 for network
in networks
:
515 netid
= network
.get_id().split(":")
519 filter_dict
["name"] = network
.get_name()
520 filter_dict
["id"] = netid
[3]
521 filter_dict
["shared"] = network
.get_IsShared()
522 filter_dict
["tenant_id"] = vdc_uuid
523 if network
.get_status() == 1:
524 filter_dict
["admin_state_up"] = True
526 filter_dict
["admin_state_up"] = False
527 filter_dict
["status"] = "ACTIVE"
528 filter_dict
["type"] = "bridge"
529 network_list
.append(filter_dict
)
530 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
532 self
.logger
.debug("Error in get_vcd_network_list")
533 self
.logger
.debug(traceback
.format_exc())
536 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
539 def get_network_list(self
, filter_dict
={}):
540 """Obtain tenant networks of VIM
542 name: network name OR/AND
543 id: network uuid OR/AND
544 shared: boolean OR/AND
545 tenant_id: tenant OR/AND
546 admin_state_up: boolean
549 [{key : value , key : value}]
551 Returns the network list of dictionaries:
552 [{<the fields at Filter_dict plus some VIM specific>}, ...]
556 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
559 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
561 if not self
.tenant_name
:
562 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
564 vdc
= vca
.get_vdc(self
.tenant_name
)
566 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
569 vdcid
= vdc
.get_id().split(":")[3]
570 networks
= vca
.get_networks(vdc
.get_name())
573 for network
in networks
:
575 net_uuid
= network
.get_id().split(":")
576 if len(net_uuid
) != 4:
579 net_uuid
= net_uuid
[3]
581 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
584 filter_entry
["name"] = network
.get_name()
585 filter_entry
["id"] = net_uuid
586 filter_entry
["shared"] = network
.get_IsShared()
587 filter_entry
["tenant_id"] = vdcid
588 if network
.get_status() == 1:
589 filter_entry
["admin_state_up"] = True
591 filter_entry
["admin_state_up"] = False
592 filter_entry
["status"] = "ACTIVE"
593 filter_entry
["type"] = "bridge"
594 filtered_entry
= filter_entry
.copy()
596 if filter_dict
is not None and filter_dict
:
597 # we remove all the key : value we don't care and match only
599 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
600 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
601 if filter_dict
== filter_entry
:
602 network_list
.append(filtered_entry
)
604 network_list
.append(filtered_entry
)
606 self
.logger
.debug("Error in get_vcd_network_list")
607 self
.logger
.debug(traceback
.format_exc())
609 self
.logger
.debug("Returning {}".format(network_list
))
612 def get_network(self
, net_id
):
613 """Method obtains network details of net_id VIM network
614 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
618 raise vimconn
.vimconnConnectionException("self.connect() is failed")
621 vdc
= vca
.get_vdc(self
.tenant_name
)
622 vdc_id
= vdc
.get_id().split(":")[3]
624 networks
= vca
.get_networks(vdc
.get_name())
627 for network
in networks
:
628 vdc_network_id
= network
.get_id().split(":")
629 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
630 filter_dict
["name"] = network
.get_name()
631 filter_dict
["id"] = vdc_network_id
[3]
632 filter_dict
["shared"] = network
.get_IsShared()
633 filter_dict
["tenant_id"] = vdc_id
634 if network
.get_status() == 1:
635 filter_dict
["admin_state_up"] = True
637 filter_dict
["admin_state_up"] = False
638 filter_dict
["status"] = "ACTIVE"
639 filter_dict
["type"] = "bridge"
640 self
.logger
.debug("Returning {}".format(filter_dict
))
643 self
.logger
.debug("Error in get_network")
644 self
.logger
.debug(traceback
.format_exc())
648 def delete_network(self
, net_id
):
650 Method Deletes a tenant network from VIM, provide the network id.
652 Returns the network identifier or raise an exception
657 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
659 # ############# Stub code for SRIOV #################
660 # dvport_group = self.get_dvport_group(net_id)
663 # status = self.destroy_dvport_group(net_id)
665 # # Remove vlanID from persistent info
666 # if net_id in self.persistent_info["used_vlanIDs"]:
667 # del self.persistent_info["used_vlanIDs"][net_id]
671 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
672 if vcd_network
is not None and vcd_network
:
673 if self
.delete_network_action(network_uuid
=net_id
):
676 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
678 def refresh_nets_status(self
, net_list
):
679 """Get the status of the networks
680 Params: the list of network identifiers
681 Returns a dictionary with:
682 net_id: #VIM id of this network
683 status: #Mandatory. Text with one of:
684 # DELETED (not found at vim)
685 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
686 # OTHER (Vim reported other status not understood)
687 # ERROR (VIM indicates an ERROR status)
688 # ACTIVE, INACTIVE, DOWN (admin down),
689 # BUILD (on building process)
691 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
692 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
698 raise vimconn
.vimconnConnectionException("self.connect() is failed")
704 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
705 if vcd_network
is not None and vcd_network
:
706 if vcd_network
['status'] == '1':
712 errormsg
= 'Network not found.'
714 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
715 'vim_info': yaml
.safe_dump(vcd_network
)}
717 self
.logger
.debug("Error in refresh_nets_status")
718 self
.logger
.debug(traceback
.format_exc())
722 def get_flavor(self
, flavor_id
):
723 """Obtain flavor details from the VIM
724 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
726 if flavor_id
not in vimconnector
.flavorlist
:
727 raise vimconn
.vimconnNotFoundException("Flavor not found.")
728 return vimconnector
.flavorlist
[flavor_id
]
730 def new_flavor(self
, flavor_data
):
731 """Adds a tenant flavor to VIM
732 flavor_data contains a dictionary with information, keys:
734 ram: memory (cloud type) in MBytes
735 vpcus: cpus (cloud type)
736 extended: EPA parameters
737 - numas: #items requested in same NUMA
738 memory: number of 1G huge pages memory
739 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
740 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
741 - name: interface name
742 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
743 bandwidth: X Gbps; requested guarantee bandwidth
744 vpci: requested virtual PCI address
748 Returns the flavor identifier"""
750 # generate a new uuid put to internal dict and return it.
751 self
.logger
.debug("Creating new flavor - flavor_data: {}".format(flavor_data
))
752 new_flavor
=flavor_data
753 ram
= flavor_data
.get(FLAVOR_RAM_KEY
, 1024)
754 cpu
= flavor_data
.get(FLAVOR_VCPUS_KEY
, 1)
755 disk
= flavor_data
.get(FLAVOR_DISK_KEY
, 1)
757 if not isinstance(ram
, int):
758 raise vimconn
.vimconnException("Non-integer value for ram")
759 elif not isinstance(cpu
, int):
760 raise vimconn
.vimconnException("Non-integer value for cpu")
761 elif not isinstance(disk
, int):
762 raise vimconn
.vimconnException("Non-integer value for disk")
764 extended_flv
= flavor_data
.get("extended")
766 numas
=extended_flv
.get("numas")
769 #overwrite ram and vcpus
770 ram
= numa
['memory']*1024
771 if 'paired-threads' in numa
:
772 cpu
= numa
['paired-threads']*2
773 elif 'cores' in numa
:
775 elif 'threads' in numa
:
776 cpu
= numa
['threads']
778 new_flavor
[FLAVOR_RAM_KEY
] = ram
779 new_flavor
[FLAVOR_VCPUS_KEY
] = cpu
780 new_flavor
[FLAVOR_DISK_KEY
] = disk
781 # generate a new uuid put to internal dict and return it.
782 flavor_id
= uuid
.uuid4()
783 vimconnector
.flavorlist
[str(flavor_id
)] = new_flavor
784 self
.logger
.debug("Created flavor - {} : {}".format(flavor_id
, new_flavor
))
786 return str(flavor_id
)
788 def delete_flavor(self
, flavor_id
):
789 """Deletes a tenant flavor from VIM identify by its id
791 Returns the used id or raise an exception
793 if flavor_id
not in vimconnector
.flavorlist
:
794 raise vimconn
.vimconnNotFoundException("Flavor not found.")
796 vimconnector
.flavorlist
.pop(flavor_id
, None)
799 def new_image(self
, image_dict
):
801 Adds a tenant image to VIM
803 200, image-id if the image is created
804 <0, message if there is an error
807 return self
.get_image_id_from_path(image_dict
['location'])
809 def delete_image(self
, image_id
):
816 raise vimconn
.vimconnNotImplemented("Should have implemented this")
818 def catalog_exists(self
, catalog_name
, catalogs
):
825 for catalog
in catalogs
:
826 if catalog
.name
== catalog_name
:
830 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
831 """ Create new catalog entry in vCloud director.
834 vca: vCloud director.
835 catalog_name catalog that client wish to create. Note no validation done for a name.
836 Client must make sure that provide valid string representation.
838 Return (bool) True if catalog created.
842 task
= vca
.create_catalog(catalog_name
, catalog_name
)
843 result
= vca
.block_until_completed(task
)
846 catalogs
= vca
.get_catalogs()
849 return self
.catalog_exists(catalog_name
, catalogs
)
851 # noinspection PyIncorrectDocstring
852 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
853 description
='', progress
=False, chunk_bytes
=128 * 1024):
855 Uploads a OVF file to a vCloud catalog
862 :param catalog_name: (str): The name of the catalog to upload the media.
863 :param media_file_name: (str): The name of the local media file to upload.
864 :return: (bool) True if the media file was successfully uploaded, false otherwise.
866 os
.path
.isfile(media_file_name
)
867 statinfo
= os
.stat(media_file_name
)
869 # find a catalog entry where we upload OVF.
870 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
872 # if VCD can parse OVF we upload VMDK file
874 for catalog
in vca
.get_catalogs():
875 if catalog_name
!= catalog
.name
:
877 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
878 link
.get_rel() == 'add', catalog
.get_Link())
879 assert len(link
) == 1
881 <UploadVAppTemplateParams name="%s" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
882 """ % (escape(catalog_name
), escape(description
))
883 headers
= vca
.vcloud_session
.get_vcloud_headers()
884 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
885 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
886 if response
.status_code
== requests
.codes
.created
:
887 catalogItem
= XmlElementTree
.fromstring(response
.content
)
888 entity
= [child
for child
in catalogItem
if
889 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
890 href
= entity
.get('href')
892 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
893 verify
=vca
.verify
, logger
=self
.logger
)
895 if response
.status_code
== requests
.codes
.ok
:
896 media
= mediaType
.parseString(response
.content
, True)
897 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
898 media
.get_Files().get_File()[0].get_Link())[0]
899 headers
= vca
.vcloud_session
.get_vcloud_headers()
900 headers
['Content-Type'] = 'Content-Type text/xml'
901 response
= Http
.put(link
.get_href(),
902 data
=open(media_file_name
, 'rb'),
904 verify
=vca
.verify
, logger
=self
.logger
)
905 if response
.status_code
!= requests
.codes
.ok
:
907 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
911 # TODO fix this with aync block
914 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
916 # uploading VMDK file
917 # check status of OVF upload and upload remaining files.
918 response
= Http
.get(template
,
919 headers
=vca
.vcloud_session
.get_vcloud_headers(),
923 if response
.status_code
== requests
.codes
.ok
:
924 media
= mediaType
.parseString(response
.content
, True)
925 number_of_files
= len(media
.get_Files().get_File())
926 for index
in xrange(0, number_of_files
):
927 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
928 media
.get_Files().get_File()[index
].get_Link())
929 for link
in links_list
:
930 # we skip ovf since it already uploaded.
931 if 'ovf' in link
.get_href():
933 # The OVF file and VMDK must be in a same directory
934 head
, tail
= os
.path
.split(media_file_name
)
935 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
936 if not os
.path
.isfile(file_vmdk
):
938 statinfo
= os
.stat(file_vmdk
)
939 if statinfo
.st_size
== 0:
941 hrefvmdk
= link
.get_href()
944 print("Uploading file: {}".format(file_vmdk
))
946 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
948 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
950 bytes_transferred
= 0
951 f
= open(file_vmdk
, 'rb')
952 while bytes_transferred
< statinfo
.st_size
:
953 my_bytes
= f
.read(chunk_bytes
)
954 if len(my_bytes
) <= chunk_bytes
:
955 headers
= vca
.vcloud_session
.get_vcloud_headers()
956 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
957 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
958 headers
['Content-Length'] = str(len(my_bytes
))
959 response
= Http
.put(hrefvmdk
,
965 if response
.status_code
== requests
.codes
.ok
:
966 bytes_transferred
+= len(my_bytes
)
968 progress_bar
.update(bytes_transferred
)
971 'file upload failed with error: [%s] %s' % (response
.status_code
,
978 progress_bar
.finish()
982 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
983 format(catalog_name
, media_file_name
))
985 except Exception as exp
:
986 self
.logger
.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
987 .format(catalog_name
,media_file_name
, exp
))
988 raise vimconn
.vimconnException(
989 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
990 .format(catalog_name
,media_file_name
, exp
))
992 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
995 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
996 """Upload media file"""
997 # TODO add named parameters for readability
999 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
1000 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
1002 def validate_uuid4(self
, uuid_string
=None):
1003 """ Method validate correct format of UUID.
1005 Return: true if string represent valid uuid
1008 val
= uuid
.UUID(uuid_string
, version
=4)
1013 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
1014 """ Method check catalog and return catalog ID in UUID format.
1017 catalog_name: catalog name as string
1018 catalogs: list of catalogs.
1020 Return: catalogs uuid
1023 for catalog
in catalogs
:
1024 if catalog
.name
== catalog_name
:
1025 catalog_id
= catalog
.get_id().split(":")
1026 return catalog_id
[3]
1029 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
1030 """ Method check catalog and return catalog name lookup done by catalog UUID.
1033 catalog_name: catalog name as string
1034 catalogs: list of catalogs.
1036 Return: catalogs name or None
1039 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1042 for catalog
in catalogs
:
1043 catalog_id
= catalog
.get_id().split(":")[3]
1044 if catalog_id
== catalog_uuid
:
1048 def get_catalog_obj(self
, catalog_uuid
=None, catalogs
=None):
1049 """ Method check catalog and return catalog name lookup done by catalog UUID.
1052 catalog_name: catalog name as string
1053 catalogs: list of catalogs.
1055 Return: catalogs name or None
1058 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1061 for catalog
in catalogs
:
1062 catalog_id
= catalog
.get_id().split(":")[3]
1063 if catalog_id
== catalog_uuid
:
1067 def get_image_id_from_path(self
, path
=None, progress
=False):
1068 """ Method upload OVF image to vCloud director.
1070 Each OVF image represented as single catalog entry in vcloud director.
1071 The method check for existing catalog entry. The check done by file name without file extension.
1073 if given catalog name already present method will respond with existing catalog uuid otherwise
1074 it will create new catalog entry and upload OVF file to newly created catalog.
1076 If method can't create catalog entry or upload a file it will throw exception.
1078 Method accept boolean flag progress that will output progress bar. It useful method
1079 for standalone upload use case. In case to test large file upload.
1082 path: - valid path to OVF file.
1083 progress - boolean progress bar show progress bar.
1085 Return: if image uploaded correct method will provide image catalog UUID.
1087 vca
= self
.connect()
1089 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1092 raise vimconn
.vimconnException("Image path can't be None.")
1094 if not os
.path
.isfile(path
):
1095 raise vimconn
.vimconnException("Can't read file. File not found.")
1097 if not os
.access(path
, os
.R_OK
):
1098 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
1100 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1102 dirpath
, filename
= os
.path
.split(path
)
1103 flname
, file_extension
= os
.path
.splitext(path
)
1104 if file_extension
!= '.ovf':
1105 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1106 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1108 catalog_name
= os
.path
.splitext(filename
)[0]
1109 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1110 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1111 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1114 catalogs
= vca
.get_catalogs()
1115 except Exception as exp
:
1116 self
.logger
.debug("Failed get catalogs() with Exception {} ".format(exp
))
1117 raise vimconn
.vimconnException("Failed get catalogs() with Exception {} ".format(exp
))
1119 if len(catalogs
) == 0:
1120 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1121 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1123 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1124 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1125 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1127 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1128 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1130 for catalog
in catalogs
:
1131 # search for existing catalog if we find same name we return ID
1132 # TODO optimize this
1133 if catalog
.name
== catalog_md5_name
:
1134 self
.logger
.debug("Found existing catalog entry for {} "
1135 "catalog id {}".format(catalog_name
,
1136 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1137 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1139 # if we didn't find existing catalog we create a new one and upload image.
1140 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1141 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1143 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1145 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1146 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1148 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1150 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1152 def get_image_list(self
, filter_dict
={}):
1153 '''Obtain tenant images from VIM
1157 checksum: image checksum
1158 location: image path
1159 Returns the image list of dictionaries:
1160 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1163 vca
= self
.connect()
1165 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1168 catalogs
= vca
.get_catalogs()
1169 if len(catalogs
) == 0:
1172 for catalog
in catalogs
:
1173 catalog_uuid
= catalog
.get_id().split(":")[3]
1176 if filter_dict
.get("name") and filter_dict
["name"] != name
:
1178 if filter_dict
.get("id") and filter_dict
["id"] != catalog_uuid
:
1180 filtered_dict
["name"] = name
1181 filtered_dict
["id"] = catalog_uuid
1182 image_list
.append(filtered_dict
)
1184 self
.logger
.debug("List of already created catalog items: {}".format(image_list
))
1186 except Exception as exp
:
1187 raise vimconn
.vimconnException("Exception occured while retriving catalog items {}".format(exp
))
1189 def get_vappid(self
, vdc
=None, vapp_name
=None):
1190 """ Method takes vdc object and vApp name and returns vapp uuid or None
1193 vdc: The VDC object.
1194 vapp_name: is application vappp name identifier
1197 The return vApp name otherwise None
1199 if vdc
is None or vapp_name
is None:
1201 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1203 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1204 vdc
.ResourceEntities
.ResourceEntity
)
1206 return refs
[0].href
.split("vapp")[1][1:]
1207 except Exception as e
:
1208 self
.logger
.exception(e
)
1212 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1213 """ Method Method returns True or False if vapp deployed in vCloud director
1216 vca: Connector to VCA
1217 vdc: The VDC object.
1218 vappid: vappid is application identifier
1221 The return True if vApp deployed
1226 refs
= filter(lambda ref
:
1227 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1228 vdc
.ResourceEntities
.ResourceEntity
)
1230 vappid
= ref
.href
.split("vapp")[1][1:]
1231 # find vapp with respected vapp uuid
1232 if vappid
== vapp_uuid
:
1234 except Exception as e
:
1235 self
.logger
.exception(e
)
1239 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1240 """Method returns vApp name from vCD and lookup done by vapp_id.
1243 vca: Connector to VCA
1244 vdc: The VDC object.
1245 vapp_uuid: vappid is application identifier
1248 The return vApp name otherwise None
1252 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1253 vdc
.ResourceEntities
.ResourceEntity
)
1255 # we care only about UUID the rest doesn't matter
1256 vappid
= ref
.href
.split("vapp")[1][1:]
1257 if vappid
== vapp_uuid
:
1258 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1260 tree
= XmlElementTree
.fromstring(response
.content
)
1261 return tree
.attrib
['name']
1262 except Exception as e
:
1263 self
.logger
.exception(e
)
1267 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1268 cloud_config
=None, disk_list
=None):
1269 """Adds a VM instance to VIM
1271 start: indicates if VM must start or boot in pause mode. Ignored
1272 image_id,flavor_id: image and flavor uuid
1273 net_list: list of interfaces, each one is a dictionary with:
1275 net_id: network uuid to connect
1276 vpci: virtual vcpi to assign
1277 model: interface model, virtio, e2000, ...
1279 use: 'data', 'bridge', 'mgmt'
1280 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1281 vim_id: filled/added by this function
1282 cloud_config: can be a text script to be passed directly to cloud-init,
1283 or an object to inject users and ssh keys with format:
1284 key-pairs: [] list of keys to install to the default user
1285 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1286 #TODO ip, security groups
1287 Returns >=0, the instance identifier
1291 self
.logger
.info("Creating new instance for entry {}".format(name
))
1292 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {} disk_list {}".format(
1293 description
, start
, image_id
, flavor_id
, net_list
, cloud_config
, disk_list
))
1294 vca
= self
.connect()
1296 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1298 #new vm name = vmname + tenant_id + uuid
1299 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1300 vmname_andid
= ''.join(new_vm_name
)
1302 # if vm already deployed we return existing uuid
1303 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1304 # if vapp_uuid is not None:
1307 # we check for presence of VDC, Catalog entry and Flavor.
1308 vdc
= vca
.get_vdc(self
.tenant_name
)
1310 raise vimconn
.vimconnNotFoundException(
1311 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1312 catalogs
= vca
.get_catalogs()
1313 if catalogs
is None:
1314 raise vimconn
.vimconnNotFoundException(
1315 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1317 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1318 if catalog_hash_name
:
1319 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1321 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1322 "(Failed retrieve catalog information {})".format(name
, image_id
))
1325 # Set vCPU and Memory based on flavor.
1330 if flavor_id
is not None:
1331 if flavor_id
not in vimconnector
.flavorlist
:
1332 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1333 "Failed retrieve flavor information "
1334 "flavor id {}".format(name
, flavor_id
))
1337 flavor
= vimconnector
.flavorlist
[flavor_id
]
1338 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1339 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1340 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1341 extended
= flavor
.get("extended", None)
1343 numas
=extended
.get("numas", None)
1345 except Exception as exp
:
1346 raise vimconn
.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id
, exp
))
1348 # image upload creates template name as catalog name space Template.
1349 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1354 # client must provide at least one entry in net_list if not we report error
1355 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1356 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1358 primary_netname
= None
1359 network_mode
= 'bridged'
1360 if net_list
is not None and len(net_list
) > 0:
1361 for net
in net_list
:
1362 if 'use' in net
and net
['use'] == 'mgmt':
1364 if primary_net
is None:
1365 primary_net
= net_list
[0]
1368 primary_net_id
= primary_net
['net_id']
1369 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1370 if 'name' in network_dict
:
1371 primary_netname
= network_dict
['name']
1374 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1376 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1378 # use: 'data', 'bridge', 'mgmt'
1379 # create vApp. Set vcpu and ram based on flavor id.
1381 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1382 self
.get_catalogbyid(image_id
, catalogs
),
1383 network_name
=None, # None while creating vapp
1384 network_mode
=network_mode
,
1385 vm_name
=vmname_andid
,
1386 vm_cpus
=vm_cpus
, # can be None if flavor is None
1387 vm_memory
=vm_memory
) # can be None if flavor is None
1389 if vapptask
is None or vapptask
is False:
1390 raise vimconn
.vimconnUnexpectedResponse(
1391 "new_vminstance(): failed to create vApp {}".format(vmname_andid
))
1392 if type(vapptask
) is VappTask
:
1393 vca
.block_until_completed(vapptask
)
1395 except Exception as exp
:
1396 raise vimconn
.vimconnUnexpectedResponse(
1397 "new_vminstance(): failed to create vApp {} with Exception:{}".format(vmname_andid
, exp
))
1399 # we should have now vapp in undeployed state.
1401 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1402 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1403 except Exception as exp
:
1404 raise vimconn
.vimconnUnexpectedResponse(
1405 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1406 .format(vmname_andid
, exp
))
1409 raise vimconn
.vimconnUnexpectedResponse(
1410 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
1413 #Add PCI passthrough/SRIOV configrations
1415 pci_devices_info
= []
1417 reserve_memory
= False
1419 for net
in net_list
:
1420 if net
["type"]=="PF":
1421 pci_devices_info
.append(net
)
1422 elif (net
["type"]=="VF" or net
["type"]=="VFnotShared") and 'net_id'in net
:
1423 sriov_net_info
.append(net
)
1426 if len(pci_devices_info
) > 0:
1427 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1429 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1432 if PCI_devices_status
:
1433 self
.logger
.info("Added PCI devives {} to VM {}".format(
1437 reserve_memory
= True
1439 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1445 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1446 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1448 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1450 #Add new or existing disks to vApp
1452 added_existing_disk
= False
1453 for disk
in disk_list
:
1454 if 'device_type' in disk
and disk
['device_type'] == 'cdrom':
1455 image_id
= disk
['image_id']
1456 # Adding CD-ROM to VM
1457 # will revisit code once specification ready to support this feature
1458 self
.insert_media_to_vm(vapp
, image_id
)
1459 elif "image_id" in disk
and disk
["image_id"] is not None:
1460 self
.logger
.debug("Adding existing disk from image {} to vm {} ".format(
1461 disk
["image_id"] , vapp_uuid
))
1462 self
.add_existing_disk(catalogs
=catalogs
,
1463 image_id
=disk
["image_id"],
1464 size
= disk
["size"],
1465 template_name
=templateName
,
1468 added_existing_disk
= True
1470 #Wait till added existing disk gets reflected into vCD database/API
1471 if added_existing_disk
:
1473 added_existing_disk
= False
1474 self
.add_new_disk(vca
, vapp_uuid
, disk
['size'])
1477 # Assigning numa affinity setting
1479 if 'paired-threads-id' in numa
:
1480 paired_threads_id
= numa
['paired-threads-id']
1481 self
.set_numa_affinity(vapp_uuid
, paired_threads_id
)
1483 # add NICs & connect to networks in netlist
1485 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1487 primary_nic_index
= 0
1488 for net
in net_list
:
1489 # openmano uses network id in UUID format.
1490 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1491 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1492 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1494 if 'net_id' not in net
:
1497 interface_net_id
= net
['net_id']
1498 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1499 interface_network_mode
= net
['use']
1501 if interface_network_mode
== 'mgmt':
1502 primary_nic_index
= nicIndex
1504 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1505 - DHCP (The IP address is obtained from a DHCP service.)
1506 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1507 - NONE (No IP addressing mode specified.)"""
1509 if primary_netname
is not None:
1510 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1512 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1513 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1514 if type(task
) is GenericTask
:
1515 vca
.block_until_completed(task
)
1516 # connect network to VM - with all DHCP by default
1518 type_list
= ['PF','VF','VFnotShared']
1519 if 'type' in net
and net
['type'] not in type_list
:
1520 # fetching nic type from vnf
1522 nic_type
= net
['model']
1523 self
.logger
.info("new_vminstance(): adding network adapter "\
1524 "to a network {}".format(nets
[0].name
))
1525 self
.add_network_adapter_to_vms(vapp
, nets
[0].name
,
1531 self
.logger
.info("new_vminstance(): adding network adapter "\
1532 "to a network {}".format(nets
[0].name
))
1533 self
.add_network_adapter_to_vms(vapp
, nets
[0].name
,
1539 # cloud-init for ssh-key injection
1541 self
.cloud_init(vapp
,cloud_config
)
1543 # deploy and power on vm
1544 self
.logger
.debug("new_vminstance(): Deploying vApp {} ".format(name
))
1545 deploytask
= vapp
.deploy(powerOn
=False)
1546 if type(deploytask
) is GenericTask
:
1547 vca
.block_until_completed(deploytask
)
1549 # ############# Stub code for SRIOV #################
1551 # if len(sriov_net_info) > 0:
1552 # self.logger.info("Need to add SRIOV adapters {} into VM {}".format(sriov_net_info,
1554 # sriov_status, vm_obj, vcenter_conect = self.add_sriov(vapp_uuid,
1558 # self.logger.info("Added SRIOV {} to VM {}".format(
1562 # reserve_memory = True
1564 # self.logger.info("Fail to add SRIOV {} to VM {}".format(
1569 # If VM has PCI devices or SRIOV reserve memory for VM
1571 memReserve
= vm_obj
.config
.hardware
.memoryMB
1572 spec
= vim
.vm
.ConfigSpec()
1573 spec
.memoryAllocation
= vim
.ResourceAllocationInfo(reservation
=memReserve
)
1574 task
= vm_obj
.ReconfigVM_Task(spec
=spec
)
1576 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
1577 self
.logger
.info("Reserved memmoery {} MB for "\
1578 "VM VM status: {}".format(str(memReserve
),result
))
1580 self
.logger
.info("Fail to reserved memmoery {} to VM {}".format(
1581 str(memReserve
),str(vm_obj
)))
1583 self
.logger
.debug("new_vminstance(): power on vApp {} ".format(name
))
1584 poweron_task
= vapp
.poweron()
1585 if type(poweron_task
) is GenericTask
:
1586 vca
.block_until_completed(poweron_task
)
1588 except Exception as exp
:
1589 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1590 self
.logger
.debug("new_vminstance(): Failed create new vm instance {}".format(name
, exp
))
1591 raise vimconn
.vimconnException("new_vminstance(): Failed create new vm instance {}".format(name
, exp
))
1593 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1596 while wait_time
<= MAX_WAIT_TIME
:
1598 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1599 except Exception as exp
:
1600 raise vimconn
.vimconnUnexpectedResponse(
1601 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1602 .format(vmname_andid
, exp
))
1604 if vapp
and vapp
.me
.deployed
:
1605 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1608 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1609 time
.sleep(INTERVAL_TIME
)
1611 wait_time
+=INTERVAL_TIME
1613 if vapp_uuid
is not None:
1616 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1620 ## based on current discussion
1624 # created: '2016-09-08T11:51:58'
1625 # description: simple-instance.linux1.1
1626 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1627 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1628 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1633 def get_vminstance(self
, vim_vm_uuid
=None):
1634 """Returns the VM instance information from VIM"""
1636 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1637 vca
= self
.connect()
1639 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1641 vdc
= vca
.get_vdc(self
.tenant_name
)
1643 raise vimconn
.vimconnConnectionException(
1644 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1646 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1647 if not vm_info_dict
:
1648 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1649 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1651 status_key
= vm_info_dict
['status']
1654 vm_dict
= {'created': vm_info_dict
['created'],
1655 'description': vm_info_dict
['name'],
1656 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1657 'hostId': vm_info_dict
['vmuuid'],
1659 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1661 if 'interfaces' in vm_info_dict
:
1662 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1664 vm_dict
['interfaces'] = []
1666 vm_dict
= {'created': '',
1668 'status': vcdStatusCode2manoFormat
[int(-1)],
1669 'hostId': vm_info_dict
['vmuuid'],
1670 'error_msg': "Inconsistency state",
1671 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1675 def delete_vminstance(self
, vm__vim_uuid
):
1676 """Method poweroff and remove VM instance from vcloud director network.
1679 vm__vim_uuid: VM UUID
1682 Returns the instance identifier
1685 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1686 vca
= self
.connect()
1688 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1690 vdc
= vca
.get_vdc(self
.tenant_name
)
1692 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1694 raise vimconn
.vimconnException(
1695 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1698 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1699 if vapp_name
is None:
1700 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1701 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1703 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1705 # Delete vApp and wait for status change if task executed and vApp is None.
1706 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1709 if vapp
.me
.deployed
:
1710 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1714 while wait_time
<= MAX_WAIT_TIME
:
1715 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1717 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1718 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1720 power_off_task
= vapp
.poweroff()
1721 if type(power_off_task
) is GenericTask
:
1722 result
= vca
.block_until_completed(power_off_task
)
1727 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1728 time
.sleep(INTERVAL_TIME
)
1730 wait_time
+=INTERVAL_TIME
1732 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1734 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1737 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1740 while wait_time
<= MAX_WAIT_TIME
:
1741 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1743 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1744 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1745 undeploy_task
= vapp
.undeploy(action
='powerOff')
1747 if type(undeploy_task
) is GenericTask
:
1748 result
= vca
.block_until_completed(undeploy_task
)
1753 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1754 time
.sleep(INTERVAL_TIME
)
1756 wait_time
+=INTERVAL_TIME
1759 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1762 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1763 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1765 if vapp
is not None:
1769 while wait_time
<= MAX_WAIT_TIME
:
1770 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1772 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1773 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1775 delete_task
= vapp
.delete()
1777 if type(delete_task
) is GenericTask
:
1778 vca
.block_until_completed(delete_task
)
1779 result
= vca
.block_until_completed(delete_task
)
1783 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1784 time
.sleep(INTERVAL_TIME
)
1786 wait_time
+=INTERVAL_TIME
1789 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1792 self
.logger
.debug(traceback
.format_exc())
1793 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1795 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1796 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1799 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1801 def refresh_vms_status(self
, vm_list
):
1802 """Get the status of the virtual machines and their interfaces/ports
1803 Params: the list of VM identifiers
1804 Returns a dictionary with:
1805 vm_id: #VIM id of this Virtual Machine
1806 status: #Mandatory. Text with one of:
1807 # DELETED (not found at vim)
1808 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1809 # OTHER (Vim reported other status not understood)
1810 # ERROR (VIM indicates an ERROR status)
1811 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1812 # CREATING (on building process), ERROR
1813 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1815 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1816 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1818 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1819 mac_address: #Text format XX:XX:XX:XX:XX:XX
1820 vim_net_id: #network id where this interface is connected
1821 vim_interface_id: #interface/port VIM id
1822 ip_address: #null, or text with IPv4, IPv6 address
1825 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1827 vca
= self
.connect()
1829 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1831 vdc
= vca
.get_vdc(self
.tenant_name
)
1833 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1837 for vmuuid
in vm_list
:
1838 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1839 if vmname
is not None:
1842 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1843 vm_info
= the_vapp
.get_vms_details()
1844 vm_status
= vm_info
[0]['status']
1845 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
1846 vm_info
[0].update(vm_pci_details
)
1848 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1849 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1850 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
1853 vm_app_networks
= the_vapp
.get_vms_network_info()
1854 for vapp_network
in vm_app_networks
:
1855 for vm_network
in vapp_network
:
1856 if vm_network
['name'] == vmname
:
1857 #Assign IP Address based on MAC Address in NSX DHCP lease info
1858 if vm_network
['ip'] is None:
1859 if not nsx_edge_list
:
1860 nsx_edge_list
= self
.get_edge_details()
1861 if nsx_edge_list
is None:
1862 raise vimconn
.vimconnException("refresh_vms_status:"\
1863 "Failed to get edge details from NSX Manager")
1864 if vm_network
['mac'] is not None:
1865 vm_network
['ip'] = self
.get_ipaddr_from_NSXedge(nsx_edge_list
, vm_network
['mac'])
1867 vm_net_id
= self
.get_network_id_by_name(vm_network
['network_name'])
1868 interface
= {"mac_address": vm_network
['mac'],
1869 "vim_net_id": vm_net_id
,
1870 "vim_interface_id": vm_net_id
,
1871 'ip_address': vm_network
['ip']}
1872 # interface['vim_info'] = yaml.safe_dump(vm_network)
1873 vm_dict
["interfaces"].append(interface
)
1874 # add a vm to vm dict
1875 vms_dict
.setdefault(vmuuid
, vm_dict
)
1876 except Exception as exp
:
1877 self
.logger
.debug("Error in response {}".format(exp
))
1878 self
.logger
.debug(traceback
.format_exc())
1883 def get_edge_details(self
):
1884 """Get the NSX edge list from NSX Manager
1885 Returns list of NSX edges
1888 rheaders
= {'Content-Type': 'application/xml'}
1889 nsx_api_url
= '/api/4.0/edges'
1891 self
.logger
.debug("Get edge details from NSX Manager {} {}".format(self
.nsx_manager
, nsx_api_url
))
1894 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
1895 auth
= (self
.nsx_user
, self
.nsx_password
),
1896 verify
= False, headers
= rheaders
)
1897 if resp
.status_code
== requests
.codes
.ok
:
1898 paged_Edge_List
= XmlElementTree
.fromstring(resp
.text
)
1899 for edge_pages
in paged_Edge_List
:
1900 if edge_pages
.tag
== 'edgePage':
1901 for edge_summary
in edge_pages
:
1902 if edge_summary
.tag
== 'pagingInfo':
1903 for element
in edge_summary
:
1904 if element
.tag
== 'totalCount' and element
.text
== '0':
1905 raise vimconn
.vimconnException("get_edge_details: No NSX edges details found: {}"
1906 .format(self
.nsx_manager
))
1908 if edge_summary
.tag
== 'edgeSummary':
1909 for element
in edge_summary
:
1910 if element
.tag
== 'id':
1911 edge_list
.append(element
.text
)
1913 raise vimconn
.vimconnException("get_edge_details: No NSX edge details found: {}"
1914 .format(self
.nsx_manager
))
1917 raise vimconn
.vimconnException("get_edge_details: "\
1918 "No NSX edge details found: {}"
1919 .format(self
.nsx_manager
))
1921 self
.logger
.debug("get_edge_details: Found NSX edges {}".format(edge_list
))
1924 self
.logger
.debug("get_edge_details: "
1925 "Failed to get NSX edge details from NSX Manager: {}"
1926 .format(resp
.content
))
1929 except Exception as exp
:
1930 self
.logger
.debug("get_edge_details: "\
1931 "Failed to get NSX edge details from NSX Manager: {}"
1933 raise vimconn
.vimconnException("get_edge_details: "\
1934 "Failed to get NSX edge details from NSX Manager: {}"
1938 def get_ipaddr_from_NSXedge(self
, nsx_edges
, mac_address
):
1939 """Get IP address details from NSX edges, using the MAC address
1940 PARAMS: nsx_edges : List of NSX edges
1941 mac_address : Find IP address corresponding to this MAC address
1942 Returns: IP address corrresponding to the provided MAC address
1946 rheaders
= {'Content-Type': 'application/xml'}
1948 self
.logger
.debug("get_ipaddr_from_NSXedge: Finding IP addr from NSX edge")
1951 for edge
in nsx_edges
:
1952 nsx_api_url
= '/api/4.0/edges/'+ edge
+'/dhcp/leaseInfo'
1954 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
1955 auth
= (self
.nsx_user
, self
.nsx_password
),
1956 verify
= False, headers
= rheaders
)
1958 if resp
.status_code
== requests
.codes
.ok
:
1959 dhcp_leases
= XmlElementTree
.fromstring(resp
.text
)
1960 for child
in dhcp_leases
:
1961 if child
.tag
== 'dhcpLeaseInfo':
1962 dhcpLeaseInfo
= child
1963 for leaseInfo
in dhcpLeaseInfo
:
1964 for elem
in leaseInfo
:
1965 if (elem
.tag
)=='macAddress':
1966 edge_mac_addr
= elem
.text
1967 if (elem
.tag
)=='ipAddress':
1969 if edge_mac_addr
is not None:
1970 if edge_mac_addr
== mac_address
:
1971 self
.logger
.debug("Found ip addr {} for mac {} at NSX edge {}"
1972 .format(ip_addr
, mac_address
,edge
))
1975 self
.logger
.debug("get_ipaddr_from_NSXedge: "\
1976 "Error occurred while getting DHCP lease info from NSX Manager: {}"
1977 .format(resp
.content
))
1979 self
.logger
.debug("get_ipaddr_from_NSXedge: No IP addr found in any NSX edge")
1982 except XmlElementTree
.ParseError
as Err
:
1983 self
.logger
.debug("ParseError in response from NSX Manager {}".format(Err
.message
), exc_info
=True)
1986 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1987 """Send and action over a VM instance from VIM
1988 Returns the vm_id if the action was successfully sent to the VIM"""
1990 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1991 if vm__vim_uuid
is None or action_dict
is None:
1992 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1994 vca
= self
.connect()
1996 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1998 vdc
= vca
.get_vdc(self
.tenant_name
)
2000 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
2002 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
2003 if vapp_name
is None:
2004 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
2005 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
2007 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
2010 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
2011 # TODO fix all status
2012 if "start" in action_dict
:
2013 vm_info
= the_vapp
.get_vms_details()
2014 vm_status
= vm_info
[0]['status']
2015 self
.logger
.info("action_vminstance: Power on vApp: {}".format(vapp_name
))
2016 if vm_status
== "Suspended" or vm_status
== "Powered off":
2017 power_on_task
= the_vapp
.poweron()
2018 result
= vca
.block_until_completed(power_on_task
)
2019 self
.instance_actions_result("start", result
, vapp_name
)
2020 elif "rebuild" in action_dict
:
2021 self
.logger
.info("action_vminstance: Rebuild vApp: {}".format(vapp_name
))
2022 rebuild_task
= the_vapp
.deploy(powerOn
=True)
2023 result
= vca
.block_until_completed(rebuild_task
)
2024 self
.instance_actions_result("rebuild", result
, vapp_name
)
2025 elif "pause" in action_dict
:
2026 self
.logger
.info("action_vminstance: pause vApp: {}".format(vapp_name
))
2027 pause_task
= the_vapp
.undeploy(action
='suspend')
2028 result
= vca
.block_until_completed(pause_task
)
2029 self
.instance_actions_result("pause", result
, vapp_name
)
2030 elif "resume" in action_dict
:
2031 self
.logger
.info("action_vminstance: resume vApp: {}".format(vapp_name
))
2032 power_task
= the_vapp
.poweron()
2033 result
= vca
.block_until_completed(power_task
)
2034 self
.instance_actions_result("resume", result
, vapp_name
)
2035 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
2036 action_name
, value
= action_dict
.items()[0]
2037 self
.logger
.info("action_vminstance: {} vApp: {}".format(action_name
, vapp_name
))
2038 power_off_task
= the_vapp
.undeploy(action
='powerOff')
2039 result
= vca
.block_until_completed(power_off_task
)
2040 if action_name
== "shutdown":
2041 self
.instance_actions_result("shutdown", result
, vapp_name
)
2043 self
.instance_actions_result("shutoff", result
, vapp_name
)
2044 elif "forceOff" in action_dict
:
2045 result
= the_vapp
.undeploy(action
='force')
2046 self
.instance_actions_result("forceOff", result
, vapp_name
)
2047 elif "reboot" in action_dict
:
2048 self
.logger
.info("action_vminstance: reboot vApp: {}".format(vapp_name
))
2049 reboot_task
= the_vapp
.reboot()
2051 raise vimconn
.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict
))
2053 except Exception as exp
:
2054 self
.logger
.debug("action_vminstance: Failed with Exception {}".format(exp
))
2055 raise vimconn
.vimconnException("action_vminstance: Failed with Exception {}".format(exp
))
2057 def instance_actions_result(self
, action
, result
, vapp_name
):
2059 self
.logger
.info("action_vminstance: Sucessfully {} the vApp: {}".format(action
, vapp_name
))
2061 self
.logger
.error("action_vminstance: Failed to {} vApp: {}".format(action
, vapp_name
))
2063 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
2065 Get a console for the virtual machine
2067 vm_id: uuid of the VM
2068 console_type, can be:
2069 "novnc" (by default), "xvpvnc" for VNC types,
2070 "rdp-html5" for RDP types, "spice-html5" for SPICE types
2071 Returns dict with the console parameters:
2072 protocol: ssh, ftp, http, https, ...
2073 server: usually ip address
2074 port: the http, ssh, ... port
2075 suffix: extra text, e.g. the http path and query string
2077 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2079 # NOT USED METHODS in current version
2081 def host_vim2gui(self
, host
, server_dict
):
2082 """Transform host dictionary from VIM format to GUI format,
2083 and append to the server_dict
2085 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2087 def get_hosts_info(self
):
2088 """Get the information of deployed hosts
2089 Returns the hosts content"""
2090 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2092 def get_hosts(self
, vim_tenant
):
2093 """Get the hosts and deployed instances
2094 Returns the hosts content"""
2095 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2097 def get_processor_rankings(self
):
2098 """Get the processor rankings in the VIM database"""
2099 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2101 def new_host(self
, host_data
):
2102 """Adds a new host to VIM"""
2103 '''Returns status code of the VIM response'''
2104 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2106 def new_external_port(self
, port_data
):
2107 """Adds a external port to VIM"""
2108 '''Returns the port identifier'''
2109 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2111 def new_external_network(self
, net_name
, net_type
):
2112 """Adds a external network to VIM (shared)"""
2113 '''Returns the network identifier'''
2114 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2116 def connect_port_network(self
, port_id
, network_id
, admin
=False):
2117 """Connects a external port to a network"""
2118 '''Returns status code of the VIM response'''
2119 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2121 def new_vminstancefromJSON(self
, vm_data
):
2122 """Adds a VM instance to VIM"""
2123 '''Returns the instance identifier'''
2124 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2126 def get_network_name_by_id(self
, network_uuid
=None):
2127 """Method gets vcloud director network named based on supplied uuid.
2130 network_uuid: network_id
2133 The return network name.
2136 vca
= self
.connect()
2138 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2140 if not network_uuid
:
2144 org_dict
= self
.get_org(self
.org_uuid
)
2145 if 'networks' in org_dict
:
2146 org_network_dict
= org_dict
['networks']
2147 for net_uuid
in org_network_dict
:
2148 if net_uuid
== network_uuid
:
2149 return org_network_dict
[net_uuid
]
2151 self
.logger
.debug("Exception in get_network_name_by_id")
2152 self
.logger
.debug(traceback
.format_exc())
2156 def get_network_id_by_name(self
, network_name
=None):
2157 """Method gets vcloud director network uuid based on supplied name.
2160 network_name: network_name
2162 The return network uuid.
2163 network_uuid: network_id
2166 vca
= self
.connect()
2168 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2170 if not network_name
:
2171 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
2175 org_dict
= self
.get_org(self
.org_uuid
)
2176 if org_dict
and 'networks' in org_dict
:
2177 org_network_dict
= org_dict
['networks']
2178 for net_uuid
,net_name
in org_network_dict
.iteritems():
2179 if net_name
== network_name
:
2182 except KeyError as exp
:
2183 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
2187 def list_org_action(self
):
2189 Method leverages vCloud director and query for available organization for particular user
2192 vca - is active VCA connection.
2193 vdc_name - is a vdc name that will be used to query vms action
2196 The return XML respond
2199 vca
= self
.connect()
2201 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2203 url_list
= [vca
.host
, '/api/org']
2204 vm_list_rest_call
= ''.join(url_list
)
2206 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2207 response
= Http
.get(url
=vm_list_rest_call
,
2208 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2211 if response
.status_code
== requests
.codes
.ok
:
2212 return response
.content
2216 def get_org_action(self
, org_uuid
=None):
2218 Method leverages vCloud director and retrieve available object fdr organization.
2221 vca - is active VCA connection.
2222 vdc_name - is a vdc name that will be used to query vms action
2225 The return XML respond
2228 vca
= self
.connect()
2230 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2232 if org_uuid
is None:
2235 url_list
= [vca
.host
, '/api/org/', org_uuid
]
2236 vm_list_rest_call
= ''.join(url_list
)
2238 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2239 response
= Http
.get(url
=vm_list_rest_call
,
2240 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2243 if response
.status_code
== requests
.codes
.ok
:
2244 return response
.content
2248 def get_org(self
, org_uuid
=None):
2250 Method retrieves available organization in vCloud Director
2253 org_uuid - is a organization uuid.
2256 The return dictionary with following key
2257 "network" - for network list under the org
2258 "catalogs" - for network list under the org
2259 "vdcs" - for vdc list under org
2263 vca
= self
.connect()
2265 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2267 if org_uuid
is None:
2270 content
= self
.get_org_action(org_uuid
=org_uuid
)
2275 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2276 for child
in vm_list_xmlroot
:
2277 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2278 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2279 org_dict
['vdcs'] = vdc_list
2280 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2281 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2282 org_dict
['networks'] = network_list
2283 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2284 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2285 org_dict
['catalogs'] = catalog_list
2291 def get_org_list(self
):
2293 Method retrieves available organization in vCloud Director
2296 vca - is active VCA connection.
2299 The return dictionary and key for each entry VDC UUID
2303 vca
= self
.connect()
2305 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2307 content
= self
.list_org_action()
2309 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2310 for vm_xml
in vm_list_xmlroot
:
2311 if vm_xml
.tag
.split("}")[1] == 'Org':
2312 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2313 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
2319 def vms_view_action(self
, vdc_name
=None):
2320 """ Method leverages vCloud director vms query call
2323 vca - is active VCA connection.
2324 vdc_name - is a vdc name that will be used to query vms action
2327 The return XML respond
2329 vca
= self
.connect()
2330 if vdc_name
is None:
2333 url_list
= [vca
.host
, '/api/vms/query']
2334 vm_list_rest_call
= ''.join(url_list
)
2336 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2337 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
2338 vca
.vcloud_session
.organization
.Link
)
2340 response
= Http
.get(url
=vm_list_rest_call
,
2341 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2344 if response
.status_code
== requests
.codes
.ok
:
2345 return response
.content
2349 def get_vapp_list(self
, vdc_name
=None):
2351 Method retrieves vApp list deployed vCloud director and returns a dictionary
2352 contains a list of all vapp deployed for queried VDC.
2353 The key for a dictionary is vApp UUID
2357 vca - is active VCA connection.
2358 vdc_name - is a vdc name that will be used to query vms action
2361 The return dictionary and key for each entry vapp UUID
2365 if vdc_name
is None:
2368 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2370 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2371 for vm_xml
in vm_list_xmlroot
:
2372 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2373 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
2374 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
2375 if 'vappTemplate-' in rawuuid
[0]:
2376 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2377 # vm and use raw UUID as key
2378 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
2384 def get_vm_list(self
, vdc_name
=None):
2386 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2387 contains a list of all VM's deployed for queried VDC.
2388 The key for a dictionary is VM UUID
2392 vca - is active VCA connection.
2393 vdc_name - is a vdc name that will be used to query vms action
2396 The return dictionary and key for each entry vapp UUID
2400 if vdc_name
is None:
2403 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2405 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2406 for vm_xml
in vm_list_xmlroot
:
2407 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2408 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2409 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2410 if 'vm-' in rawuuid
[0]:
2411 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2412 # vm and use raw UUID as key
2413 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2419 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2421 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2422 contains a list of all VM's deployed for queried VDC.
2423 The key for a dictionary is VM UUID
2427 vca - is active VCA connection.
2428 vdc_name - is a vdc name that will be used to query vms action
2431 The return dictionary and key for each entry vapp UUID
2434 vca
= self
.connect()
2436 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2438 if vdc_name
is None:
2441 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2443 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2444 for vm_xml
in vm_list_xmlroot
:
2445 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2446 # lookup done by UUID
2448 if vapp_name
in vm_xml
.attrib
['container']:
2449 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2450 if 'vm-' in rawuuid
[0]:
2451 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2453 # lookup done by Name
2455 if vapp_name
in vm_xml
.attrib
['name']:
2456 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2457 if 'vm-' in rawuuid
[0]:
2458 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2465 def get_network_action(self
, network_uuid
=None):
2467 Method leverages vCloud director and query network based on network uuid
2470 vca - is active VCA connection.
2471 network_uuid - is a network uuid
2474 The return XML respond
2477 vca
= self
.connect()
2479 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2481 if network_uuid
is None:
2484 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2485 vm_list_rest_call
= ''.join(url_list
)
2487 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2488 response
= Http
.get(url
=vm_list_rest_call
,
2489 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2492 if response
.status_code
== requests
.codes
.ok
:
2493 return response
.content
2497 def get_vcd_network(self
, network_uuid
=None):
2499 Method retrieves available network from vCloud Director
2502 network_uuid - is VCD network UUID
2504 Each element serialized as key : value pair
2506 Following keys available for access. network_configuration['Gateway'}
2510 <IsInherited>true</IsInherited>
2511 <Gateway>172.16.252.100</Gateway>
2512 <Netmask>255.255.255.0</Netmask>
2513 <Dns1>172.16.254.201</Dns1>
2514 <Dns2>172.16.254.202</Dns2>
2515 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2516 <IsEnabled>true</IsEnabled>
2519 <StartAddress>172.16.252.1</StartAddress>
2520 <EndAddress>172.16.252.99</EndAddress>
2525 <FenceMode>bridged</FenceMode>
2528 The return dictionary and key for each entry vapp UUID
2531 network_configuration
= {}
2532 if network_uuid
is None:
2536 content
= self
.get_network_action(network_uuid
=network_uuid
)
2537 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2539 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2540 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2541 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2543 for child
in vm_list_xmlroot
:
2544 if child
.tag
.split("}")[1] == 'IsShared':
2545 network_configuration
['isShared'] = child
.text
.strip()
2546 if child
.tag
.split("}")[1] == 'Configuration':
2547 for configuration
in child
.iter():
2548 tagKey
= configuration
.tag
.split("}")[1].strip()
2550 network_configuration
[tagKey
] = configuration
.text
.strip()
2551 return network_configuration
2552 except Exception as exp
:
2553 self
.logger
.debug("get_vcd_network: Failed with Exception {}".format(exp
))
2554 raise vimconn
.vimconnException("get_vcd_network: Failed with Exception {}".format(exp
))
2556 return network_configuration
2558 def delete_network_action(self
, network_uuid
=None):
2560 Method delete given network from vCloud director
2563 network_uuid - is a network uuid that client wish to delete
2566 The return None or XML respond or false
2569 vca
= self
.connect_as_admin()
2571 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2572 if network_uuid
is None:
2575 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2576 vm_list_rest_call
= ''.join(url_list
)
2578 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2579 response
= Http
.delete(url
=vm_list_rest_call
,
2580 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2584 if response
.status_code
== 202:
2589 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2590 ip_profile
=None, isshared
='true'):
2592 Method create network in vCloud director
2595 network_name - is network name to be created.
2596 net_type - can be 'bridge','data','ptp','mgmt'.
2597 ip_profile is a dict containing the IP parameters of the network
2598 isshared - is a boolean
2599 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2600 It optional attribute. by default if no parent network indicate the first available will be used.
2603 The return network uuid or return None
2606 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2607 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2608 ip_profile
=ip_profile
,
2610 parent_network_uuid
=parent_network_uuid
,
2613 self
.logger
.debug("Failed create network {}.".format(network_name
))
2617 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2618 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2619 if len(vcd_uuid
) == 4:
2620 self
.logger
.info("Created new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2623 self
.logger
.debug("Failed create network {}".format(network_name
))
2626 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2627 ip_profile
=None, isshared
='true'):
2629 Method create network in vCloud director
2632 network_name - is network name to be created.
2633 net_type - can be 'bridge','data','ptp','mgmt'.
2634 ip_profile is a dict containing the IP parameters of the network
2635 isshared - is a boolean
2636 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2637 It optional attribute. by default if no parent network indicate the first available will be used.
2640 The return network uuid or return None
2643 vca
= self
.connect_as_admin()
2645 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2646 if network_name
is None:
2649 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2650 vm_list_rest_call
= ''.join(url_list
)
2651 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2652 response
= Http
.get(url
=vm_list_rest_call
,
2653 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2657 provider_network
= None
2658 available_networks
= None
2659 add_vdc_rest_url
= None
2661 if response
.status_code
!= requests
.codes
.ok
:
2662 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2663 response
.status_code
))
2667 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2668 for child
in vm_list_xmlroot
:
2669 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2670 provider_network
= child
.attrib
.get('href')
2671 # application/vnd.vmware.admin.providervdc+xml
2672 if child
.tag
.split("}")[1] == 'Link':
2673 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2674 and child
.attrib
.get('rel') == 'add':
2675 add_vdc_rest_url
= child
.attrib
.get('href')
2677 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2678 self
.logger
.debug("Respond body {}".format(response
.content
))
2681 # find pvdc provided available network
2682 response
= Http
.get(url
=provider_network
,
2683 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2686 if response
.status_code
!= requests
.codes
.ok
:
2687 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2688 response
.status_code
))
2691 # available_networks.split("/")[-1]
2693 if parent_network_uuid
is None:
2695 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2696 for child
in vm_list_xmlroot
.iter():
2697 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2698 for networks
in child
.iter():
2699 # application/vnd.vmware.admin.network+xml
2700 if networks
.attrib
.get('href') is not None:
2701 available_networks
= networks
.attrib
.get('href')
2707 #Configure IP profile of the network
2708 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2710 if 'subnet_address' not in ip_profile
or ip_profile
['subnet_address'] is None:
2711 subnet_rand
= random
.randint(0, 255)
2712 ip_base
= "192.168.{}.".format(subnet_rand
)
2713 ip_profile
['subnet_address'] = ip_base
+ "0/24"
2715 ip_base
= ip_profile
['subnet_address'].rsplit('.',1)[0] + '.'
2717 if 'gateway_address' not in ip_profile
or ip_profile
['gateway_address'] is None:
2718 ip_profile
['gateway_address']=ip_base
+ "1"
2719 if 'dhcp_count' not in ip_profile
or ip_profile
['dhcp_count'] is None:
2720 ip_profile
['dhcp_count']=DEFAULT_IP_PROFILE
['dhcp_count']
2721 if 'dhcp_enabled' not in ip_profile
or ip_profile
['dhcp_enabled'] is None:
2722 ip_profile
['dhcp_enabled']=DEFAULT_IP_PROFILE
['dhcp_enabled']
2723 if 'dhcp_start_address' not in ip_profile
or ip_profile
['dhcp_start_address'] is None:
2724 ip_profile
['dhcp_start_address']=ip_base
+ "3"
2725 if 'ip_version' not in ip_profile
or ip_profile
['ip_version'] is None:
2726 ip_profile
['ip_version']=DEFAULT_IP_PROFILE
['ip_version']
2727 if 'dns_address' not in ip_profile
or ip_profile
['dns_address'] is None:
2728 ip_profile
['dns_address']=ip_base
+ "2"
2730 gateway_address
=ip_profile
['gateway_address']
2731 dhcp_count
=int(ip_profile
['dhcp_count'])
2732 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2734 if ip_profile
['dhcp_enabled']==True:
2737 dhcp_enabled
='false'
2738 dhcp_start_address
=ip_profile
['dhcp_start_address']
2740 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2741 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2742 end_ip_int
+= dhcp_count
- 1
2743 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2745 ip_version
=ip_profile
['ip_version']
2746 dns_address
=ip_profile
['dns_address']
2747 except KeyError as exp
:
2748 self
.logger
.debug("Create Network REST: Key error {}".format(exp
))
2749 raise vimconn
.vimconnException("Create Network REST: Key error{}".format(exp
))
2751 # either use client provided UUID or search for a first available
2752 # if both are not defined we return none
2753 if parent_network_uuid
is not None:
2754 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2755 add_vdc_rest_url
= ''.join(url_list
)
2757 #Creating all networks as Direct Org VDC type networks.
2758 #Unused in case of Underlay (data/ptp) network interface.
2759 fence_mode
="bridged"
2760 is_inherited
='false'
2761 dns_list
= dns_address
.split(";")
2764 if len(dns_list
) >= 2:
2765 dns2_text
= "\n <Dns2>{}</Dns2>\n".format(dns_list
[1])
2766 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2767 <Description>Openmano created</Description>
2771 <IsInherited>{1:s}</IsInherited>
2772 <Gateway>{2:s}</Gateway>
2773 <Netmask>{3:s}</Netmask>
2774 <Dns1>{4:s}</Dns1>{5:s}
2775 <IsEnabled>{6:s}</IsEnabled>
2778 <StartAddress>{7:s}</StartAddress>
2779 <EndAddress>{8:s}</EndAddress>
2784 <ParentNetwork href="{9:s}"/>
2785 <FenceMode>{10:s}</FenceMode>
2787 <IsShared>{11:s}</IsShared>
2788 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2789 subnet_address
, dns1
, dns2_text
, dhcp_enabled
,
2790 dhcp_start_address
, dhcp_end_address
, available_networks
,
2791 fence_mode
, isshared
)
2793 headers
= vca
.vcloud_session
.get_vcloud_headers()
2794 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2796 response
= Http
.post(url
=add_vdc_rest_url
,
2802 if response
.status_code
!= 201:
2803 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
2804 .format(response
.status_code
,response
.content
))
2806 network
= networkType
.parseString(response
.content
, True)
2807 create_nw_task
= network
.get_Tasks().get_Task()[0]
2809 # if we all ok we respond with content after network creation completes
2810 # otherwise by default return None
2811 if create_nw_task
is not None:
2812 self
.logger
.debug("Create Network REST : Waiting for Network creation complete")
2813 status
= vca
.block_until_completed(create_nw_task
)
2815 return response
.content
2817 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2818 .format(response
.content
))
2819 except Exception as exp
:
2820 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2824 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2826 Method sets convert CIDR netmask address to normal IP format
2828 cidr_ip : CIDR IP address
2830 netmask : Converted netmask
2832 if cidr_ip
is not None:
2834 network
, net_bits
= cidr_ip
.split('/')
2835 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2841 def get_provider_rest(self
, vca
=None):
2843 Method gets provider vdc view from vcloud director
2846 network_name - is network name to be created.
2847 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2848 It optional attribute. by default if no parent network indicate the first available will be used.
2851 The return xml content of respond or None
2854 url_list
= [vca
.host
, '/api/admin']
2855 response
= Http
.get(url
=''.join(url_list
),
2856 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2860 if response
.status_code
== requests
.codes
.ok
:
2861 return response
.content
2864 def create_vdc(self
, vdc_name
=None):
2868 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2869 if xml_content
is not None:
2871 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2872 for child
in task_resp_xmlroot
:
2873 if child
.tag
.split("}")[1] == 'Owner':
2874 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2875 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2878 self
.logger
.debug("Respond body {}".format(xml_content
))
2882 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2884 Method create vdc in vCloud director based on VDC template.
2885 it uses pre-defined template that must be named openmano
2888 vdc_name - name of a new vdc.
2891 The return xml content of respond or None
2894 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2895 vca
= self
.connect()
2897 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2898 if vdc_name
is None:
2901 url_list
= [vca
.host
, '/api/vdcTemplates']
2902 vm_list_rest_call
= ''.join(url_list
)
2903 response
= Http
.get(url
=vm_list_rest_call
,
2904 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2908 # container url to a template
2909 vdc_template_ref
= None
2911 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2912 for child
in vm_list_xmlroot
:
2913 # application/vnd.vmware.admin.providervdc+xml
2914 # we need find a template from witch we instantiate VDC
2915 if child
.tag
.split("}")[1] == 'VdcTemplate':
2916 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2917 'name') == 'openmano':
2918 vdc_template_ref
= child
.attrib
.get('href')
2920 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2921 self
.logger
.debug("Respond body {}".format(response
.content
))
2924 # if we didn't found required pre defined template we return None
2925 if vdc_template_ref
is None:
2930 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2931 vm_list_rest_call
= ''.join(url_list
)
2932 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2933 <Source href="{1:s}"></Source>
2934 <Description>opnemano</Description>
2935 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2936 headers
= vca
.vcloud_session
.get_vcloud_headers()
2937 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2938 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2940 # if we all ok we respond with content otherwise by default None
2941 if response
.status_code
>= 200 and response
.status_code
< 300:
2942 return response
.content
2945 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2946 self
.logger
.debug("Respond body {}".format(response
.content
))
2950 def create_vdc_rest(self
, vdc_name
=None):
2952 Method create network in vCloud director
2955 network_name - is network name to be created.
2956 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2957 It optional attribute. by default if no parent network indicate the first available will be used.
2960 The return network uuid or return None
2963 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2965 vca
= self
.connect_as_admin()
2967 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2968 if vdc_name
is None:
2971 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2972 vm_list_rest_call
= ''.join(url_list
)
2973 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2974 response
= Http
.get(url
=vm_list_rest_call
,
2975 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2979 provider_vdc_ref
= None
2980 add_vdc_rest_url
= None
2981 available_networks
= None
2983 if response
.status_code
!= requests
.codes
.ok
:
2984 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2985 response
.status_code
))
2989 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2990 for child
in vm_list_xmlroot
:
2991 # application/vnd.vmware.admin.providervdc+xml
2992 if child
.tag
.split("}")[1] == 'Link':
2993 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2994 and child
.attrib
.get('rel') == 'add':
2995 add_vdc_rest_url
= child
.attrib
.get('href')
2997 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2998 self
.logger
.debug("Respond body {}".format(response
.content
))
3001 response
= self
.get_provider_rest(vca
=vca
)
3003 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
3004 for child
in vm_list_xmlroot
:
3005 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
3006 for sub_child
in child
:
3007 provider_vdc_ref
= sub_child
.attrib
.get('href')
3009 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
3010 self
.logger
.debug("Respond body {}".format(response
))
3013 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
3014 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
3015 <AllocationModel>ReservationPool</AllocationModel>
3016 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
3017 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
3018 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
3019 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
3020 <ProviderVdcReference
3021 name="Main Provider"
3023 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
3027 headers
= vca
.vcloud_session
.get_vcloud_headers()
3028 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
3029 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
3032 # if we all ok we respond with content otherwise by default None
3033 if response
.status_code
== 201:
3034 return response
.content
3037 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
3039 Method retrieve vapp detail from vCloud director
3042 vapp_uuid - is vapp identifier.
3045 The return network uuid or return None
3051 if need_admin_access
:
3052 vca
= self
.connect_as_admin()
3054 vca
= self
.connect()
3057 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3058 if vapp_uuid
is None:
3061 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
3062 get_vapp_restcall
= ''.join(url_list
)
3064 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3065 response
= Http
.get(url
=get_vapp_restcall
,
3066 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3070 if response
.status_code
!= requests
.codes
.ok
:
3071 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
3072 response
.status_code
))
3073 return parsed_respond
3076 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
3077 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
3079 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
3080 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
3081 'vmw': 'http://www.vmware.com/schema/ovf',
3082 'vm': 'http://www.vmware.com/vcloud/v1.5',
3083 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
3084 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3085 "xmlns":"http://www.vmware.com/vcloud/v1.5"
3088 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
3089 if created_section
is not None:
3090 parsed_respond
['created'] = created_section
.text
3092 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
3093 if network_section
is not None and 'networkName' in network_section
.attrib
:
3094 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
3096 ipscopes_section
= \
3097 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
3099 if ipscopes_section
is not None:
3100 for ipscope
in ipscopes_section
:
3101 for scope
in ipscope
:
3102 tag_key
= scope
.tag
.split("}")[1]
3103 if tag_key
== 'IpRanges':
3104 ip_ranges
= scope
.getchildren()
3105 for ipblock
in ip_ranges
:
3106 for block
in ipblock
:
3107 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
3109 parsed_respond
[tag_key
] = scope
.text
3111 # parse children section for other attrib
3112 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
3113 if children_section
is not None:
3114 parsed_respond
['name'] = children_section
.attrib
['name']
3115 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
3116 if "nestedHypervisorEnabled" in children_section
.attrib
else None
3117 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
3118 parsed_respond
['status'] = children_section
.attrib
['status']
3119 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
3120 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
3122 for adapters
in network_adapter
:
3123 adapter_key
= adapters
.tag
.split("}")[1]
3124 if adapter_key
== 'PrimaryNetworkConnectionIndex':
3125 parsed_respond
['primarynetwork'] = adapters
.text
3126 if adapter_key
== 'NetworkConnection':
3128 if 'network' in adapters
.attrib
:
3129 vnic
['network'] = adapters
.attrib
['network']
3130 for adapter
in adapters
:
3131 setting_key
= adapter
.tag
.split("}")[1]
3132 vnic
[setting_key
] = adapter
.text
3133 nic_list
.append(vnic
)
3135 for link
in children_section
:
3136 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
3137 if link
.attrib
['rel'] == 'screen:acquireTicket':
3138 parsed_respond
['acquireTicket'] = link
.attrib
3139 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
3140 parsed_respond
['acquireMksTicket'] = link
.attrib
3142 parsed_respond
['interfaces'] = nic_list
3143 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
3144 if vCloud_extension_section
is not None:
3145 vm_vcenter_info
= {}
3146 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
3147 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
3148 if vmext
is not None:
3149 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
3150 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
3152 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
3153 vm_virtual_hardware_info
= {}
3154 if virtual_hardware_section
is not None:
3155 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
3156 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
3157 disk_size
= item
.find("rasd:HostResource" ,namespaces
3158 ).attrib
["{"+namespaces
['vm']+"}capacity"]
3160 vm_virtual_hardware_info
["disk_size"]= disk_size
3163 for link
in virtual_hardware_section
:
3164 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
3165 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
3166 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
3169 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
3170 except Exception as exp
:
3171 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
3172 return parsed_respond
3174 def acuire_console(self
, vm_uuid
=None):
3176 vca
= self
.connect()
3178 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3182 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
3183 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
3184 console_dict
= vm_dict
['acquireTicket']
3185 console_rest_call
= console_dict
['href']
3187 response
= Http
.post(url
=console_rest_call
,
3188 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3192 if response
.status_code
== requests
.codes
.ok
:
3193 return response
.content
3197 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
3199 Method retrieve vm disk details
3202 vapp_uuid - is vapp identifier.
3203 flavor_disk - disk size as specified in VNFD (flavor)
3206 The return network uuid or return None
3210 #Flavor disk is in GB convert it into MB
3211 flavor_disk
= int(flavor_disk
) * 1024
3212 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
3214 vm_name
= vm_details
["name"]
3215 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
3217 if vm_details
and "vm_virtual_hardware" in vm_details
:
3218 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
3219 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
3221 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
3223 if flavor_disk
> vm_disk
:
3224 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
3225 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
3226 vm_disk
, flavor_disk
))
3229 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
3232 except Exception as exp
:
3233 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
3236 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
3238 Method retrieve modify vm disk size
3241 disk_href - vCD API URL to GET and PUT disk data
3242 disk_size - disk size as specified in VNFD (flavor)
3245 The return network uuid or return None
3247 vca
= self
.connect()
3249 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3250 if disk_href
is None or disk_size
is None:
3253 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3254 response
= Http
.get(url
=disk_href
,
3255 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3259 if response
.status_code
!= requests
.codes
.ok
:
3260 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
3261 response
.status_code
))
3264 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
3265 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
3266 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3268 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
3269 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
3270 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
3271 if disk_item
is not None:
3272 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
3275 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
3276 xml_declaration
=True)
3278 #Send PUT request to modify disk size
3279 headers
= vca
.vcloud_session
.get_vcloud_headers()
3280 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3282 response
= Http
.put(url
=disk_href
,
3285 verify
=vca
.verify
, logger
=self
.logger
)
3287 if response
.status_code
!= 202:
3288 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
3289 response
.status_code
))
3291 modify_disk_task
= taskType
.parseString(response
.content
, True)
3292 if type(modify_disk_task
) is GenericTask
:
3293 status
= vca
.block_until_completed(modify_disk_task
)
3298 except Exception as exp
:
3299 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
3302 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
3304 Method to attach pci devices to VM
3307 vapp_uuid - uuid of vApp/VM
3308 pci_devices - pci devices infromation as specified in VNFD (flavor)
3311 The status of add pci device task , vm object and
3312 vcenter_conect object
3315 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
3316 vcenter_conect
, content
= self
.get_vcenter_content()
3317 vm_moref_id
= self
.get_vm_moref_id(vapp_uuid
)
3321 no_of_pci_devices
= len(pci_devices
)
3322 if no_of_pci_devices
> 0:
3323 #Get VM and its host
3324 host_obj
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
3325 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
3326 if host_obj
and vm_obj
:
3327 #get PCI devies from host on which vapp is currently installed
3328 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
3330 if avilable_pci_devices
is None:
3331 #find other hosts with active pci devices
3332 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
3337 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3338 #Migrate vm to the host where PCI devices are availble
3339 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
3340 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
3341 if task
is not None:
3342 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3343 self
.logger
.info("Migrate VM status: {}".format(result
))
3344 host_obj
= new_host_obj
3346 self
.logger
.info("Fail to migrate VM : {}".format(result
))
3347 raise vimconn
.vimconnNotFoundException(
3348 "Fail to migrate VM : {} to host {}".format(
3353 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3354 #Add PCI devices one by one
3355 for pci_device
in avilable_pci_devices
:
3356 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
3358 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3360 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3362 self
.logger
.error("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3363 return True, vm_obj
, vcenter_conect
3365 self
.logger
.error("Currently there is no host with"\
3366 " {} number of avaialble PCI devices required for VM {}".format(
3370 raise vimconn
.vimconnNotFoundException(
3371 "Currently there is no host with {} "\
3372 "number of avaialble PCI devices required for VM {}".format(
3376 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
3378 except vmodl
.MethodFault
as error
:
3379 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
3380 return None, vm_obj
, vcenter_conect
3382 def get_vm_obj(self
, content
, mob_id
):
3384 Method to get the vsphere VM object associated with a given morf ID
3386 vapp_uuid - uuid of vApp/VM
3387 content - vCenter content object
3388 mob_id - mob_id of VM
3396 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3397 [vim
.VirtualMachine
], True
3399 for vm
in container
.view
:
3400 mobID
= vm
._GetMoId
()
3403 host_obj
= vm_obj
.runtime
.host
3405 except Exception as exp
:
3406 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
3407 return host_obj
, vm_obj
3409 def get_pci_devices(self
, host
, need_devices
):
3411 Method to get the details of pci devices on given host
3413 host - vSphere host object
3414 need_devices - number of pci devices needed on host
3417 array of pci devices
3421 used_devices_ids
= []
3425 pciPassthruInfo
= host
.config
.pciPassthruInfo
3426 pciDevies
= host
.hardware
.pciDevice
3428 for pci_status
in pciPassthruInfo
:
3429 if pci_status
.passthruActive
:
3430 for device
in pciDevies
:
3431 if device
.id == pci_status
.id:
3432 all_device_ids
.append(device
.id)
3433 all_devices
.append(device
)
3435 #check if devices are in use
3436 avalible_devices
= all_devices
3438 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
3439 vm_devices
= vm
.config
.hardware
.device
3440 for device
in vm_devices
:
3441 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
3442 if device
.backing
.id in all_device_ids
:
3443 for use_device
in avalible_devices
:
3444 if use_device
.id == device
.backing
.id:
3445 avalible_devices
.remove(use_device
)
3446 used_devices_ids
.append(device
.backing
.id)
3447 self
.logger
.debug("Device {} from devices {}"\
3448 "is in use".format(device
.backing
.id,
3451 if len(avalible_devices
) < need_devices
:
3452 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
3454 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
3458 required_devices
= avalible_devices
[:need_devices
]
3459 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
3460 len(avalible_devices
),
3463 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
3465 return required_devices
3467 except Exception as exp
:
3468 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
3472 def get_host_and_PCIdevices(self
, content
, need_devices
):
3474 Method to get the details of pci devices infromation on all hosts
3477 content - vSphere host object
3478 need_devices - number of pci devices needed on host
3481 array of pci devices and host object
3484 pci_device_objs
= None
3487 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3488 [vim
.HostSystem
], True)
3489 for host
in container
.view
:
3490 devices
= self
.get_pci_devices(host
, need_devices
)
3493 pci_device_objs
= devices
3495 except Exception as exp
:
3496 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
3498 return host_obj
,pci_device_objs
3500 def relocate_vm(self
, dest_host
, vm
) :
3502 Method to get the relocate VM to new host
3505 dest_host - vSphere host object
3506 vm - vSphere VM object
3513 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
3514 task
= vm
.Relocate(relocate_spec
)
3515 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
3516 except Exception as exp
:
3517 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
3518 dest_host
, vm
, exp
))
3521 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
3523 Waits and provides updates on a vSphere task
3525 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
3528 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
3529 if task
.info
.result
is not None and not hideResult
:
3530 self
.logger
.info('{} completed successfully, result: {}'.format(
3534 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
3536 self
.logger
.error('{} did not complete successfully: {} '.format(
3541 return task
.info
.result
3543 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
3545 Method to add pci device in given VM
3548 host_object - vSphere host object
3549 vm_object - vSphere VM object
3550 host_pci_dev - host_pci_dev must be one of the devices from the
3551 host_object.hardware.pciDevice list
3552 which is configured as a PCI passthrough device
3558 if vm_object
and host_object
and host_pci_dev
:
3560 #Add PCI device to VM
3561 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
3562 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
3564 if host_pci_dev
.id not in systemid_by_pciid
:
3565 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
3568 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
3569 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
3571 systemId
=systemid_by_pciid
[host_pci_dev
.id],
3572 vendorId
=host_pci_dev
.vendorId
,
3573 deviceName
=host_pci_dev
.deviceName
)
3575 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
3577 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
3578 new_device_config
.operation
= "add"
3579 vmConfigSpec
= vim
.vm
.ConfigSpec()
3580 vmConfigSpec
.deviceChange
= [new_device_config
]
3582 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
3583 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
3584 host_pci_dev
, vm_object
, host_object
)
3586 except Exception as exp
:
3587 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3593 def get_vm_vcenter_info(self
):
3595 Method to get details of vCenter and vm
3598 vapp_uuid - uuid of vApp or VM
3601 Moref Id of VM and deails of vCenter
3603 vm_vcenter_info
= {}
3605 if self
.vcenter_ip
is not None:
3606 vm_vcenter_info
["vm_vcenter_ip"] = self
.vcenter_ip
3608 raise vimconn
.vimconnException(message
="vCenter IP is not provided."\
3609 " Please provide vCenter IP while attaching datacenter to tenant in --config")
3610 if self
.vcenter_port
is not None:
3611 vm_vcenter_info
["vm_vcenter_port"] = self
.vcenter_port
3613 raise vimconn
.vimconnException(message
="vCenter port is not provided."\
3614 " Please provide vCenter port while attaching datacenter to tenant in --config")
3615 if self
.vcenter_user
is not None:
3616 vm_vcenter_info
["vm_vcenter_user"] = self
.vcenter_user
3618 raise vimconn
.vimconnException(message
="vCenter user is not provided."\
3619 " Please provide vCenter user while attaching datacenter to tenant in --config")
3621 if self
.vcenter_password
is not None:
3622 vm_vcenter_info
["vm_vcenter_password"] = self
.vcenter_password
3624 raise vimconn
.vimconnException(message
="vCenter user password is not provided."\
3625 " Please provide vCenter user password while attaching datacenter to tenant in --config")
3627 return vm_vcenter_info
3630 def get_vm_pci_details(self
, vmuuid
):
3632 Method to get VM PCI device details from vCenter
3635 vm_obj - vSphere VM object
3638 dict of PCI devives attached to VM
3641 vm_pci_devices_info
= {}
3643 vcenter_conect
, content
= self
.get_vcenter_content()
3644 vm_moref_id
= self
.get_vm_moref_id(vmuuid
)
3646 #Get VM and its host
3648 host_obj
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
3649 if host_obj
and vm_obj
:
3650 vm_pci_devices_info
["host_name"]= host_obj
.name
3651 vm_pci_devices_info
["host_ip"]= host_obj
.config
.network
.vnic
[0].spec
.ip
.ipAddress
3652 for device
in vm_obj
.config
.hardware
.device
:
3653 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
3654 device_details
={'devide_id':device
.backing
.id,
3655 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
,
3657 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
3659 self
.logger
.error("Can not connect to vCenter while getting "\
3660 "PCI devices infromationn")
3661 return vm_pci_devices_info
3662 except Exception as exp
:
3663 self
.logger
.error("Error occurred while getting VM infromationn"\
3664 " for VM : {}".format(exp
))
3665 raise vimconn
.vimconnException(message
=exp
)
3667 def add_network_adapter_to_vms(self
, vapp
, network_name
, primary_nic_index
, nicIndex
, net
, nic_type
=None):
3669 Method to add network adapter type to vm
3671 network_name - name of network
3672 primary_nic_index - int value for primary nic index
3673 nicIndex - int value for nic index
3674 nic_type - specify model name to which add to vm
3678 vca
= self
.connect()
3680 raise vimconn
.vimconnConnectionException("Failed to connect vCloud director")
3685 if 'floating_ip' in net
: floating_ip
= net
['floating_ip']
3687 # Stub for ip_address feature
3688 if 'ip_address' in net
: ip_address
= net
['ip_address']
3691 allocation_mode
= "POOL"
3693 allocation_mode
= "MANUAL"
3695 allocation_mode
= "DHCP"
3698 for vms
in vapp
._get
_vms
():
3699 vm_id
= (vms
.id).split(':')[-1]
3701 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca
.host
, vm_id
)
3703 response
= Http
.get(url
=url_rest_call
,
3704 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3707 if response
.status_code
!= 200:
3708 self
.logger
.error("REST call {} failed reason : {}"\
3709 "status code : {}".format(url_rest_call
,
3711 response
.status_code
))
3712 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to get "\
3713 "network connection section")
3715 data
= response
.content
3716 if '<PrimaryNetworkConnectionIndex>' not in data
:
3717 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3718 <NetworkConnection network="{}">
3719 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3720 <IsConnected>true</IsConnected>
3721 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3722 </NetworkConnection>""".format(primary_nic_index
, network_name
, nicIndex
,
3724 # Stub for ip_address feature
3726 ip_tag
= '<IpAddress>{}</IpAddress>'.format(ip_address
)
3727 item
= item
.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag
))
3729 data
= data
.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item
))
3731 new_item
= """<NetworkConnection network="{}">
3732 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3733 <IsConnected>true</IsConnected>
3734 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3735 </NetworkConnection>""".format(network_name
, nicIndex
,
3737 # Stub for ip_address feature
3739 ip_tag
= '<IpAddress>{}</IpAddress>'.format(ip_address
)
3740 new_item
= new_item
.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag
))
3742 data
= data
.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item
))
3744 headers
= vca
.vcloud_session
.get_vcloud_headers()
3745 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3746 response
= Http
.put(url
=url_rest_call
, headers
=headers
, data
=data
,
3749 if response
.status_code
!= 202:
3750 self
.logger
.error("REST call {} failed reason : {}"\
3751 "status code : {} ".format(url_rest_call
,
3753 response
.status_code
))
3754 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to update "\
3755 "network connection section")
3757 nic_task
= taskType
.parseString(response
.content
, True)
3758 if isinstance(nic_task
, GenericTask
):
3759 vca
.block_until_completed(nic_task
)
3760 self
.logger
.info("add_network_adapter_to_vms(): VM {} conneced to "\
3761 "default NIC type".format(vm_id
))
3763 self
.logger
.error("add_network_adapter_to_vms(): VM {} failed to "\
3764 "connect NIC type".format(vm_id
))
3766 for vms
in vapp
._get
_vms
():
3767 vm_id
= (vms
.id).split(':')[-1]
3769 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca
.host
, vm_id
)
3771 response
= Http
.get(url
=url_rest_call
,
3772 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3775 if response
.status_code
!= 200:
3776 self
.logger
.error("REST call {} failed reason : {}"\
3777 "status code : {}".format(url_rest_call
,
3779 response
.status_code
))
3780 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to get "\
3781 "network connection section")
3782 data
= response
.content
3783 if '<PrimaryNetworkConnectionIndex>' not in data
:
3784 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3785 <NetworkConnection network="{}">
3786 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3787 <IsConnected>true</IsConnected>
3788 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3789 <NetworkAdapterType>{}</NetworkAdapterType>
3790 </NetworkConnection>""".format(primary_nic_index
, network_name
, nicIndex
,
3791 allocation_mode
, nic_type
)
3792 # Stub for ip_address feature
3794 ip_tag
= '<IpAddress>{}</IpAddress>'.format(ip_address
)
3795 item
= item
.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag
))
3797 data
= data
.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item
))
3799 new_item
= """<NetworkConnection network="{}">
3800 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3801 <IsConnected>true</IsConnected>
3802 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3803 <NetworkAdapterType>{}</NetworkAdapterType>
3804 </NetworkConnection>""".format(network_name
, nicIndex
,
3805 allocation_mode
, nic_type
)
3806 # Stub for ip_address feature
3808 ip_tag
= '<IpAddress>{}</IpAddress>'.format(ip_address
)
3809 new_item
= new_item
.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag
))
3811 data
= data
.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item
))
3813 headers
= vca
.vcloud_session
.get_vcloud_headers()
3814 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3815 response
= Http
.put(url
=url_rest_call
, headers
=headers
, data
=data
,
3819 if response
.status_code
!= 202:
3820 self
.logger
.error("REST call {} failed reason : {}"\
3821 "status code : {}".format(url_rest_call
,
3823 response
.status_code
))
3824 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to update "\
3825 "network connection section")
3827 nic_task
= taskType
.parseString(response
.content
, True)
3828 if isinstance(nic_task
, GenericTask
):
3829 vca
.block_until_completed(nic_task
)
3830 self
.logger
.info("add_network_adapter_to_vms(): VM {} "\
3831 "conneced to NIC type {}".format(vm_id
, nic_type
))
3833 self
.logger
.error("add_network_adapter_to_vms(): VM {} "\
3834 "failed to connect NIC type {}".format(vm_id
, nic_type
))
3835 except Exception as exp
:
3836 self
.logger
.error("add_network_adapter_to_vms() : exception occurred "\
3837 "while adding Network adapter")
3838 raise vimconn
.vimconnException(message
=exp
)
3841 def set_numa_affinity(self
, vmuuid
, paired_threads_id
):
3843 Method to assign numa affinity in vm configuration parammeters
3846 paired_threads_id - one or more virtual processor
3852 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vmuuid
)
3853 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3855 if hasattr(ssl
, '_create_unverified_context'):
3856 context
= ssl
._create
_unverified
_context
()
3857 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3858 pwd
=self
.passwd
, port
=int(vm_vcenter_port
),
3860 atexit
.register(Disconnect
, vcenter_conect
)
3861 content
= vcenter_conect
.RetrieveContent()
3863 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3865 config_spec
= vim
.vm
.ConfigSpec()
3866 config_spec
.extraConfig
= []
3867 opt
= vim
.option
.OptionValue()
3868 opt
.key
= 'numa.nodeAffinity'
3869 opt
.value
= str(paired_threads_id
)
3870 config_spec
.extraConfig
.append(opt
)
3871 task
= vm_obj
.ReconfigVM_Task(config_spec
)
3873 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3874 extra_config
= vm_obj
.config
.extraConfig
3876 for opts
in extra_config
:
3877 if 'numa.nodeAffinity' in opts
.key
:
3879 self
.logger
.info("set_numa_affinity: Sucessfully assign numa affinity "\
3880 "value {} for vm {}".format(opt
.value
, vm_obj
))
3884 self
.logger
.error("set_numa_affinity: Failed to assign numa affinity")
3885 except Exception as exp
:
3886 self
.logger
.error("set_numa_affinity : exception occurred while setting numa affinity "\
3887 "for VM {} : {}".format(vm_obj
, vm_moref_id
))
3888 raise vimconn
.vimconnException("set_numa_affinity : Error {} failed to assign numa "\
3889 "affinity".format(exp
))
3893 def cloud_init(self
, vapp
, cloud_config
):
3895 Method to inject ssh-key
3897 cloud_config a dictionary with:
3898 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
3899 'users': (optional) list of users to be inserted, each item is a dict with:
3900 'name': (mandatory) user name,
3901 'key-pairs': (optional) list of strings with the public key to be inserted to the user
3902 'user-data': (optional) string is a text script to be passed directly to cloud-init
3903 'config-files': (optional). List of files to be transferred. Each item is a dict with:
3904 'dest': (mandatory) string with the destination absolute path
3905 'encoding': (optional, by default text). Can be one of:
3906 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
3907 'content' (mandatory): string with the content of the file
3908 'permissions': (optional) string with file permissions, typically octal notation '0644'
3909 'owner': (optional) file owner, string with the format 'owner:group'
3910 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk
3912 vca
= self
.connect()
3914 raise vimconn
.vimconnConnectionException("Failed to connect vCloud director")
3917 if isinstance(cloud_config
, dict):
3920 if "key-pairs" in cloud_config
:
3921 key_pairs
= cloud_config
["key-pairs"]
3923 if "users" in cloud_config
:
3924 userdata
= cloud_config
["users"]
3926 for key
in key_pairs
:
3927 for user
in userdata
:
3928 if 'name' in user
: user_name
= user
['name']
3929 if 'key-pairs' in user
and len(user
['key-pairs']) > 0:
3930 for user_key
in user
['key-pairs']:
3931 customize_script
= """
3933 echo performing customization tasks with param $1 at `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
3934 if [ "$1" = "precustomization" ];then
3935 echo performing precustomization tasks on `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
3936 if [ ! -d /root/.ssh ];then
3938 chown root:root /root/.ssh
3939 chmod 700 /root/.ssh
3940 touch /root/.ssh/authorized_keys
3941 chown root:root /root/.ssh/authorized_keys
3942 chmod 600 /root/.ssh/authorized_keys
3943 # make centos with selinux happy
3944 which restorecon && restorecon -Rv /root/.ssh
3945 echo '{key}' >> /root/.ssh/authorized_keys
3947 touch /root/.ssh/authorized_keys
3948 chown root:root /root/.ssh/authorized_keys
3949 chmod 600 /root/.ssh/authorized_keys
3950 echo '{key}' >> /root/.ssh/authorized_keys
3952 if [ -d /home/{user_name} ];then
3953 if [ ! -d /home/{user_name}/.ssh ];then
3954 mkdir /home/{user_name}/.ssh
3955 chown {user_name}:{user_name} /home/{user_name}/.ssh
3956 chmod 700 /home/{user_name}/.ssh
3957 touch /home/{user_name}/.ssh/authorized_keys
3958 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
3959 chmod 600 /home/{user_name}/.ssh/authorized_keys
3960 # make centos with selinux happy
3961 which restorecon && restorecon -Rv /home/{user_name}/.ssh
3962 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
3964 touch /home/{user_name}/.ssh/authorized_keys
3965 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
3966 chmod 600 /home/{user_name}/.ssh/authorized_keys
3967 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
3970 fi""".format(key
=key
, user_name
=user_name
, user_key
=user_key
)
3972 for vm
in vapp
._get
_vms
():
3974 task
= vapp
.customize_guest_os(vm_name
, customization_script
=customize_script
)
3975 if isinstance(task
, GenericTask
):
3976 vca
.block_until_completed(task
)
3977 self
.logger
.info("cloud_init : customized guest os task "\
3978 "completed for VM {}".format(vm_name
))
3980 self
.logger
.error("cloud_init : task for customized guest os"\
3981 "failed for VM {}".format(vm_name
))
3982 except Exception as exp
:
3983 self
.logger
.error("cloud_init : exception occurred while injecting "\
3985 raise vimconn
.vimconnException("cloud_init : Error {} failed to inject "\
3986 "ssh-key".format(exp
))
3989 def add_new_disk(self
, vca
, vapp_uuid
, disk_size
):
3991 Method to create an empty vm disk
3994 vapp_uuid - is vapp identifier.
3995 disk_size - size of disk to be created in GB
4003 #Disk size in GB, convert it into MB
4004 if disk_size
is not None:
4005 disk_size_mb
= int(disk_size
) * 1024
4006 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
4008 if vm_details
and "vm_virtual_hardware" in vm_details
:
4009 self
.logger
.info("Adding disk to VM: {} disk size:{}GB".format(vm_details
["name"], disk_size
))
4010 disk_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
4011 status
= self
.add_new_disk_rest(vca
, disk_href
, disk_size_mb
)
4013 except Exception as exp
:
4014 msg
= "Error occurred while creating new disk {}.".format(exp
)
4015 self
.rollback_newvm(vapp_uuid
, msg
)
4018 self
.logger
.info("Added new disk to VM: {} disk size:{}GB".format(vm_details
["name"], disk_size
))
4020 #If failed to add disk, delete VM
4021 msg
= "add_new_disk: Failed to add new disk to {}".format(vm_details
["name"])
4022 self
.rollback_newvm(vapp_uuid
, msg
)
4025 def add_new_disk_rest(self
, vca
, disk_href
, disk_size_mb
):
4027 Retrives vApp Disks section & add new empty disk
4030 disk_href: Disk section href to addd disk
4031 disk_size_mb: Disk size in MB
4033 Returns: Status of add new disk task
4036 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
4037 response
= Http
.get(url
=disk_href
,
4038 headers
=vca
.vcloud_session
.get_vcloud_headers(),
4042 if response
.status_code
!= requests
.codes
.ok
:
4043 self
.logger
.error("add_new_disk_rest: GET REST API call {} failed. Return status code {}"
4044 .format(disk_href
, response
.status_code
))
4047 #Find but type & max of instance IDs assigned to disks
4048 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
4049 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
4050 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
4052 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
4053 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
4054 inst_id
= int(item
.find("rasd:InstanceID" ,namespaces
).text
)
4055 if inst_id
> instance_id
:
4056 instance_id
= inst_id
4057 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
4058 bus_subtype
= disk_item
.attrib
["{"+namespaces
['xmlns']+"}busSubType"]
4059 bus_type
= disk_item
.attrib
["{"+namespaces
['xmlns']+"}busType"]
4061 instance_id
= instance_id
+ 1
4062 new_item
= """<Item>
4063 <rasd:Description>Hard disk</rasd:Description>
4064 <rasd:ElementName>New disk</rasd:ElementName>
4066 xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
4067 vcloud:capacity="{}"
4068 vcloud:busSubType="{}"
4069 vcloud:busType="{}"></rasd:HostResource>
4070 <rasd:InstanceID>{}</rasd:InstanceID>
4071 <rasd:ResourceType>17</rasd:ResourceType>
4072 </Item>""".format(disk_size_mb
, bus_subtype
, bus_type
, instance_id
)
4074 new_data
= response
.content
4075 #Add new item at the bottom
4076 new_data
= new_data
.replace('</Item>\n</RasdItemsList>', '</Item>\n{}\n</RasdItemsList>'.format(new_item
))
4078 # Send PUT request to modify virtual hardware section with new disk
4079 headers
= vca
.vcloud_session
.get_vcloud_headers()
4080 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
4082 response
= Http
.put(url
=disk_href
,
4085 verify
=vca
.verify
, logger
=self
.logger
)
4087 if response
.status_code
!= 202:
4088 self
.logger
.error("PUT REST API call {} failed. Return status code {}. Response Content:{}"
4089 .format(disk_href
, response
.status_code
, response
.content
))
4091 add_disk_task
= taskType
.parseString(response
.content
, True)
4092 if type(add_disk_task
) is GenericTask
:
4093 status
= vca
.block_until_completed(add_disk_task
)
4095 self
.logger
.error("Add new disk REST task failed to add {} MB disk".format(disk_size_mb
))
4097 except Exception as exp
:
4098 self
.logger
.error("Error occurred calling rest api for creating new disk {}".format(exp
))
4103 def add_existing_disk(self
, catalogs
=None, image_id
=None, size
=None, template_name
=None, vapp_uuid
=None):
4105 Method to add existing disk to vm
4107 catalogs - List of VDC catalogs
4108 image_id - Catalog ID
4109 template_name - Name of template in catalog
4110 vapp_uuid - UUID of vApp
4115 vcenter_conect
, content
= self
.get_vcenter_content()
4116 #find moref-id of vm in image
4117 catalog_vm_info
= self
.get_vapp_template_details(catalogs
=catalogs
,
4121 if catalog_vm_info
and "vm_vcenter_info" in catalog_vm_info
:
4122 if "vm_moref_id" in catalog_vm_info
["vm_vcenter_info"]:
4123 catalog_vm_moref_id
= catalog_vm_info
["vm_vcenter_info"].get("vm_moref_id", None)
4124 if catalog_vm_moref_id
:
4125 self
.logger
.info("Moref_id of VM in catalog : {}" .format(catalog_vm_moref_id
))
4126 host
, catalog_vm_obj
= self
.get_vm_obj(content
, catalog_vm_moref_id
)
4129 disk_info
= self
.find_disk(catalog_vm_obj
)
4131 exp_msg
= "No VM with image id {} found".format(image_id
)
4132 self
.rollback_newvm(vapp_uuid
, exp_msg
, exp_type
="NotFound")
4134 exp_msg
= "No Image found with image ID {} ".format(image_id
)
4135 self
.rollback_newvm(vapp_uuid
, exp_msg
, exp_type
="NotFound")
4138 self
.logger
.info("Existing disk_info : {}".format(disk_info
))
4140 vm_moref_id
= self
.get_vm_moref_id(vapp_uuid
)
4141 host
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
4143 status
= self
.add_disk(vcenter_conect
=vcenter_conect
,
4145 disk_info
=disk_info
,
4150 self
.logger
.info("Disk from image id {} added to {}".format(image_id
,
4154 msg
= "No disk found with image id {} to add in VM {}".format(
4157 self
.rollback_newvm(vapp_uuid
, msg
, exp_type
="NotFound")
4160 def find_disk(self
, vm_obj
):
4162 Method to find details of existing disk in VM
4164 vm_obj - vCenter object of VM
4165 image_id - Catalog ID
4167 disk_info : dict of disk details
4172 devices
= vm_obj
.config
.hardware
.device
4173 for device
in devices
:
4174 if type(device
) is vim
.vm
.device
.VirtualDisk
:
4175 if isinstance(device
.backing
,vim
.vm
.device
.VirtualDisk
.FlatVer2BackingInfo
) and hasattr(device
.backing
, 'fileName'):
4176 disk_info
["full_path"] = device
.backing
.fileName
4177 disk_info
["datastore"] = device
.backing
.datastore
4178 disk_info
["capacityKB"] = device
.capacityInKB
4180 except Exception as exp
:
4181 self
.logger
.error("find_disk() : exception occurred while "\
4182 "getting existing disk details :{}".format(exp
))
4186 def add_disk(self
, vcenter_conect
=None, vm
=None, size
=None, vapp_uuid
=None, disk_info
={}):
4188 Method to add existing disk in VM
4190 vcenter_conect - vCenter content object
4191 vm - vCenter vm object
4192 disk_info : dict of disk details
4194 status : status of add disk task
4196 datastore
= disk_info
["datastore"] if "datastore" in disk_info
else None
4197 fullpath
= disk_info
["full_path"] if "full_path" in disk_info
else None
4198 capacityKB
= disk_info
["capacityKB"] if "capacityKB" in disk_info
else None
4199 if size
is not None:
4200 #Convert size from GB to KB
4201 sizeKB
= int(size
) * 1024 * 1024
4202 #compare size of existing disk and user given size.Assign whicherver is greater
4203 self
.logger
.info("Add Existing disk : sizeKB {} , capacityKB {}".format(
4204 sizeKB
, capacityKB
))
4205 if sizeKB
> capacityKB
:
4208 if datastore
and fullpath
and capacityKB
:
4210 spec
= vim
.vm
.ConfigSpec()
4211 # get all disks on a VM, set unit_number to the next available
4213 for dev
in vm
.config
.hardware
.device
:
4214 if hasattr(dev
.backing
, 'fileName'):
4215 unit_number
= int(dev
.unitNumber
) + 1
4216 # unit_number 7 reserved for scsi controller
4217 if unit_number
== 7:
4219 if isinstance(dev
, vim
.vm
.device
.VirtualDisk
):
4220 #vim.vm.device.VirtualSCSIController
4221 controller_key
= dev
.controllerKey
4223 self
.logger
.info("Add Existing disk : unit number {} , controller key {}".format(
4224 unit_number
, controller_key
))
4227 disk_spec
= vim
.vm
.device
.VirtualDeviceSpec()
4228 disk_spec
.operation
= vim
.vm
.device
.VirtualDeviceSpec
.Operation
.add
4229 disk_spec
.device
= vim
.vm
.device
.VirtualDisk()
4230 disk_spec
.device
.backing
= \
4231 vim
.vm
.device
.VirtualDisk
.FlatVer2BackingInfo()
4232 disk_spec
.device
.backing
.thinProvisioned
= True
4233 disk_spec
.device
.backing
.diskMode
= 'persistent'
4234 disk_spec
.device
.backing
.datastore
= datastore
4235 disk_spec
.device
.backing
.fileName
= fullpath
4237 disk_spec
.device
.unitNumber
= unit_number
4238 disk_spec
.device
.capacityInKB
= capacityKB
4239 disk_spec
.device
.controllerKey
= controller_key
4240 dev_changes
.append(disk_spec
)
4241 spec
.deviceChange
= dev_changes
4242 task
= vm
.ReconfigVM_Task(spec
=spec
)
4243 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
4245 except Exception as exp
:
4246 exp_msg
= "add_disk() : exception {} occurred while adding disk "\
4247 "{} to vm {}".format(exp
,
4250 self
.rollback_newvm(vapp_uuid
, exp_msg
)
4252 msg
= "add_disk() : Can not add disk to VM with disk info {} ".format(disk_info
)
4253 self
.rollback_newvm(vapp_uuid
, msg
)
4256 def get_vcenter_content(self
):
4258 Get the vsphere content object
4261 vm_vcenter_info
= self
.get_vm_vcenter_info()
4262 except Exception as exp
:
4263 self
.logger
.error("Error occurred while getting vCenter infromationn"\
4264 " for VM : {}".format(exp
))
4265 raise vimconn
.vimconnException(message
=exp
)
4268 if hasattr(ssl
, '_create_unverified_context'):
4269 context
= ssl
._create
_unverified
_context
()
4271 vcenter_conect
= SmartConnect(
4272 host
=vm_vcenter_info
["vm_vcenter_ip"],
4273 user
=vm_vcenter_info
["vm_vcenter_user"],
4274 pwd
=vm_vcenter_info
["vm_vcenter_password"],
4275 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
4278 atexit
.register(Disconnect
, vcenter_conect
)
4279 content
= vcenter_conect
.RetrieveContent()
4280 return vcenter_conect
, content
4283 def get_vm_moref_id(self
, vapp_uuid
):
4285 Get the moref_id of given VM
4289 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
4290 if vm_details
and "vm_vcenter_info" in vm_details
:
4291 vm_moref_id
= vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
4295 except Exception as exp
:
4296 self
.logger
.error("Error occurred while getting VM moref ID "\
4297 " for VM : {}".format(exp
))
4301 def get_vapp_template_details(self
, catalogs
=None, image_id
=None , template_name
=None):
4303 Method to get vApp template details
4305 catalogs - list of VDC catalogs
4306 image_id - Catalog ID to find
4307 template_name : template name in catalog
4309 parsed_respond : dict of vApp tempalte details
4311 parsed_response
= {}
4313 vca
= self
.connect_as_admin()
4315 raise vimconn
.vimconnConnectionException("self.connect() is failed")
4318 catalog
= self
.get_catalog_obj(image_id
, catalogs
)
4320 template_name
= self
.get_catalogbyid(image_id
, catalogs
)
4321 catalog_items
= filter(lambda catalogItemRef
: catalogItemRef
.get_name() == template_name
, catalog
.get_CatalogItems().get_CatalogItem())
4322 if len(catalog_items
) == 1:
4323 response
= Http
.get(catalog_items
[0].get_href(),
4324 headers
=vca
.vcloud_session
.get_vcloud_headers(),
4327 catalogItem
= XmlElementTree
.fromstring(response
.content
)
4328 entity
= [child
for child
in catalogItem
if child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
4329 vapp_tempalte_href
= entity
.get("href")
4330 #get vapp details and parse moref id
4332 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
4333 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
4334 'vmw': 'http://www.vmware.com/schema/ovf',
4335 'vm': 'http://www.vmware.com/vcloud/v1.5',
4336 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
4337 'vmext':"http://www.vmware.com/vcloud/extension/v1.5",
4338 'xmlns':"http://www.vmware.com/vcloud/v1.5"
4341 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
4342 response
= Http
.get(url
=vapp_tempalte_href
,
4343 headers
=vca
.vcloud_session
.get_vcloud_headers(),
4348 if response
.status_code
!= requests
.codes
.ok
:
4349 self
.logger
.debug("REST API call {} failed. Return status code {}".format(
4350 vapp_tempalte_href
, response
.status_code
))
4353 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
4354 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
4355 if children_section
is not None:
4356 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
4357 if vCloud_extension_section
is not None:
4358 vm_vcenter_info
= {}
4359 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
4360 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
4361 if vmext
is not None:
4362 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
4363 parsed_response
["vm_vcenter_info"]= vm_vcenter_info
4365 except Exception as exp
:
4366 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
4368 return parsed_response
4371 def rollback_newvm(self
, vapp_uuid
, msg
, exp_type
="Genric"):
4373 Method to delete vApp
4375 vapp_uuid - vApp UUID
4376 msg - Error message to be logged
4377 exp_type : Exception type
4382 status
= self
.delete_vminstance(vapp_uuid
)
4385 self
.logger
.error(msg
)
4386 if exp_type
== "Genric":
4387 raise vimconn
.vimconnException(msg
)
4388 elif exp_type
== "NotFound":
4389 raise vimconn
.vimconnNotFoundException(message
=msg
)
4391 def add_sriov(self
, vapp_uuid
, sriov_nets
, vmname_andid
):
4393 Method to attach SRIOV adapters to VM
4396 vapp_uuid - uuid of vApp/VM
4397 sriov_nets - SRIOV devices infromation as specified in VNFD (flavor)
4398 vmname_andid - vmname
4401 The status of add SRIOV adapter task , vm object and
4402 vcenter_conect object
4405 vcenter_conect
, content
= self
.get_vcenter_content()
4406 vm_moref_id
= self
.get_vm_moref_id(vapp_uuid
)
4410 no_of_sriov_devices
= len(sriov_nets
)
4411 if no_of_sriov_devices
> 0:
4412 #Get VM and its host
4413 host_obj
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
4414 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
4415 if host_obj
and vm_obj
:
4416 #get SRIOV devies from host on which vapp is currently installed
4417 avilable_sriov_devices
= self
.get_sriov_devices(host_obj
,
4418 no_of_sriov_devices
,
4421 if len(avilable_sriov_devices
) == 0:
4422 #find other hosts with active pci devices
4423 new_host_obj
, avilable_sriov_devices
= self
.get_host_and_sriov_devices(
4425 no_of_sriov_devices
,
4428 if new_host_obj
is not None and len(avilable_sriov_devices
)> 0:
4429 #Migrate vm to the host where SRIOV devices are available
4430 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
,
4432 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
4433 if task
is not None:
4434 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
4435 self
.logger
.info("Migrate VM status: {}".format(result
))
4436 host_obj
= new_host_obj
4438 self
.logger
.info("Fail to migrate VM : {}".format(result
))
4439 raise vimconn
.vimconnNotFoundException(
4440 "Fail to migrate VM : {} to host {}".format(
4445 if host_obj
is not None and avilable_sriov_devices
is not None and len(avilable_sriov_devices
)> 0:
4446 #Add SRIOV devices one by one
4447 for sriov_net
in sriov_nets
:
4448 network_name
= sriov_net
.get('net_id')
4449 dvs_portgr_name
= self
.create_dvPort_group(network_name
)
4450 if sriov_net
.get('type') == "VF":
4451 #add vlan ID ,Modify portgroup for vlan ID
4452 self
.configure_vlanID(content
, vcenter_conect
, network_name
)
4454 task
= self
.add_sriov_to_vm(content
,
4458 avilable_sriov_devices
[0]
4461 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
4463 self
.logger
.info("Added SRIOV {} to VM {}".format(
4464 no_of_sriov_devices
,
4467 self
.logger
.error("Fail to add SRIOV {} to VM {}".format(
4468 no_of_sriov_devices
,
4470 raise vimconn
.vimconnUnexpectedResponse(
4471 "Fail to add SRIOV adapter in VM ".format(str(vm_obj
))
4473 return True, vm_obj
, vcenter_conect
4475 self
.logger
.error("Currently there is no host with"\
4476 " {} number of avaialble SRIOV "\
4477 "VFs required for VM {}".format(
4478 no_of_sriov_devices
,
4481 raise vimconn
.vimconnNotFoundException(
4482 "Currently there is no host with {} "\
4483 "number of avaialble SRIOV devices required for VM {}".format(
4484 no_of_sriov_devices
,
4487 self
.logger
.debug("No infromation about SRIOV devices {} ",sriov_nets
)
4489 except vmodl
.MethodFault
as error
:
4490 self
.logger
.error("Error occurred while adding SRIOV {} ",error
)
4491 return None, vm_obj
, vcenter_conect
4494 def get_sriov_devices(self
,host
, no_of_vfs
):
4496 Method to get the details of SRIOV devices on given host
4498 host - vSphere host object
4499 no_of_vfs - number of VFs needed on host
4502 array of SRIOV devices
4506 for device
in host
.config
.pciPassthruInfo
:
4507 if isinstance(device
,vim
.host
.SriovInfo
) and device
.sriovActive
:
4508 if device
.numVirtualFunction
>= no_of_vfs
:
4509 sriovInfo
.append(device
)
4514 def get_host_and_sriov_devices(self
, content
, no_of_vfs
):
4516 Method to get the details of SRIOV devices infromation on all hosts
4519 content - vSphere host object
4520 no_of_vfs - number of pci VFs needed on host
4523 array of SRIOV devices and host object
4526 sriov_device_objs
= None
4529 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
4530 [vim
.HostSystem
], True)
4531 for host
in container
.view
:
4532 devices
= self
.get_sriov_devices(host
, no_of_vfs
)
4535 sriov_device_objs
= devices
4537 except Exception as exp
:
4538 self
.logger
.error("Error {} occurred while finding SRIOV devices on host: {}".format(exp
, host_obj
))
4540 return host_obj
,sriov_device_objs
4543 def add_sriov_to_vm(self
,content
, vm_obj
, host_obj
, network_name
, sriov_device
):
4545 Method to add SRIOV adapter to vm
4548 host_obj - vSphere host object
4549 vm_obj - vSphere vm object
4550 content - vCenter content object
4551 network_name - name of distributed virtaul portgroup
4552 sriov_device - SRIOV device info
4558 vnic_label
= "sriov nic"
4560 dvs_portgr
= self
.get_dvport_group(network_name
)
4561 network_name
= dvs_portgr
.name
4562 nic
= vim
.vm
.device
.VirtualDeviceSpec()
4564 nic
.operation
= vim
.vm
.device
.VirtualDeviceSpec
.Operation
.add
4565 nic
.device
= vim
.vm
.device
.VirtualSriovEthernetCard()
4566 nic
.device
.addressType
= 'assigned'
4567 #nic.device.key = 13016
4568 nic
.device
.deviceInfo
= vim
.Description()
4569 nic
.device
.deviceInfo
.label
= vnic_label
4570 nic
.device
.deviceInfo
.summary
= network_name
4571 nic
.device
.backing
= vim
.vm
.device
.VirtualEthernetCard
.NetworkBackingInfo()
4573 nic
.device
.backing
.network
= self
.get_obj(content
, [vim
.Network
], network_name
)
4574 nic
.device
.backing
.deviceName
= network_name
4575 nic
.device
.backing
.useAutoDetect
= False
4576 nic
.device
.connectable
= vim
.vm
.device
.VirtualDevice
.ConnectInfo()
4577 nic
.device
.connectable
.startConnected
= True
4578 nic
.device
.connectable
.allowGuestControl
= True
4580 nic
.device
.sriovBacking
= vim
.vm
.device
.VirtualSriovEthernetCard
.SriovBackingInfo()
4581 nic
.device
.sriovBacking
.physicalFunctionBacking
= vim
.vm
.device
.VirtualPCIPassthrough
.DeviceBackingInfo()
4582 nic
.device
.sriovBacking
.physicalFunctionBacking
.id = sriov_device
.id
4585 vmconf
= vim
.vm
.ConfigSpec(deviceChange
=devices
)
4586 task
= vm_obj
.ReconfigVM_Task(vmconf
)
4588 except Exception as exp
:
4589 self
.logger
.error("Error {} occurred while adding SRIOV adapter in VM: {}".format(exp
, vm_obj
))
4593 def create_dvPort_group(self
, network_name
):
4595 Method to create disributed virtual portgroup
4598 network_name - name of network/portgroup
4604 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
4605 network_name
=''.join(new_network_name
)
4606 vcenter_conect
, content
= self
.get_vcenter_content()
4608 dv_switch
= self
.get_obj(content
, [vim
.DistributedVirtualSwitch
], self
.dvs_name
)
4610 dv_pg_spec
= vim
.dvs
.DistributedVirtualPortgroup
.ConfigSpec()
4611 dv_pg_spec
.name
= network_name
4613 dv_pg_spec
.type = vim
.dvs
.DistributedVirtualPortgroup
.PortgroupType
.earlyBinding
4614 dv_pg_spec
.defaultPortConfig
= vim
.dvs
.VmwareDistributedVirtualSwitch
.VmwarePortConfigPolicy()
4615 dv_pg_spec
.defaultPortConfig
.securityPolicy
= vim
.dvs
.VmwareDistributedVirtualSwitch
.SecurityPolicy()
4616 dv_pg_spec
.defaultPortConfig
.securityPolicy
.allowPromiscuous
= vim
.BoolPolicy(value
=False)
4617 dv_pg_spec
.defaultPortConfig
.securityPolicy
.forgedTransmits
= vim
.BoolPolicy(value
=False)
4618 dv_pg_spec
.defaultPortConfig
.securityPolicy
.macChanges
= vim
.BoolPolicy(value
=False)
4620 task
= dv_switch
.AddDVPortgroup_Task([dv_pg_spec
])
4621 self
.wait_for_vcenter_task(task
, vcenter_conect
)
4623 dvPort_group
= self
.get_obj(content
, [vim
.dvs
.DistributedVirtualPortgroup
], network_name
)
4625 self
.logger
.info("Created disributed virtaul port group: {}".format(dvPort_group
))
4626 return dvPort_group
.key
4628 self
.logger
.debug("No disributed virtual switch found with name {}".format(network_name
))
4630 except Exception as exp
:
4631 self
.logger
.error("Error occurred while creating disributed virtaul port group {}"\
4632 " : {}".format(network_name
, exp
))
4635 def reconfig_portgroup(self
, content
, dvPort_group_name
, config_info
={}):
4637 Method to reconfigure disributed virtual portgroup
4640 dvPort_group_name - name of disributed virtual portgroup
4641 content - vCenter content object
4642 config_info - disributed virtual portgroup configuration
4648 dvPort_group
= self
.get_dvport_group(dvPort_group_name
)
4650 dv_pg_spec
= vim
.dvs
.DistributedVirtualPortgroup
.ConfigSpec()
4651 dv_pg_spec
.configVersion
= dvPort_group
.config
.configVersion
4652 dv_pg_spec
.defaultPortConfig
= vim
.dvs
.VmwareDistributedVirtualSwitch
.VmwarePortConfigPolicy()
4653 if "vlanID" in config_info
:
4654 dv_pg_spec
.defaultPortConfig
.vlan
= vim
.dvs
.VmwareDistributedVirtualSwitch
.VlanIdSpec()
4655 dv_pg_spec
.defaultPortConfig
.vlan
.vlanId
= config_info
.get('vlanID')
4657 task
= dvPort_group
.ReconfigureDVPortgroup_Task(spec
=dv_pg_spec
)
4661 except Exception as exp
:
4662 self
.logger
.error("Error occurred while reconfiguraing disributed virtaul port group {}"\
4663 " : {}".format(dvPort_group_name
, exp
))
4667 def destroy_dvport_group(self
, dvPort_group_name
):
4669 Method to destroy disributed virtual portgroup
4672 network_name - name of network/portgroup
4675 True if portgroup successfully got deleted else false
4677 vcenter_conect
, content
= self
.get_vcenter_content()
4680 dvPort_group
= self
.get_dvport_group(dvPort_group_name
)
4682 task
= dvPort_group
.Destroy_Task()
4683 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
4685 except vmodl
.MethodFault
as exp
:
4686 self
.logger
.error("Caught vmodl fault {} while deleting disributed virtaul port group {}".format(
4687 exp
, dvPort_group_name
))
4691 def get_dvport_group(self
, dvPort_group_name
):
4693 Method to get disributed virtual portgroup
4696 network_name - name of network/portgroup
4701 vcenter_conect
, content
= self
.get_vcenter_content()
4704 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
, [vim
.dvs
.DistributedVirtualPortgroup
], True)
4705 for item
in container
.view
:
4706 if item
.key
== dvPort_group_name
:
4710 except vmodl
.MethodFault
as exp
:
4711 self
.logger
.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
4712 exp
, dvPort_group_name
))
4715 def get_vlanID_from_dvs_portgr(self
, dvPort_group_name
):
4717 Method to get disributed virtual portgroup vlanID
4720 network_name - name of network/portgroup
4727 dvPort_group
= self
.get_dvport_group(dvPort_group_name
)
4729 vlanId
= dvPort_group
.config
.defaultPortConfig
.vlan
.vlanId
4730 except vmodl
.MethodFault
as exp
:
4731 self
.logger
.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
4732 exp
, dvPort_group_name
))
4736 def configure_vlanID(self
, content
, vcenter_conect
, dvPort_group_name
):
4738 Method to configure vlanID in disributed virtual portgroup vlanID
4741 network_name - name of network/portgroup
4746 vlanID
= self
.get_vlanID_from_dvs_portgr(dvPort_group_name
)
4749 vlanID
= self
.genrate_vlanID(dvPort_group_name
)
4750 config
= {"vlanID":vlanID
}
4751 task
= self
.reconfig_portgroup(content
, dvPort_group_name
,
4754 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
4756 self
.logger
.info("Reconfigured Port group {} for vlan ID {}".format(
4757 dvPort_group_name
,vlanID
))
4759 self
.logger
.error("Fail reconfigure portgroup {} for vlanID{}".format(
4760 dvPort_group_name
, vlanID
))
4763 def genrate_vlanID(self
, network_name
):
4765 Method to get unused vlanID
4767 network_name - name of network/portgroup
4773 if self
.config
.get('vlanID_range') == None:
4774 raise vimconn
.vimconnConflictException("You must provide a 'vlanID_range' "\
4775 "at config value before creating sriov network with vlan tag")
4776 if "used_vlanIDs" not in self
.persistent_info
:
4777 self
.persistent_info
["used_vlanIDs"] = {}
4779 used_ids
= self
.persistent_info
["used_vlanIDs"].values()
4781 for vlanID_range
in self
.config
.get('vlanID_range'):
4782 start_vlanid
, end_vlanid
= vlanID_range
.split("-")
4783 if start_vlanid
> end_vlanid
:
4784 raise vimconn
.vimconnConflictException("Invalid vlan ID range {}".format(
4787 for id in xrange(int(start_vlanid
), int(end_vlanid
) + 1):
4788 if id not in used_ids
:
4790 self
.persistent_info
["used_vlanIDs"][network_name
] = vlan_id
4793 raise vimconn
.vimconnConflictException("All Vlan IDs are in use")
4796 def get_obj(self
, content
, vimtype
, name
):
4798 Get the vsphere object associated with a given text name
4801 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
, vimtype
, True)
4802 for item
in container
.view
:
4803 if item
.name
== name
:
4809 def insert_media_to_vm(self
, vapp
, image_id
):
4811 Method to insert media CD-ROM (ISO image) from catalog to vm.
4812 vapp - vapp object to get vm id
4813 Image_id - image id for cdrom to be inerted to vm
4815 # create connection object
4816 vca
= self
.connect()
4818 # fetching catalog details
4819 rest_url
= "{}/api/catalog/{}".format(vca
.host
,image_id
)
4820 response
= Http
.get(url
=rest_url
,
4821 headers
=vca
.vcloud_session
.get_vcloud_headers(),
4825 if response
.status_code
!= 200:
4826 self
.logger
.error("REST call {} failed reason : {}"\
4827 "status code : {}".format(url_rest_call
,
4829 response
.status_code
))
4830 raise vimconn
.vimconnException("insert_media_to_vm(): Failed to get "\
4832 # searching iso name and id
4833 iso_name
,media_id
= self
.get_media_details(vca
, response
.content
)
4835 if iso_name
and media_id
:
4836 data
="""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
4837 <ns6:MediaInsertOrEjectParams
4838 xmlns="http://www.vmware.com/vcloud/versions" xmlns:ns2="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns3="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ns6="http://www.vmware.com/vcloud/v1.5" xmlns:ns7="http://www.vmware.com/schema/ovf" xmlns:ns8="http://schemas.dmtf.org/ovf/environment/1" xmlns:ns9="http://www.vmware.com/vcloud/extension/v1.5">
4840 type="application/vnd.vmware.vcloud.media+xml"
4842 id="urn:vcloud:media:{}"
4843 href="https://{}/api/media/{}"/>
4844 </ns6:MediaInsertOrEjectParams>""".format(iso_name
, media_id
,
4847 for vms
in vapp
._get
_vms
():
4848 vm_id
= (vms
.id).split(':')[-1]
4850 headers
= vca
.vcloud_session
.get_vcloud_headers()
4851 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.mediaInsertOrEjectParams+xml'
4852 rest_url
= "{}/api/vApp/vm-{}/media/action/insertMedia".format(vca
.host
,vm_id
)
4854 response
= Http
.post(url
=rest_url
,
4860 if response
.status_code
!= 202:
4861 self
.logger
.error("Failed to insert CD-ROM to vm")
4862 raise vimconn
.vimconnException("insert_media_to_vm() : Failed to insert"\
4865 task
= taskType
.parseString(response
.content
, True)
4866 if isinstance(task
, GenericTask
):
4867 vca
.block_until_completed(task
)
4868 self
.logger
.info("insert_media_to_vm(): Sucessfully inserted media ISO"\
4869 " image to vm {}".format(vm_id
))
4870 except Exception as exp
:
4871 self
.logger
.error("insert_media_to_vm() : exception occurred "\
4872 "while inserting media CD-ROM")
4873 raise vimconn
.vimconnException(message
=exp
)
4876 def get_media_details(self
, vca
, content
):
4878 Method to get catalog item details
4879 vca - connection object
4880 content - Catalog details
4881 Return - Media name, media id
4883 cataloghref_list
= []
4886 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
4887 for child
in vm_list_xmlroot
.iter():
4888 if 'CatalogItem' in child
.tag
:
4889 cataloghref_list
.append(child
.attrib
.get('href'))
4890 if cataloghref_list
is not None:
4891 for href
in cataloghref_list
:
4893 response
= Http
.get(url
=href
,
4894 headers
=vca
.vcloud_session
.get_vcloud_headers(),
4897 if response
.status_code
!= 200:
4898 self
.logger
.error("REST call {} failed reason : {}"\
4899 "status code : {}".format(href
,
4901 response
.status_code
))
4902 raise vimconn
.vimconnException("get_media_details : Failed to get "\
4903 "catalogitem details")
4904 list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
4905 for child
in list_xmlroot
.iter():
4906 if 'Entity' in child
.tag
:
4907 if 'media' in child
.attrib
.get('href'):
4908 name
= child
.attrib
.get('name')
4909 media_id
= child
.attrib
.get('href').split('/').pop()
4910 return name
,media_id
4912 self
.logger
.debug("Media name and id not found")
4914 except Exception as exp
:
4915 self
.logger
.error("get_media_details : exception occurred "\
4916 "getting media details")
4917 raise vimconn
.vimconnException(message
=exp
)