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
181 self
.vcenter_ip
= None
182 self
.vcenter_port
= None
183 self
.vcenter_user
= None
184 self
.vcenter_password
= None
186 if tenant_name
is not None:
187 orgnameandtenant
= tenant_name
.split(":")
188 if len(orgnameandtenant
) == 2:
189 self
.tenant_name
= orgnameandtenant
[1]
190 self
.org_name
= orgnameandtenant
[0]
192 self
.tenant_name
= tenant_name
193 if "orgname" in config
:
194 self
.org_name
= config
['orgname']
197 self
.logger
.setLevel(getattr(logging
, log_level
))
200 self
.admin_user
= config
['admin_username']
201 self
.admin_password
= config
['admin_password']
203 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
206 self
.nsx_manager
= config
['nsx_manager']
207 self
.nsx_user
= config
['nsx_user']
208 self
.nsx_password
= config
['nsx_password']
210 raise vimconn
.vimconnException(message
="Error: nsx manager or nsx user or nsx password is empty in Config")
212 self
.vcenter_ip
= config
.get("vcenter_ip", None)
213 self
.vcenter_port
= config
.get("vcenter_port", None)
214 self
.vcenter_user
= config
.get("vcenter_user", None)
215 self
.vcenter_password
= config
.get("vcenter_password", None)
221 raise vimconn
.vimconnException('url param can not be NoneType')
223 if not self
.url_admin
: # try to use normal url
224 self
.url_admin
= self
.url
226 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
227 self
.tenant_id
, self
.tenant_name
))
228 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
229 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
231 # initialize organization
232 if self
.user
is not None and self
.passwd
is not None and self
.url
:
233 self
.init_organization()
235 def __getitem__(self
, index
):
238 if index
== 'tenant_id':
239 return self
.tenant_id
240 if index
== 'tenant_name':
241 return self
.tenant_name
244 elif index
== 'org_name':
246 elif index
== 'org_uuid':
248 elif index
== 'user':
250 elif index
== 'passwd':
254 elif index
== 'url_admin':
255 return self
.url_admin
256 elif index
== "config":
259 raise KeyError("Invalid key '%s'" % str(index
))
261 def __setitem__(self
, index
, value
):
264 if index
== 'tenant_id':
265 self
.tenant_id
= value
266 if index
== 'tenant_name':
267 self
.tenant_name
= value
270 elif index
== 'org_name':
271 self
.org_name
= value
272 elif index
== 'org_uuid':
273 self
.org_uuid
= value
274 elif index
== 'user':
276 elif index
== 'passwd':
280 elif index
== 'url_admin':
281 self
.url_admin
= value
283 raise KeyError("Invalid key '%s'" % str(index
))
285 def connect_as_admin(self
):
286 """ Method connect as pvdc admin user to vCloud director.
287 There are certain action that can be done only by provider vdc admin user.
288 Organization creation / provider network creation etc.
291 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
294 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
296 vca_admin
= VCA(host
=self
.url
,
297 username
=self
.admin_user
,
298 service_type
=STANDALONE
,
302 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
304 raise vimconn
.vimconnConnectionException(
305 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
306 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
309 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
314 """ Method connect as normal user to vCloud director.
317 The return vca object that letter can be used to connect to vCloud director as admin for VDC
321 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
324 vca
= VCA(host
=self
.url
,
326 service_type
=STANDALONE
,
331 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
333 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
334 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
337 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
340 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
341 "{} as user: {}".format(self
.org_name
, self
.user
))
345 def init_organization(self
):
346 """ Method initialize organization UUID and VDC parameters.
348 At bare minimum client must provide organization name that present in vCloud director and VDC.
350 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
351 The Org - UUID will be initialized at the run time if data center present in vCloud director.
354 The return vca object that letter can be used to connect to vcloud direct as admin
357 if self
.org_uuid
is None:
358 org_dict
= self
.get_org_list()
360 # we set org UUID at the init phase but we can do it only when we have valid credential.
361 if org_dict
[org
] == self
.org_name
:
363 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
366 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
368 # if well good we require for org details
369 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
371 # we have two case if we want to initialize VDC ID or VDC name at run time
372 # tenant_name provided but no tenant id
373 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
374 vdcs_dict
= org_details_dict
['vdcs']
375 for vdc
in vdcs_dict
:
376 if vdcs_dict
[vdc
] == self
.tenant_name
:
378 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
382 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
383 # case two we have tenant_id but we don't have tenant name so we find and set it.
384 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
385 vdcs_dict
= org_details_dict
['vdcs']
386 for vdc
in vdcs_dict
:
387 if vdc
== self
.tenant_id
:
388 self
.tenant_name
= vdcs_dict
[vdc
]
389 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
393 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
394 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
396 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
397 self
.logger
.debug(traceback
.format_exc())
400 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
401 """ Method adds a new tenant to VIM with this name.
402 This action requires access to create VDC action in vCloud director.
405 tenant_name is tenant_name to be created.
406 tenant_description not used for this call
409 returns the tenant identifier in UUID format.
410 If action is failed method will throw vimconn.vimconnException method
412 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
413 if vdc_task
is not None:
414 vdc_uuid
, value
= vdc_task
.popitem()
415 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
418 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
420 def delete_tenant(self
, tenant_id
=None):
421 """Delete a tenant from VIM"""
422 'Returns the tenant identifier'
423 raise vimconn
.vimconnNotImplemented("Should have implemented this")
425 def get_tenant_list(self
, filter_dict
={}):
426 """Obtain tenants of VIM
427 filter_dict can contain the following keys:
428 name: filter by tenant name
429 id: filter by tenant uuid/id
431 Returns the tenant list of dictionaries:
432 [{'name':'<name>, 'id':'<id>, ...}, ...]
435 org_dict
= self
.get_org(self
.org_uuid
)
436 vdcs_dict
= org_dict
['vdcs']
441 entry
= {'name': vdcs_dict
[k
], 'id': k
}
442 # if caller didn't specify dictionary we return all tenants.
443 if filter_dict
is not None and filter_dict
:
444 filtered_entry
= entry
.copy()
445 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
446 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
447 if filter_dict
== entry
:
448 vdclist
.append(filtered_entry
)
450 vdclist
.append(entry
)
452 self
.logger
.debug("Error in get_tenant_list()")
453 self
.logger
.debug(traceback
.format_exc())
454 raise vimconn
.vimconnException("Incorrect state. {}")
458 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
459 """Adds a tenant network to VIM
461 net_type can be 'bridge','data'.'ptp'.
462 ip_profile is a dict containing the IP parameters of the network
464 Returns the network identifier"""
466 self
.logger
.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
467 .format(net_name
, net_type
, ip_profile
, shared
))
473 network_uuid
= self
.create_network(network_name
=net_name
, net_type
=net_type
,
474 ip_profile
=ip_profile
, isshared
=isshared
)
475 if network_uuid
is not None:
478 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
480 def get_vcd_network_list(self
):
481 """ Method available organization for a logged in tenant
484 The return vca object that letter can be used to connect to vcloud direct as admin
487 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
490 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
492 if not self
.tenant_name
:
493 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
495 vdc
= vca
.get_vdc(self
.tenant_name
)
497 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
499 vdc_uuid
= vdc
.get_id().split(":")[3]
500 networks
= vca
.get_networks(vdc
.get_name())
503 for network
in networks
:
505 netid
= network
.get_id().split(":")
509 filter_dict
["name"] = network
.get_name()
510 filter_dict
["id"] = netid
[3]
511 filter_dict
["shared"] = network
.get_IsShared()
512 filter_dict
["tenant_id"] = vdc_uuid
513 if network
.get_status() == 1:
514 filter_dict
["admin_state_up"] = True
516 filter_dict
["admin_state_up"] = False
517 filter_dict
["status"] = "ACTIVE"
518 filter_dict
["type"] = "bridge"
519 network_list
.append(filter_dict
)
520 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
522 self
.logger
.debug("Error in get_vcd_network_list")
523 self
.logger
.debug(traceback
.format_exc())
526 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
529 def get_network_list(self
, filter_dict
={}):
530 """Obtain tenant networks of VIM
532 name: network name OR/AND
533 id: network uuid OR/AND
534 shared: boolean OR/AND
535 tenant_id: tenant OR/AND
536 admin_state_up: boolean
539 [{key : value , key : value}]
541 Returns the network list of dictionaries:
542 [{<the fields at Filter_dict plus some VIM specific>}, ...]
546 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
549 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
551 if not self
.tenant_name
:
552 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
554 vdc
= vca
.get_vdc(self
.tenant_name
)
556 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
559 vdcid
= vdc
.get_id().split(":")[3]
560 networks
= vca
.get_networks(vdc
.get_name())
563 for network
in networks
:
565 net_uuid
= network
.get_id().split(":")
566 if len(net_uuid
) != 4:
569 net_uuid
= net_uuid
[3]
571 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
574 filter_entry
["name"] = network
.get_name()
575 filter_entry
["id"] = net_uuid
576 filter_entry
["shared"] = network
.get_IsShared()
577 filter_entry
["tenant_id"] = vdcid
578 if network
.get_status() == 1:
579 filter_entry
["admin_state_up"] = True
581 filter_entry
["admin_state_up"] = False
582 filter_entry
["status"] = "ACTIVE"
583 filter_entry
["type"] = "bridge"
584 filtered_entry
= filter_entry
.copy()
586 if filter_dict
is not None and filter_dict
:
587 # we remove all the key : value we don't care and match only
589 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
590 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
591 if filter_dict
== filter_entry
:
592 network_list
.append(filtered_entry
)
594 network_list
.append(filtered_entry
)
596 self
.logger
.debug("Error in get_vcd_network_list")
597 self
.logger
.debug(traceback
.format_exc())
599 self
.logger
.debug("Returning {}".format(network_list
))
602 def get_network(self
, net_id
):
603 """Method obtains network details of net_id VIM network
604 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
608 raise vimconn
.vimconnConnectionException("self.connect() is failed")
611 vdc
= vca
.get_vdc(self
.tenant_name
)
612 vdc_id
= vdc
.get_id().split(":")[3]
614 networks
= vca
.get_networks(vdc
.get_name())
617 for network
in networks
:
618 vdc_network_id
= network
.get_id().split(":")
619 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
620 filter_dict
["name"] = network
.get_name()
621 filter_dict
["id"] = vdc_network_id
[3]
622 filter_dict
["shared"] = network
.get_IsShared()
623 filter_dict
["tenant_id"] = vdc_id
624 if network
.get_status() == 1:
625 filter_dict
["admin_state_up"] = True
627 filter_dict
["admin_state_up"] = False
628 filter_dict
["status"] = "ACTIVE"
629 filter_dict
["type"] = "bridge"
630 self
.logger
.debug("Returning {}".format(filter_dict
))
633 self
.logger
.debug("Error in get_network")
634 self
.logger
.debug(traceback
.format_exc())
638 def delete_network(self
, net_id
):
640 Method Deletes a tenant network from VIM, provide the network id.
642 Returns the network identifier or raise an exception
647 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
649 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
650 if vcd_network
is not None and vcd_network
:
651 if self
.delete_network_action(network_uuid
=net_id
):
654 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
656 def refresh_nets_status(self
, net_list
):
657 """Get the status of the networks
658 Params: the list of network identifiers
659 Returns a dictionary with:
660 net_id: #VIM id of this network
661 status: #Mandatory. Text with one of:
662 # DELETED (not found at vim)
663 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
664 # OTHER (Vim reported other status not understood)
665 # ERROR (VIM indicates an ERROR status)
666 # ACTIVE, INACTIVE, DOWN (admin down),
667 # BUILD (on building process)
669 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
670 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
676 raise vimconn
.vimconnConnectionException("self.connect() is failed")
682 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
683 if vcd_network
is not None and vcd_network
:
684 if vcd_network
['status'] == '1':
690 errormsg
= 'Network not found.'
692 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
693 'vim_info': yaml
.safe_dump(vcd_network
)}
695 self
.logger
.debug("Error in refresh_nets_status")
696 self
.logger
.debug(traceback
.format_exc())
700 def get_flavor(self
, flavor_id
):
701 """Obtain flavor details from the VIM
702 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
704 if flavor_id
not in vimconnector
.flavorlist
:
705 raise vimconn
.vimconnNotFoundException("Flavor not found.")
706 return vimconnector
.flavorlist
[flavor_id
]
708 def new_flavor(self
, flavor_data
):
709 """Adds a tenant flavor to VIM
710 flavor_data contains a dictionary with information, keys:
712 ram: memory (cloud type) in MBytes
713 vpcus: cpus (cloud type)
714 extended: EPA parameters
715 - numas: #items requested in same NUMA
716 memory: number of 1G huge pages memory
717 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
718 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
719 - name: interface name
720 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
721 bandwidth: X Gbps; requested guarantee bandwidth
722 vpci: requested virtual PCI address
726 Returns the flavor identifier"""
728 # generate a new uuid put to internal dict and return it.
729 self
.logger
.debug("Creating new flavor - flavor_data: {}".format(flavor_data
))
730 new_flavor
=flavor_data
731 ram
= flavor_data
.get(FLAVOR_RAM_KEY
, 1024)
732 cpu
= flavor_data
.get(FLAVOR_VCPUS_KEY
, 1)
733 disk
= flavor_data
.get(FLAVOR_DISK_KEY
, 1)
735 extended_flv
= flavor_data
.get("extended")
737 numas
=extended_flv
.get("numas")
740 #overwrite ram and vcpus
741 ram
= numa
['memory']*1024
742 if 'paired-threads' in numa
:
743 cpu
= numa
['paired-threads']*2
744 elif 'cores' in numa
:
746 elif 'threads' in numa
:
747 cpu
= numa
['threads']
749 new_flavor
[FLAVOR_RAM_KEY
] = ram
750 new_flavor
[FLAVOR_VCPUS_KEY
] = cpu
751 new_flavor
[FLAVOR_DISK_KEY
] = disk
752 # generate a new uuid put to internal dict and return it.
753 flavor_id
= uuid
.uuid4()
754 vimconnector
.flavorlist
[str(flavor_id
)] = new_flavor
755 self
.logger
.debug("Created flavor - {} : {}".format(flavor_id
, new_flavor
))
757 return str(flavor_id
)
759 def delete_flavor(self
, flavor_id
):
760 """Deletes a tenant flavor from VIM identify by its id
762 Returns the used id or raise an exception
764 if flavor_id
not in vimconnector
.flavorlist
:
765 raise vimconn
.vimconnNotFoundException("Flavor not found.")
767 vimconnector
.flavorlist
.pop(flavor_id
, None)
770 def new_image(self
, image_dict
):
772 Adds a tenant image to VIM
774 200, image-id if the image is created
775 <0, message if there is an error
778 return self
.get_image_id_from_path(image_dict
['location'])
780 def delete_image(self
, image_id
):
787 raise vimconn
.vimconnNotImplemented("Should have implemented this")
789 def catalog_exists(self
, catalog_name
, catalogs
):
796 for catalog
in catalogs
:
797 if catalog
.name
== catalog_name
:
801 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
802 """ Create new catalog entry in vCloud director.
805 vca: vCloud director.
806 catalog_name catalog that client wish to create. Note no validation done for a name.
807 Client must make sure that provide valid string representation.
809 Return (bool) True if catalog created.
813 task
= vca
.create_catalog(catalog_name
, catalog_name
)
814 result
= vca
.block_until_completed(task
)
817 catalogs
= vca
.get_catalogs()
820 return self
.catalog_exists(catalog_name
, catalogs
)
822 # noinspection PyIncorrectDocstring
823 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
824 description
='', progress
=False, chunk_bytes
=128 * 1024):
826 Uploads a OVF file to a vCloud catalog
833 :param catalog_name: (str): The name of the catalog to upload the media.
834 :param media_file_name: (str): The name of the local media file to upload.
835 :return: (bool) True if the media file was successfully uploaded, false otherwise.
837 os
.path
.isfile(media_file_name
)
838 statinfo
= os
.stat(media_file_name
)
840 # find a catalog entry where we upload OVF.
841 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
843 # if VCD can parse OVF we upload VMDK file
845 for catalog
in vca
.get_catalogs():
846 if catalog_name
!= catalog
.name
:
848 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
849 link
.get_rel() == 'add', catalog
.get_Link())
850 assert len(link
) == 1
852 <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>
853 """ % (escape(catalog_name
), escape(description
))
854 headers
= vca
.vcloud_session
.get_vcloud_headers()
855 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
856 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
857 if response
.status_code
== requests
.codes
.created
:
858 catalogItem
= XmlElementTree
.fromstring(response
.content
)
859 entity
= [child
for child
in catalogItem
if
860 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
861 href
= entity
.get('href')
863 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
864 verify
=vca
.verify
, logger
=self
.logger
)
866 if response
.status_code
== requests
.codes
.ok
:
867 media
= mediaType
.parseString(response
.content
, True)
868 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
869 media
.get_Files().get_File()[0].get_Link())[0]
870 headers
= vca
.vcloud_session
.get_vcloud_headers()
871 headers
['Content-Type'] = 'Content-Type text/xml'
872 response
= Http
.put(link
.get_href(),
873 data
=open(media_file_name
, 'rb'),
875 verify
=vca
.verify
, logger
=self
.logger
)
876 if response
.status_code
!= requests
.codes
.ok
:
878 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
882 # TODO fix this with aync block
885 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
887 # uploading VMDK file
888 # check status of OVF upload and upload remaining files.
889 response
= Http
.get(template
,
890 headers
=vca
.vcloud_session
.get_vcloud_headers(),
894 if response
.status_code
== requests
.codes
.ok
:
895 media
= mediaType
.parseString(response
.content
, True)
896 number_of_files
= len(media
.get_Files().get_File())
897 for index
in xrange(0, number_of_files
):
898 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
899 media
.get_Files().get_File()[index
].get_Link())
900 for link
in links_list
:
901 # we skip ovf since it already uploaded.
902 if 'ovf' in link
.get_href():
904 # The OVF file and VMDK must be in a same directory
905 head
, tail
= os
.path
.split(media_file_name
)
906 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
907 if not os
.path
.isfile(file_vmdk
):
909 statinfo
= os
.stat(file_vmdk
)
910 if statinfo
.st_size
== 0:
912 hrefvmdk
= link
.get_href()
915 print("Uploading file: {}".format(file_vmdk
))
917 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
919 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
921 bytes_transferred
= 0
922 f
= open(file_vmdk
, 'rb')
923 while bytes_transferred
< statinfo
.st_size
:
924 my_bytes
= f
.read(chunk_bytes
)
925 if len(my_bytes
) <= chunk_bytes
:
926 headers
= vca
.vcloud_session
.get_vcloud_headers()
927 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
928 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
929 headers
['Content-Length'] = str(len(my_bytes
))
930 response
= Http
.put(hrefvmdk
,
936 if response
.status_code
== requests
.codes
.ok
:
937 bytes_transferred
+= len(my_bytes
)
939 progress_bar
.update(bytes_transferred
)
942 'file upload failed with error: [%s] %s' % (response
.status_code
,
949 progress_bar
.finish()
953 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
954 format(catalog_name
, media_file_name
))
956 except Exception as exp
:
957 self
.logger
.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
958 .format(catalog_name
,media_file_name
, exp
))
959 raise vimconn
.vimconnException(
960 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
961 .format(catalog_name
,media_file_name
, exp
))
963 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
966 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
967 """Upload media file"""
968 # TODO add named parameters for readability
970 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
971 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
973 def validate_uuid4(self
, uuid_string
=None):
974 """ Method validate correct format of UUID.
976 Return: true if string represent valid uuid
979 val
= uuid
.UUID(uuid_string
, version
=4)
984 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
985 """ Method check catalog and return catalog ID in UUID format.
988 catalog_name: catalog name as string
989 catalogs: list of catalogs.
991 Return: catalogs uuid
994 for catalog
in catalogs
:
995 if catalog
.name
== catalog_name
:
996 catalog_id
= catalog
.get_id().split(":")
1000 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
1001 """ Method check catalog and return catalog name lookup done by catalog UUID.
1004 catalog_name: catalog name as string
1005 catalogs: list of catalogs.
1007 Return: catalogs name or None
1010 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1013 for catalog
in catalogs
:
1014 catalog_id
= catalog
.get_id().split(":")[3]
1015 if catalog_id
== catalog_uuid
:
1019 def get_image_id_from_path(self
, path
=None, progress
=False):
1020 """ Method upload OVF image to vCloud director.
1022 Each OVF image represented as single catalog entry in vcloud director.
1023 The method check for existing catalog entry. The check done by file name without file extension.
1025 if given catalog name already present method will respond with existing catalog uuid otherwise
1026 it will create new catalog entry and upload OVF file to newly created catalog.
1028 If method can't create catalog entry or upload a file it will throw exception.
1030 Method accept boolean flag progress that will output progress bar. It useful method
1031 for standalone upload use case. In case to test large file upload.
1034 path: - valid path to OVF file.
1035 progress - boolean progress bar show progress bar.
1037 Return: if image uploaded correct method will provide image catalog UUID.
1039 vca
= self
.connect()
1041 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1044 raise vimconn
.vimconnException("Image path can't be None.")
1046 if not os
.path
.isfile(path
):
1047 raise vimconn
.vimconnException("Can't read file. File not found.")
1049 if not os
.access(path
, os
.R_OK
):
1050 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
1052 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1054 dirpath
, filename
= os
.path
.split(path
)
1055 flname
, file_extension
= os
.path
.splitext(path
)
1056 if file_extension
!= '.ovf':
1057 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1058 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1060 catalog_name
= os
.path
.splitext(filename
)[0]
1061 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1062 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1063 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1066 catalogs
= vca
.get_catalogs()
1067 except Exception as exp
:
1068 self
.logger
.debug("Failed get catalogs() with Exception {} ".format(exp
))
1069 raise vimconn
.vimconnException("Failed get catalogs() with Exception {} ".format(exp
))
1071 if len(catalogs
) == 0:
1072 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1073 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1075 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1076 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1077 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1079 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1080 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1082 for catalog
in catalogs
:
1083 # search for existing catalog if we find same name we return ID
1084 # TODO optimize this
1085 if catalog
.name
== catalog_md5_name
:
1086 self
.logger
.debug("Found existing catalog entry for {} "
1087 "catalog id {}".format(catalog_name
,
1088 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1089 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1091 # if we didn't find existing catalog we create a new one and upload image.
1092 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1093 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1095 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1097 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1098 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1100 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1102 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1104 def get_image_list(self
, filter_dict
={}):
1105 '''Obtain tenant images from VIM
1109 checksum: image checksum
1110 location: image path
1111 Returns the image list of dictionaries:
1112 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1115 vca
= self
.connect()
1117 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1120 catalogs
= vca
.get_catalogs()
1121 if len(catalogs
) == 0:
1124 for catalog
in catalogs
:
1125 catalog_uuid
= catalog
.get_id().split(":")[3]
1128 if filter_dict
.get("name") and filter_dict
["name"] != name
:
1130 if filter_dict
.get("id") and filter_dict
["id"] != catalog_uuid
:
1132 filtered_dict
["name"] = name
1133 filtered_dict
["id"] = catalog_uuid
1134 image_list
.append(filtered_dict
)
1136 self
.logger
.debug("List of already created catalog items: {}".format(image_list
))
1138 except Exception as exp
:
1139 raise vimconn
.vimconnException("Exception occured while retriving catalog items {}".format(exp
))
1141 def get_vappid(self
, vdc
=None, vapp_name
=None):
1142 """ Method takes vdc object and vApp name and returns vapp uuid or None
1145 vdc: The VDC object.
1146 vapp_name: is application vappp name identifier
1149 The return vApp name otherwise None
1151 if vdc
is None or vapp_name
is None:
1153 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1155 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1156 vdc
.ResourceEntities
.ResourceEntity
)
1158 return refs
[0].href
.split("vapp")[1][1:]
1159 except Exception as e
:
1160 self
.logger
.exception(e
)
1164 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1165 """ Method Method returns True or False if vapp deployed in vCloud director
1168 vca: Connector to VCA
1169 vdc: The VDC object.
1170 vappid: vappid is application identifier
1173 The return True if vApp deployed
1178 refs
= filter(lambda ref
:
1179 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1180 vdc
.ResourceEntities
.ResourceEntity
)
1182 vappid
= ref
.href
.split("vapp")[1][1:]
1183 # find vapp with respected vapp uuid
1184 if vappid
== vapp_uuid
:
1186 except Exception as e
:
1187 self
.logger
.exception(e
)
1191 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1192 """Method returns vApp name from vCD and lookup done by vapp_id.
1195 vca: Connector to VCA
1196 vdc: The VDC object.
1197 vapp_uuid: vappid is application identifier
1200 The return vApp name otherwise None
1204 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1205 vdc
.ResourceEntities
.ResourceEntity
)
1207 # we care only about UUID the rest doesn't matter
1208 vappid
= ref
.href
.split("vapp")[1][1:]
1209 if vappid
== vapp_uuid
:
1210 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1212 tree
= XmlElementTree
.fromstring(response
.content
)
1213 return tree
.attrib
['name']
1214 except Exception as e
:
1215 self
.logger
.exception(e
)
1219 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1220 cloud_config
=None, disk_list
=None):
1221 """Adds a VM instance to VIM
1223 start: indicates if VM must start or boot in pause mode. Ignored
1224 image_id,flavor_id: image and flavor uuid
1225 net_list: list of interfaces, each one is a dictionary with:
1227 net_id: network uuid to connect
1228 vpci: virtual vcpi to assign
1229 model: interface model, virtio, e2000, ...
1231 use: 'data', 'bridge', 'mgmt'
1232 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1233 vim_id: filled/added by this function
1234 cloud_config: can be a text script to be passed directly to cloud-init,
1235 or an object to inject users and ssh keys with format:
1236 key-pairs: [] list of keys to install to the default user
1237 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1238 #TODO ip, security groups
1239 Returns >=0, the instance identifier
1243 self
.logger
.info("Creating new instance for entry {}".format(name
))
1244 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".format(
1245 description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1246 vca
= self
.connect()
1248 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1250 #new vm name = vmname + tenant_id + uuid
1251 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1252 vmname_andid
= ''.join(new_vm_name
)
1254 # if vm already deployed we return existing uuid
1255 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1256 # if vapp_uuid is not None:
1259 # we check for presence of VDC, Catalog entry and Flavor.
1260 vdc
= vca
.get_vdc(self
.tenant_name
)
1262 raise vimconn
.vimconnNotFoundException(
1263 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1264 catalogs
= vca
.get_catalogs()
1265 if catalogs
is None:
1266 raise vimconn
.vimconnNotFoundException(
1267 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1269 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1270 if catalog_hash_name
:
1271 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1273 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1274 "(Failed retrieve catalog information {})".format(name
, image_id
))
1277 # Set vCPU and Memory based on flavor.
1282 pci_devices_info
= []
1283 if flavor_id
is not None:
1284 if flavor_id
not in vimconnector
.flavorlist
:
1285 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1286 "Failed retrieve flavor information "
1287 "flavor id {}".format(name
, flavor_id
))
1290 flavor
= vimconnector
.flavorlist
[flavor_id
]
1291 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1292 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1293 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1294 extended
= flavor
.get("extended", None)
1296 numas
=extended
.get("numas", None)
1299 for interface
in numa
.get("interfaces",() ):
1300 if interface
["dedicated"].strip()=="yes":
1301 pci_devices_info
.append(interface
)
1302 except Exception as exp
:
1303 raise vimconn
.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id
, exp
))
1305 # image upload creates template name as catalog name space Template.
1306 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1311 # client must provide at least one entry in net_list if not we report error
1312 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1313 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1315 primary_netname
= None
1316 network_mode
= 'bridged'
1317 if net_list
is not None and len(net_list
) > 0:
1318 for net
in net_list
:
1319 if 'use' in net
and net
['use'] == 'mgmt':
1321 if primary_net
is None:
1322 primary_net
= net_list
[0]
1325 primary_net_id
= primary_net
['net_id']
1326 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1327 if 'name' in network_dict
:
1328 primary_netname
= network_dict
['name']
1331 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1333 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1335 # use: 'data', 'bridge', 'mgmt'
1336 # create vApp. Set vcpu and ram based on flavor id.
1338 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1339 self
.get_catalogbyid(image_id
, catalogs
),
1340 network_name
=None, # None while creating vapp
1341 network_mode
=network_mode
,
1342 vm_name
=vmname_andid
,
1343 vm_cpus
=vm_cpus
, # can be None if flavor is None
1344 vm_memory
=vm_memory
) # can be None if flavor is None
1346 if vapptask
is None or vapptask
is False:
1347 raise vimconn
.vimconnUnexpectedResponse(
1348 "new_vminstance(): failed to create vApp {}".format(vmname_andid
))
1349 if type(vapptask
) is VappTask
:
1350 vca
.block_until_completed(vapptask
)
1352 except Exception as exp
:
1353 raise vimconn
.vimconnUnexpectedResponse(
1354 "new_vminstance(): failed to create vApp {} with Exception:{}".format(vmname_andid
, exp
))
1356 # we should have now vapp in undeployed state.
1358 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1359 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1360 except Exception as exp
:
1361 raise vimconn
.vimconnUnexpectedResponse(
1362 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1363 .format(vmname_andid
, exp
))
1366 raise vimconn
.vimconnUnexpectedResponse(
1367 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
1370 #Add PCI passthrough configrations
1371 PCI_devices_status
= False
1374 if len(pci_devices_info
) > 0:
1375 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1377 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1380 if PCI_devices_status
:
1381 self
.logger
.info("Added PCI devives {} to VM {}".format(
1386 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1392 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1393 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1395 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1398 # Assigning numa affinity setting
1400 if 'paired-threads-id' in numa
:
1401 paired_threads_id
= numa
['paired-threads-id']
1402 self
.set_numa_affinity(vapp_uuid
, paired_threads_id
)
1404 # add NICs & connect to networks in netlist
1406 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1408 primary_nic_index
= 0
1409 for net
in net_list
:
1410 # openmano uses network id in UUID format.
1411 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1412 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1413 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1415 if 'net_id' not in net
:
1418 interface_net_id
= net
['net_id']
1419 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1420 interface_network_mode
= net
['use']
1422 if interface_network_mode
== 'mgmt':
1423 primary_nic_index
= nicIndex
1425 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1426 - DHCP (The IP address is obtained from a DHCP service.)
1427 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1428 - NONE (No IP addressing mode specified.)"""
1430 if primary_netname
is not None:
1431 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1433 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1434 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1435 if type(task
) is GenericTask
:
1436 vca
.block_until_completed(task
)
1437 # connect network to VM - with all DHCP by default
1439 type_list
= ['PF','VF','VFnotShared']
1440 if 'type' in net
and net
['type'] not in type_list
:
1441 # fetching nic type from vnf
1443 nic_type
= net
['model']
1444 self
.logger
.info("new_vminstance(): adding network adapter "\
1445 "to a network {}".format(nets
[0].name
))
1446 self
.add_network_adapter_to_vms(vapp
, nets
[0].name
,
1451 self
.logger
.info("new_vminstance(): adding network adapter "\
1452 "to a network {}".format(nets
[0].name
))
1453 self
.add_network_adapter_to_vms(vapp
, nets
[0].name
,
1458 # deploy and power on vm
1459 self
.logger
.debug("new_vminstance(): Deploying vApp {} ".format(name
))
1460 deploytask
= vapp
.deploy(powerOn
=False)
1461 if type(deploytask
) is GenericTask
:
1462 vca
.block_until_completed(deploytask
)
1464 # If VM has PCI devices reserve memory for VM
1465 if PCI_devices_status
and vm_obj
and vcenter_conect
:
1466 memReserve
= vm_obj
.config
.hardware
.memoryMB
1467 spec
= vim
.vm
.ConfigSpec()
1468 spec
.memoryAllocation
= vim
.ResourceAllocationInfo(reservation
=memReserve
)
1469 task
= vm_obj
.ReconfigVM_Task(spec
=spec
)
1471 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
1472 self
.logger
.info("Reserved memmoery {} MB for "\
1473 "VM VM status: {}".format(str(memReserve
),result
))
1475 self
.logger
.info("Fail to reserved memmoery {} to VM {}".format(
1476 str(memReserve
),str(vm_obj
)))
1478 self
.logger
.debug("new_vminstance(): power on vApp {} ".format(name
))
1479 poweron_task
= vapp
.poweron()
1480 if type(poweron_task
) is GenericTask
:
1481 vca
.block_until_completed(poweron_task
)
1483 except Exception as exp
:
1484 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1485 self
.logger
.debug("new_vminstance(): Failed create new vm instance {}".format(name
, exp
))
1486 raise vimconn
.vimconnException("new_vminstance(): Failed create new vm instance {}".format(name
, exp
))
1488 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1491 while wait_time
<= MAX_WAIT_TIME
:
1493 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1494 except Exception as exp
:
1495 raise vimconn
.vimconnUnexpectedResponse(
1496 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1497 .format(vmname_andid
, exp
))
1499 if vapp
and vapp
.me
.deployed
:
1500 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1503 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1504 time
.sleep(INTERVAL_TIME
)
1506 wait_time
+=INTERVAL_TIME
1508 if vapp_uuid
is not None:
1511 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1515 ## based on current discussion
1519 # created: '2016-09-08T11:51:58'
1520 # description: simple-instance.linux1.1
1521 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1522 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1523 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1528 def get_vminstance(self
, vim_vm_uuid
=None):
1529 """Returns the VM instance information from VIM"""
1531 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1532 vca
= self
.connect()
1534 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1536 vdc
= vca
.get_vdc(self
.tenant_name
)
1538 raise vimconn
.vimconnConnectionException(
1539 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1541 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1542 if not vm_info_dict
:
1543 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1544 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1546 status_key
= vm_info_dict
['status']
1549 vm_dict
= {'created': vm_info_dict
['created'],
1550 'description': vm_info_dict
['name'],
1551 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1552 'hostId': vm_info_dict
['vmuuid'],
1554 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1556 if 'interfaces' in vm_info_dict
:
1557 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1559 vm_dict
['interfaces'] = []
1561 vm_dict
= {'created': '',
1563 'status': vcdStatusCode2manoFormat
[int(-1)],
1564 'hostId': vm_info_dict
['vmuuid'],
1565 'error_msg': "Inconsistency state",
1566 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1570 def delete_vminstance(self
, vm__vim_uuid
):
1571 """Method poweroff and remove VM instance from vcloud director network.
1574 vm__vim_uuid: VM UUID
1577 Returns the instance identifier
1580 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1581 vca
= self
.connect()
1583 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1585 vdc
= vca
.get_vdc(self
.tenant_name
)
1587 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1589 raise vimconn
.vimconnException(
1590 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1593 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1594 if vapp_name
is None:
1595 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1596 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1598 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1600 # Delete vApp and wait for status change if task executed and vApp is None.
1601 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1604 if vapp
.me
.deployed
:
1605 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1609 while wait_time
<= MAX_WAIT_TIME
:
1610 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1612 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1613 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1615 power_off_task
= vapp
.poweroff()
1616 if type(power_off_task
) is GenericTask
:
1617 result
= vca
.block_until_completed(power_off_task
)
1622 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1623 time
.sleep(INTERVAL_TIME
)
1625 wait_time
+=INTERVAL_TIME
1627 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1629 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1632 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1635 while wait_time
<= MAX_WAIT_TIME
:
1636 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1638 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1639 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1640 undeploy_task
= vapp
.undeploy(action
='powerOff')
1642 if type(undeploy_task
) is GenericTask
:
1643 result
= vca
.block_until_completed(undeploy_task
)
1648 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1649 time
.sleep(INTERVAL_TIME
)
1651 wait_time
+=INTERVAL_TIME
1654 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1657 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1658 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1660 if vapp
is not None:
1664 while wait_time
<= MAX_WAIT_TIME
:
1665 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1667 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1668 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1670 delete_task
= vapp
.delete()
1672 if type(delete_task
) is GenericTask
:
1673 vca
.block_until_completed(delete_task
)
1674 result
= vca
.block_until_completed(delete_task
)
1678 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1679 time
.sleep(INTERVAL_TIME
)
1681 wait_time
+=INTERVAL_TIME
1684 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1687 self
.logger
.debug(traceback
.format_exc())
1688 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1690 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1691 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1694 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1696 def refresh_vms_status(self
, vm_list
):
1697 """Get the status of the virtual machines and their interfaces/ports
1698 Params: the list of VM identifiers
1699 Returns a dictionary with:
1700 vm_id: #VIM id of this Virtual Machine
1701 status: #Mandatory. Text with one of:
1702 # DELETED (not found at vim)
1703 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1704 # OTHER (Vim reported other status not understood)
1705 # ERROR (VIM indicates an ERROR status)
1706 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1707 # CREATING (on building process), ERROR
1708 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1710 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1711 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1713 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1714 mac_address: #Text format XX:XX:XX:XX:XX:XX
1715 vim_net_id: #network id where this interface is connected
1716 vim_interface_id: #interface/port VIM id
1717 ip_address: #null, or text with IPv4, IPv6 address
1720 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1722 vca
= self
.connect()
1724 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1726 vdc
= vca
.get_vdc(self
.tenant_name
)
1728 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1732 for vmuuid
in vm_list
:
1733 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1734 if vmname
is not None:
1737 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1738 vm_info
= the_vapp
.get_vms_details()
1739 vm_status
= vm_info
[0]['status']
1740 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
1741 vm_info
[0].update(vm_pci_details
)
1743 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1744 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1745 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
1748 vm_app_networks
= the_vapp
.get_vms_network_info()
1749 for vapp_network
in vm_app_networks
:
1750 for vm_network
in vapp_network
:
1751 if vm_network
['name'] == vmname
:
1752 #Assign IP Address based on MAC Address in NSX DHCP lease info
1753 if vm_network
['ip'] is None:
1754 if not nsx_edge_list
:
1755 nsx_edge_list
= self
.get_edge_details()
1756 if nsx_edge_list
is None:
1757 raise vimconn
.vimconnException("refresh_vms_status:"\
1758 "Failed to get edge details from NSX Manager")
1759 if vm_network
['mac'] is not None:
1760 vm_network
['ip'] = self
.get_ipaddr_from_NSXedge(nsx_edge_list
, vm_network
['mac'])
1762 vm_net_id
= self
.get_network_id_by_name(vm_network
['network_name'])
1763 interface
= {"mac_address": vm_network
['mac'],
1764 "vim_net_id": vm_net_id
,
1765 "vim_interface_id": vm_net_id
,
1766 'ip_address': vm_network
['ip']}
1767 # interface['vim_info'] = yaml.safe_dump(vm_network)
1768 vm_dict
["interfaces"].append(interface
)
1769 # add a vm to vm dict
1770 vms_dict
.setdefault(vmuuid
, vm_dict
)
1771 except Exception as exp
:
1772 self
.logger
.debug("Error in response {}".format(exp
))
1773 self
.logger
.debug(traceback
.format_exc())
1778 def get_edge_details(self
):
1779 """Get the NSX edge list from NSX Manager
1780 Returns list of NSX edges
1783 rheaders
= {'Content-Type': 'application/xml'}
1784 nsx_api_url
= '/api/4.0/edges'
1786 self
.logger
.debug("Get edge details from NSX Manager {} {}".format(self
.nsx_manager
, nsx_api_url
))
1789 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
1790 auth
= (self
.nsx_user
, self
.nsx_password
),
1791 verify
= False, headers
= rheaders
)
1792 if resp
.status_code
== requests
.codes
.ok
:
1793 paged_Edge_List
= XmlElementTree
.fromstring(resp
.text
)
1794 for edge_pages
in paged_Edge_List
:
1795 if edge_pages
.tag
== 'edgePage':
1796 for edge_summary
in edge_pages
:
1797 if edge_summary
.tag
== 'pagingInfo':
1798 for element
in edge_summary
:
1799 if element
.tag
== 'totalCount' and element
.text
== '0':
1800 raise vimconn
.vimconnException("get_edge_details: No NSX edges details found: {}"
1801 .format(self
.nsx_manager
))
1803 if edge_summary
.tag
== 'edgeSummary':
1804 for element
in edge_summary
:
1805 if element
.tag
== 'id':
1806 edge_list
.append(element
.text
)
1808 raise vimconn
.vimconnException("get_edge_details: No NSX edge details found: {}"
1809 .format(self
.nsx_manager
))
1812 raise vimconn
.vimconnException("get_edge_details: "\
1813 "No NSX edge details found: {}"
1814 .format(self
.nsx_manager
))
1816 self
.logger
.debug("get_edge_details: Found NSX edges {}".format(edge_list
))
1819 self
.logger
.debug("get_edge_details: "
1820 "Failed to get NSX edge details from NSX Manager: {}"
1821 .format(resp
.content
))
1824 except Exception as exp
:
1825 self
.logger
.debug("get_edge_details: "\
1826 "Failed to get NSX edge details from NSX Manager: {}"
1828 raise vimconn
.vimconnException("get_edge_details: "\
1829 "Failed to get NSX edge details from NSX Manager: {}"
1833 def get_ipaddr_from_NSXedge(self
, nsx_edges
, mac_address
):
1834 """Get IP address details from NSX edges, using the MAC address
1835 PARAMS: nsx_edges : List of NSX edges
1836 mac_address : Find IP address corresponding to this MAC address
1837 Returns: IP address corrresponding to the provided MAC address
1841 rheaders
= {'Content-Type': 'application/xml'}
1843 self
.logger
.debug("get_ipaddr_from_NSXedge: Finding IP addr from NSX edge")
1846 for edge
in nsx_edges
:
1847 nsx_api_url
= '/api/4.0/edges/'+ edge
+'/dhcp/leaseInfo'
1849 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
1850 auth
= (self
.nsx_user
, self
.nsx_password
),
1851 verify
= False, headers
= rheaders
)
1853 if resp
.status_code
== requests
.codes
.ok
:
1854 dhcp_leases
= XmlElementTree
.fromstring(resp
.text
)
1855 for child
in dhcp_leases
:
1856 if child
.tag
== 'dhcpLeaseInfo':
1857 dhcpLeaseInfo
= child
1858 for leaseInfo
in dhcpLeaseInfo
:
1859 for elem
in leaseInfo
:
1860 if (elem
.tag
)=='macAddress':
1861 edge_mac_addr
= elem
.text
1862 if (elem
.tag
)=='ipAddress':
1864 if edge_mac_addr
is not None:
1865 if edge_mac_addr
== mac_address
:
1866 self
.logger
.debug("Found ip addr {} for mac {} at NSX edge {}"
1867 .format(ip_addr
, mac_address
,edge
))
1870 self
.logger
.debug("get_ipaddr_from_NSXedge: "\
1871 "Error occurred while getting DHCP lease info from NSX Manager: {}"
1872 .format(resp
.content
))
1874 self
.logger
.debug("get_ipaddr_from_NSXedge: No IP addr found in any NSX edge")
1877 except XmlElementTree
.ParseError
as Err
:
1878 self
.logger
.debug("ParseError in response from NSX Manager {}".format(Err
.message
), exc_info
=True)
1881 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1882 """Send and action over a VM instance from VIM
1883 Returns the vm_id if the action was successfully sent to the VIM"""
1885 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1886 if vm__vim_uuid
is None or action_dict
is None:
1887 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1889 vca
= self
.connect()
1891 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1893 vdc
= vca
.get_vdc(self
.tenant_name
)
1895 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1897 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1898 if vapp_name
is None:
1899 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1900 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1902 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1905 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1906 # TODO fix all status
1907 if "start" in action_dict
:
1908 vm_info
= the_vapp
.get_vms_details()
1909 vm_status
= vm_info
[0]['status']
1910 self
.logger
.info("action_vminstance: Power on vApp: {}".format(vapp_name
))
1911 if vm_status
== "Suspended" or vm_status
== "Powered off":
1912 power_on_task
= the_vapp
.poweron()
1913 result
= vca
.block_until_completed(power_on_task
)
1914 self
.instance_actions_result("start", result
, vapp_name
)
1915 elif "rebuild" in action_dict
:
1916 self
.logger
.info("action_vminstance: Rebuild vApp: {}".format(vapp_name
))
1917 rebuild_task
= the_vapp
.deploy(powerOn
=True)
1918 result
= vca
.block_until_completed(rebuild_task
)
1919 self
.instance_actions_result("rebuild", result
, vapp_name
)
1920 elif "pause" in action_dict
:
1921 self
.logger
.info("action_vminstance: pause vApp: {}".format(vapp_name
))
1922 pause_task
= the_vapp
.undeploy(action
='suspend')
1923 result
= vca
.block_until_completed(pause_task
)
1924 self
.instance_actions_result("pause", result
, vapp_name
)
1925 elif "resume" in action_dict
:
1926 self
.logger
.info("action_vminstance: resume vApp: {}".format(vapp_name
))
1927 power_task
= the_vapp
.poweron()
1928 result
= vca
.block_until_completed(power_task
)
1929 self
.instance_actions_result("resume", result
, vapp_name
)
1930 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1931 action_name
, value
= action_dict
.items()[0]
1932 self
.logger
.info("action_vminstance: {} vApp: {}".format(action_name
, vapp_name
))
1933 power_off_task
= the_vapp
.undeploy(action
='powerOff')
1934 result
= vca
.block_until_completed(power_off_task
)
1935 if action_name
== "shutdown":
1936 self
.instance_actions_result("shutdown", result
, vapp_name
)
1938 self
.instance_actions_result("shutoff", result
, vapp_name
)
1939 elif "forceOff" in action_dict
:
1940 result
= the_vapp
.undeploy(action
='force')
1941 self
.instance_actions_result("forceOff", result
, vapp_name
)
1942 elif "reboot" in action_dict
:
1943 self
.logger
.info("action_vminstance: reboot vApp: {}".format(vapp_name
))
1944 reboot_task
= the_vapp
.reboot()
1946 raise vimconn
.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict
))
1948 except Exception as exp
:
1949 self
.logger
.debug("action_vminstance: Failed with Exception {}".format(exp
))
1950 raise vimconn
.vimconnException("action_vminstance: Failed with Exception {}".format(exp
))
1952 def instance_actions_result(self
, action
, result
, vapp_name
):
1954 self
.logger
.info("action_vminstance: Sucessfully {} the vApp: {}".format(action
, vapp_name
))
1956 self
.logger
.error("action_vminstance: Failed to {} vApp: {}".format(action
, vapp_name
))
1958 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1960 Get a console for the virtual machine
1962 vm_id: uuid of the VM
1963 console_type, can be:
1964 "novnc" (by default), "xvpvnc" for VNC types,
1965 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1966 Returns dict with the console parameters:
1967 protocol: ssh, ftp, http, https, ...
1968 server: usually ip address
1969 port: the http, ssh, ... port
1970 suffix: extra text, e.g. the http path and query string
1972 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1974 # NOT USED METHODS in current version
1976 def host_vim2gui(self
, host
, server_dict
):
1977 """Transform host dictionary from VIM format to GUI format,
1978 and append to the server_dict
1980 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1982 def get_hosts_info(self
):
1983 """Get the information of deployed hosts
1984 Returns the hosts content"""
1985 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1987 def get_hosts(self
, vim_tenant
):
1988 """Get the hosts and deployed instances
1989 Returns the hosts content"""
1990 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1992 def get_processor_rankings(self
):
1993 """Get the processor rankings in the VIM database"""
1994 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1996 def new_host(self
, host_data
):
1997 """Adds a new host to VIM"""
1998 '''Returns status code of the VIM response'''
1999 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2001 def new_external_port(self
, port_data
):
2002 """Adds a external port to VIM"""
2003 '''Returns the port identifier'''
2004 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2006 def new_external_network(self
, net_name
, net_type
):
2007 """Adds a external network to VIM (shared)"""
2008 '''Returns the network identifier'''
2009 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2011 def connect_port_network(self
, port_id
, network_id
, admin
=False):
2012 """Connects a external port to a network"""
2013 '''Returns status code of the VIM response'''
2014 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2016 def new_vminstancefromJSON(self
, vm_data
):
2017 """Adds a VM instance to VIM"""
2018 '''Returns the instance identifier'''
2019 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2021 def get_network_name_by_id(self
, network_uuid
=None):
2022 """Method gets vcloud director network named based on supplied uuid.
2025 network_uuid: network_id
2028 The return network name.
2031 vca
= self
.connect()
2033 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2035 if not network_uuid
:
2039 org_dict
= self
.get_org(self
.org_uuid
)
2040 if 'networks' in org_dict
:
2041 org_network_dict
= org_dict
['networks']
2042 for net_uuid
in org_network_dict
:
2043 if net_uuid
== network_uuid
:
2044 return org_network_dict
[net_uuid
]
2046 self
.logger
.debug("Exception in get_network_name_by_id")
2047 self
.logger
.debug(traceback
.format_exc())
2051 def get_network_id_by_name(self
, network_name
=None):
2052 """Method gets vcloud director network uuid based on supplied name.
2055 network_name: network_name
2057 The return network uuid.
2058 network_uuid: network_id
2061 vca
= self
.connect()
2063 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2065 if not network_name
:
2066 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
2070 org_dict
= self
.get_org(self
.org_uuid
)
2071 if org_dict
and 'networks' in org_dict
:
2072 org_network_dict
= org_dict
['networks']
2073 for net_uuid
,net_name
in org_network_dict
.iteritems():
2074 if net_name
== network_name
:
2077 except KeyError as exp
:
2078 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
2082 def list_org_action(self
):
2084 Method leverages vCloud director and query for available organization for particular user
2087 vca - is active VCA connection.
2088 vdc_name - is a vdc name that will be used to query vms action
2091 The return XML respond
2094 vca
= self
.connect()
2096 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2098 url_list
= [vca
.host
, '/api/org']
2099 vm_list_rest_call
= ''.join(url_list
)
2101 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2102 response
= Http
.get(url
=vm_list_rest_call
,
2103 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2106 if response
.status_code
== requests
.codes
.ok
:
2107 return response
.content
2111 def get_org_action(self
, org_uuid
=None):
2113 Method leverages vCloud director and retrieve available object fdr organization.
2116 vca - is active VCA connection.
2117 vdc_name - is a vdc name that will be used to query vms action
2120 The return XML respond
2123 vca
= self
.connect()
2125 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2127 if org_uuid
is None:
2130 url_list
= [vca
.host
, '/api/org/', org_uuid
]
2131 vm_list_rest_call
= ''.join(url_list
)
2133 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2134 response
= Http
.get(url
=vm_list_rest_call
,
2135 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2138 if response
.status_code
== requests
.codes
.ok
:
2139 return response
.content
2143 def get_org(self
, org_uuid
=None):
2145 Method retrieves available organization in vCloud Director
2148 org_uuid - is a organization uuid.
2151 The return dictionary with following key
2152 "network" - for network list under the org
2153 "catalogs" - for network list under the org
2154 "vdcs" - for vdc list under org
2158 vca
= self
.connect()
2160 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2162 if org_uuid
is None:
2165 content
= self
.get_org_action(org_uuid
=org_uuid
)
2170 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2171 for child
in vm_list_xmlroot
:
2172 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2173 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2174 org_dict
['vdcs'] = vdc_list
2175 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2176 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2177 org_dict
['networks'] = network_list
2178 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2179 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2180 org_dict
['catalogs'] = catalog_list
2186 def get_org_list(self
):
2188 Method retrieves available organization in vCloud Director
2191 vca - is active VCA connection.
2194 The return dictionary and key for each entry VDC UUID
2198 vca
= self
.connect()
2200 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2202 content
= self
.list_org_action()
2204 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2205 for vm_xml
in vm_list_xmlroot
:
2206 if vm_xml
.tag
.split("}")[1] == 'Org':
2207 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2208 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
2214 def vms_view_action(self
, vdc_name
=None):
2215 """ Method leverages vCloud director vms query call
2218 vca - is active VCA connection.
2219 vdc_name - is a vdc name that will be used to query vms action
2222 The return XML respond
2224 vca
= self
.connect()
2225 if vdc_name
is None:
2228 url_list
= [vca
.host
, '/api/vms/query']
2229 vm_list_rest_call
= ''.join(url_list
)
2231 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2232 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
2233 vca
.vcloud_session
.organization
.Link
)
2235 response
= Http
.get(url
=vm_list_rest_call
,
2236 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2239 if response
.status_code
== requests
.codes
.ok
:
2240 return response
.content
2244 def get_vapp_list(self
, vdc_name
=None):
2246 Method retrieves vApp list deployed vCloud director and returns a dictionary
2247 contains a list of all vapp deployed for queried VDC.
2248 The key for a dictionary is vApp UUID
2252 vca - is active VCA connection.
2253 vdc_name - is a vdc name that will be used to query vms action
2256 The return dictionary and key for each entry vapp UUID
2260 if vdc_name
is None:
2263 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2265 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2266 for vm_xml
in vm_list_xmlroot
:
2267 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2268 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
2269 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
2270 if 'vappTemplate-' in rawuuid
[0]:
2271 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2272 # vm and use raw UUID as key
2273 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
2279 def get_vm_list(self
, vdc_name
=None):
2281 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2282 contains a list of all VM's deployed for queried VDC.
2283 The key for a dictionary is VM UUID
2287 vca - is active VCA connection.
2288 vdc_name - is a vdc name that will be used to query vms action
2291 The return dictionary and key for each entry vapp UUID
2295 if vdc_name
is None:
2298 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2300 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2301 for vm_xml
in vm_list_xmlroot
:
2302 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2303 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2304 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2305 if 'vm-' in rawuuid
[0]:
2306 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2307 # vm and use raw UUID as key
2308 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2314 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2316 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2317 contains a list of all VM's deployed for queried VDC.
2318 The key for a dictionary is VM UUID
2322 vca - is active VCA connection.
2323 vdc_name - is a vdc name that will be used to query vms action
2326 The return dictionary and key for each entry vapp UUID
2329 vca
= self
.connect()
2331 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2333 if vdc_name
is None:
2336 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2338 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2339 for vm_xml
in vm_list_xmlroot
:
2340 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2341 # lookup done by UUID
2343 if vapp_name
in vm_xml
.attrib
['container']:
2344 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2345 if 'vm-' in rawuuid
[0]:
2346 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2348 # lookup done by Name
2350 if vapp_name
in vm_xml
.attrib
['name']:
2351 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2352 if 'vm-' in rawuuid
[0]:
2353 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2360 def get_network_action(self
, network_uuid
=None):
2362 Method leverages vCloud director and query network based on network uuid
2365 vca - is active VCA connection.
2366 network_uuid - is a network uuid
2369 The return XML respond
2372 vca
= self
.connect()
2374 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2376 if network_uuid
is None:
2379 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2380 vm_list_rest_call
= ''.join(url_list
)
2382 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2383 response
= Http
.get(url
=vm_list_rest_call
,
2384 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2387 if response
.status_code
== requests
.codes
.ok
:
2388 return response
.content
2392 def get_vcd_network(self
, network_uuid
=None):
2394 Method retrieves available network from vCloud Director
2397 network_uuid - is VCD network UUID
2399 Each element serialized as key : value pair
2401 Following keys available for access. network_configuration['Gateway'}
2405 <IsInherited>true</IsInherited>
2406 <Gateway>172.16.252.100</Gateway>
2407 <Netmask>255.255.255.0</Netmask>
2408 <Dns1>172.16.254.201</Dns1>
2409 <Dns2>172.16.254.202</Dns2>
2410 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2411 <IsEnabled>true</IsEnabled>
2414 <StartAddress>172.16.252.1</StartAddress>
2415 <EndAddress>172.16.252.99</EndAddress>
2420 <FenceMode>bridged</FenceMode>
2423 The return dictionary and key for each entry vapp UUID
2426 network_configuration
= {}
2427 if network_uuid
is None:
2431 content
= self
.get_network_action(network_uuid
=network_uuid
)
2432 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2434 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2435 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2436 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2438 for child
in vm_list_xmlroot
:
2439 if child
.tag
.split("}")[1] == 'IsShared':
2440 network_configuration
['isShared'] = child
.text
.strip()
2441 if child
.tag
.split("}")[1] == 'Configuration':
2442 for configuration
in child
.iter():
2443 tagKey
= configuration
.tag
.split("}")[1].strip()
2445 network_configuration
[tagKey
] = configuration
.text
.strip()
2446 return network_configuration
2447 except Exception as exp
:
2448 self
.logger
.debug("get_vcd_network: Failed with Exception {}".format(exp
))
2449 raise vimconn
.vimconnException("get_vcd_network: Failed with Exception {}".format(exp
))
2451 return network_configuration
2453 def delete_network_action(self
, network_uuid
=None):
2455 Method delete given network from vCloud director
2458 network_uuid - is a network uuid that client wish to delete
2461 The return None or XML respond or false
2464 vca
= self
.connect_as_admin()
2466 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2467 if network_uuid
is None:
2470 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2471 vm_list_rest_call
= ''.join(url_list
)
2473 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2474 response
= Http
.delete(url
=vm_list_rest_call
,
2475 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2479 if response
.status_code
== 202:
2484 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2485 ip_profile
=None, isshared
='true'):
2487 Method create network in vCloud director
2490 network_name - is network name to be created.
2491 net_type - can be 'bridge','data','ptp','mgmt'.
2492 ip_profile is a dict containing the IP parameters of the network
2493 isshared - is a boolean
2494 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2495 It optional attribute. by default if no parent network indicate the first available will be used.
2498 The return network uuid or return None
2501 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2502 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2503 ip_profile
=ip_profile
,
2505 parent_network_uuid
=parent_network_uuid
,
2508 self
.logger
.debug("Failed create network {}.".format(network_name
))
2512 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2513 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2514 if len(vcd_uuid
) == 4:
2515 self
.logger
.info("Created new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2518 self
.logger
.debug("Failed create network {}".format(network_name
))
2521 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2522 ip_profile
=None, isshared
='true'):
2524 Method create network in vCloud director
2527 network_name - is network name to be created.
2528 net_type - can be 'bridge','data','ptp','mgmt'.
2529 ip_profile is a dict containing the IP parameters of the network
2530 isshared - is a boolean
2531 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2532 It optional attribute. by default if no parent network indicate the first available will be used.
2535 The return network uuid or return None
2538 vca
= self
.connect_as_admin()
2540 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2541 if network_name
is None:
2544 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2545 vm_list_rest_call
= ''.join(url_list
)
2546 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2547 response
= Http
.get(url
=vm_list_rest_call
,
2548 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2552 provider_network
= None
2553 available_networks
= None
2554 add_vdc_rest_url
= None
2556 if response
.status_code
!= requests
.codes
.ok
:
2557 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2558 response
.status_code
))
2562 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2563 for child
in vm_list_xmlroot
:
2564 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2565 provider_network
= child
.attrib
.get('href')
2566 # application/vnd.vmware.admin.providervdc+xml
2567 if child
.tag
.split("}")[1] == 'Link':
2568 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2569 and child
.attrib
.get('rel') == 'add':
2570 add_vdc_rest_url
= child
.attrib
.get('href')
2572 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2573 self
.logger
.debug("Respond body {}".format(response
.content
))
2576 # find pvdc provided available network
2577 response
= Http
.get(url
=provider_network
,
2578 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2581 if response
.status_code
!= requests
.codes
.ok
:
2582 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2583 response
.status_code
))
2586 # available_networks.split("/")[-1]
2588 if parent_network_uuid
is None:
2590 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2591 for child
in vm_list_xmlroot
.iter():
2592 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2593 for networks
in child
.iter():
2594 # application/vnd.vmware.admin.network+xml
2595 if networks
.attrib
.get('href') is not None:
2596 available_networks
= networks
.attrib
.get('href')
2602 #Configure IP profile of the network
2603 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2605 if 'subnet_address' not in ip_profile
or ip_profile
['subnet_address'] is None:
2606 subnet_rand
= random
.randint(0, 255)
2607 ip_base
= "192.168.{}.".format(subnet_rand
)
2608 ip_profile
['subnet_address'] = ip_base
+ "0/24"
2610 ip_base
= ip_profile
['subnet_address'].rsplit('.',1)[0] + '.'
2612 if 'gateway_address' not in ip_profile
or ip_profile
['gateway_address'] is None:
2613 ip_profile
['gateway_address']=ip_base
+ "1"
2614 if 'dhcp_count' not in ip_profile
or ip_profile
['dhcp_count'] is None:
2615 ip_profile
['dhcp_count']=DEFAULT_IP_PROFILE
['dhcp_count']
2616 if 'dhcp_enabled' not in ip_profile
or ip_profile
['dhcp_enabled'] is None:
2617 ip_profile
['dhcp_enabled']=DEFAULT_IP_PROFILE
['dhcp_enabled']
2618 if 'dhcp_start_address' not in ip_profile
or ip_profile
['dhcp_start_address'] is None:
2619 ip_profile
['dhcp_start_address']=ip_base
+ "3"
2620 if 'ip_version' not in ip_profile
or ip_profile
['ip_version'] is None:
2621 ip_profile
['ip_version']=DEFAULT_IP_PROFILE
['ip_version']
2622 if 'dns_address' not in ip_profile
or ip_profile
['dns_address'] is None:
2623 ip_profile
['dns_address']=ip_base
+ "2"
2625 gateway_address
=ip_profile
['gateway_address']
2626 dhcp_count
=int(ip_profile
['dhcp_count'])
2627 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2629 if ip_profile
['dhcp_enabled']==True:
2632 dhcp_enabled
='false'
2633 dhcp_start_address
=ip_profile
['dhcp_start_address']
2635 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2636 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2637 end_ip_int
+= dhcp_count
- 1
2638 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2640 ip_version
=ip_profile
['ip_version']
2641 dns_address
=ip_profile
['dns_address']
2642 except KeyError as exp
:
2643 self
.logger
.debug("Create Network REST: Key error {}".format(exp
))
2644 raise vimconn
.vimconnException("Create Network REST: Key error{}".format(exp
))
2646 # either use client provided UUID or search for a first available
2647 # if both are not defined we return none
2648 if parent_network_uuid
is not None:
2649 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2650 add_vdc_rest_url
= ''.join(url_list
)
2653 fence_mode
="isolated"
2655 is_inherited
='false'
2656 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2657 <Description>Openmano created</Description>
2661 <IsInherited>{1:s}</IsInherited>
2662 <Gateway>{2:s}</Gateway>
2663 <Netmask>{3:s}</Netmask>
2665 <IsEnabled>{5:s}</IsEnabled>
2668 <StartAddress>{6:s}</StartAddress>
2669 <EndAddress>{7:s}</EndAddress>
2674 <FenceMode>{8:s}</FenceMode>
2676 <IsShared>{9:s}</IsShared>
2677 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2678 subnet_address
, dns_address
, dhcp_enabled
,
2679 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2682 fence_mode
="bridged"
2683 is_inherited
='false'
2684 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2685 <Description>Openmano created</Description>
2689 <IsInherited>{1:s}</IsInherited>
2690 <Gateway>{2:s}</Gateway>
2691 <Netmask>{3:s}</Netmask>
2693 <IsEnabled>{5:s}</IsEnabled>
2696 <StartAddress>{6:s}</StartAddress>
2697 <EndAddress>{7:s}</EndAddress>
2702 <ParentNetwork href="{8:s}"/>
2703 <FenceMode>{9:s}</FenceMode>
2705 <IsShared>{10:s}</IsShared>
2706 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2707 subnet_address
, dns_address
, dhcp_enabled
,
2708 dhcp_start_address
, dhcp_end_address
, available_networks
,
2709 fence_mode
, isshared
)
2711 headers
= vca
.vcloud_session
.get_vcloud_headers()
2712 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2714 response
= Http
.post(url
=add_vdc_rest_url
,
2720 if response
.status_code
!= 201:
2721 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
2722 .format(response
.status_code
,response
.content
))
2724 network
= networkType
.parseString(response
.content
, True)
2725 create_nw_task
= network
.get_Tasks().get_Task()[0]
2727 # if we all ok we respond with content after network creation completes
2728 # otherwise by default return None
2729 if create_nw_task
is not None:
2730 self
.logger
.debug("Create Network REST : Waiting for Network creation complete")
2731 status
= vca
.block_until_completed(create_nw_task
)
2733 return response
.content
2735 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2736 .format(response
.content
))
2737 except Exception as exp
:
2738 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2742 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2744 Method sets convert CIDR netmask address to normal IP format
2746 cidr_ip : CIDR IP address
2748 netmask : Converted netmask
2750 if cidr_ip
is not None:
2752 network
, net_bits
= cidr_ip
.split('/')
2753 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2759 def get_provider_rest(self
, vca
=None):
2761 Method gets provider vdc view from vcloud director
2764 network_name - is network name to be created.
2765 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2766 It optional attribute. by default if no parent network indicate the first available will be used.
2769 The return xml content of respond or None
2772 url_list
= [vca
.host
, '/api/admin']
2773 response
= Http
.get(url
=''.join(url_list
),
2774 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2778 if response
.status_code
== requests
.codes
.ok
:
2779 return response
.content
2782 def create_vdc(self
, vdc_name
=None):
2786 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2787 if xml_content
is not None:
2789 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2790 for child
in task_resp_xmlroot
:
2791 if child
.tag
.split("}")[1] == 'Owner':
2792 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2793 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2796 self
.logger
.debug("Respond body {}".format(xml_content
))
2800 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2802 Method create vdc in vCloud director based on VDC template.
2803 it uses pre-defined template that must be named openmano
2806 vdc_name - name of a new vdc.
2809 The return xml content of respond or None
2812 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2813 vca
= self
.connect()
2815 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2816 if vdc_name
is None:
2819 url_list
= [vca
.host
, '/api/vdcTemplates']
2820 vm_list_rest_call
= ''.join(url_list
)
2821 response
= Http
.get(url
=vm_list_rest_call
,
2822 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2826 # container url to a template
2827 vdc_template_ref
= None
2829 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2830 for child
in vm_list_xmlroot
:
2831 # application/vnd.vmware.admin.providervdc+xml
2832 # we need find a template from witch we instantiate VDC
2833 if child
.tag
.split("}")[1] == 'VdcTemplate':
2834 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2835 'name') == 'openmano':
2836 vdc_template_ref
= child
.attrib
.get('href')
2838 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2839 self
.logger
.debug("Respond body {}".format(response
.content
))
2842 # if we didn't found required pre defined template we return None
2843 if vdc_template_ref
is None:
2848 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2849 vm_list_rest_call
= ''.join(url_list
)
2850 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2851 <Source href="{1:s}"></Source>
2852 <Description>opnemano</Description>
2853 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2854 headers
= vca
.vcloud_session
.get_vcloud_headers()
2855 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2856 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2858 # if we all ok we respond with content otherwise by default None
2859 if response
.status_code
>= 200 and response
.status_code
< 300:
2860 return response
.content
2863 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2864 self
.logger
.debug("Respond body {}".format(response
.content
))
2868 def create_vdc_rest(self
, vdc_name
=None):
2870 Method create network in vCloud director
2873 network_name - is network name to be created.
2874 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2875 It optional attribute. by default if no parent network indicate the first available will be used.
2878 The return network uuid or return None
2881 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2883 vca
= self
.connect_as_admin()
2885 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2886 if vdc_name
is None:
2889 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2890 vm_list_rest_call
= ''.join(url_list
)
2891 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2892 response
= Http
.get(url
=vm_list_rest_call
,
2893 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2897 provider_vdc_ref
= None
2898 add_vdc_rest_url
= None
2899 available_networks
= None
2901 if response
.status_code
!= requests
.codes
.ok
:
2902 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2903 response
.status_code
))
2907 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2908 for child
in vm_list_xmlroot
:
2909 # application/vnd.vmware.admin.providervdc+xml
2910 if child
.tag
.split("}")[1] == 'Link':
2911 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2912 and child
.attrib
.get('rel') == 'add':
2913 add_vdc_rest_url
= child
.attrib
.get('href')
2915 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2916 self
.logger
.debug("Respond body {}".format(response
.content
))
2919 response
= self
.get_provider_rest(vca
=vca
)
2921 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2922 for child
in vm_list_xmlroot
:
2923 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2924 for sub_child
in child
:
2925 provider_vdc_ref
= sub_child
.attrib
.get('href')
2927 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2928 self
.logger
.debug("Respond body {}".format(response
))
2931 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2932 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2933 <AllocationModel>ReservationPool</AllocationModel>
2934 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2935 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2936 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2937 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2938 <ProviderVdcReference
2939 name="Main Provider"
2941 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2945 headers
= vca
.vcloud_session
.get_vcloud_headers()
2946 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2947 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2950 # if we all ok we respond with content otherwise by default None
2951 if response
.status_code
== 201:
2952 return response
.content
2955 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
2957 Method retrieve vapp detail from vCloud director
2960 vapp_uuid - is vapp identifier.
2963 The return network uuid or return None
2969 if need_admin_access
:
2970 vca
= self
.connect_as_admin()
2972 vca
= self
.connect()
2975 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2976 if vapp_uuid
is None:
2979 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2980 get_vapp_restcall
= ''.join(url_list
)
2982 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2983 response
= Http
.get(url
=get_vapp_restcall
,
2984 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2988 if response
.status_code
!= requests
.codes
.ok
:
2989 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2990 response
.status_code
))
2991 return parsed_respond
2994 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2995 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2997 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2998 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2999 'vmw': 'http://www.vmware.com/schema/ovf',
3000 'vm': 'http://www.vmware.com/vcloud/v1.5',
3001 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
3002 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3003 "xmlns":"http://www.vmware.com/vcloud/v1.5"
3006 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
3007 if created_section
is not None:
3008 parsed_respond
['created'] = created_section
.text
3010 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
3011 if network_section
is not None and 'networkName' in network_section
.attrib
:
3012 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
3014 ipscopes_section
= \
3015 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
3017 if ipscopes_section
is not None:
3018 for ipscope
in ipscopes_section
:
3019 for scope
in ipscope
:
3020 tag_key
= scope
.tag
.split("}")[1]
3021 if tag_key
== 'IpRanges':
3022 ip_ranges
= scope
.getchildren()
3023 for ipblock
in ip_ranges
:
3024 for block
in ipblock
:
3025 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
3027 parsed_respond
[tag_key
] = scope
.text
3029 # parse children section for other attrib
3030 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
3031 if children_section
is not None:
3032 parsed_respond
['name'] = children_section
.attrib
['name']
3033 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
3034 if "nestedHypervisorEnabled" in children_section
.attrib
else None
3035 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
3036 parsed_respond
['status'] = children_section
.attrib
['status']
3037 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
3038 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
3040 for adapters
in network_adapter
:
3041 adapter_key
= adapters
.tag
.split("}")[1]
3042 if adapter_key
== 'PrimaryNetworkConnectionIndex':
3043 parsed_respond
['primarynetwork'] = adapters
.text
3044 if adapter_key
== 'NetworkConnection':
3046 if 'network' in adapters
.attrib
:
3047 vnic
['network'] = adapters
.attrib
['network']
3048 for adapter
in adapters
:
3049 setting_key
= adapter
.tag
.split("}")[1]
3050 vnic
[setting_key
] = adapter
.text
3051 nic_list
.append(vnic
)
3053 for link
in children_section
:
3054 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
3055 if link
.attrib
['rel'] == 'screen:acquireTicket':
3056 parsed_respond
['acquireTicket'] = link
.attrib
3057 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
3058 parsed_respond
['acquireMksTicket'] = link
.attrib
3060 parsed_respond
['interfaces'] = nic_list
3061 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
3062 if vCloud_extension_section
is not None:
3063 vm_vcenter_info
= {}
3064 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
3065 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
3066 if vmext
is not None:
3067 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
3068 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
3070 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
3071 vm_virtual_hardware_info
= {}
3072 if virtual_hardware_section
is not None:
3073 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
3074 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
3075 disk_size
= item
.find("rasd:HostResource" ,namespaces
3076 ).attrib
["{"+namespaces
['vm']+"}capacity"]
3078 vm_virtual_hardware_info
["disk_size"]= disk_size
3081 for link
in virtual_hardware_section
:
3082 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
3083 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
3084 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
3087 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
3088 except Exception as exp
:
3089 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
3090 return parsed_respond
3092 def acuire_console(self
, vm_uuid
=None):
3094 vca
= self
.connect()
3096 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3100 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
3101 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
3102 console_dict
= vm_dict
['acquireTicket']
3103 console_rest_call
= console_dict
['href']
3105 response
= Http
.post(url
=console_rest_call
,
3106 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3110 if response
.status_code
== requests
.codes
.ok
:
3111 return response
.content
3115 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
3117 Method retrieve vm disk details
3120 vapp_uuid - is vapp identifier.
3121 flavor_disk - disk size as specified in VNFD (flavor)
3124 The return network uuid or return None
3128 #Flavor disk is in GB convert it into MB
3129 flavor_disk
= int(flavor_disk
) * 1024
3130 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
3132 vm_name
= vm_details
["name"]
3133 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
3135 if vm_details
and "vm_virtual_hardware" in vm_details
:
3136 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
3137 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
3139 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
3141 if flavor_disk
> vm_disk
:
3142 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
3143 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
3144 vm_disk
, flavor_disk
))
3147 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
3150 except Exception as exp
:
3151 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
3154 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
3156 Method retrieve modify vm disk size
3159 disk_href - vCD API URL to GET and PUT disk data
3160 disk_size - disk size as specified in VNFD (flavor)
3163 The return network uuid or return None
3165 vca
= self
.connect()
3167 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3168 if disk_href
is None or disk_size
is None:
3171 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3172 response
= Http
.get(url
=disk_href
,
3173 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3177 if response
.status_code
!= requests
.codes
.ok
:
3178 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
3179 response
.status_code
))
3182 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
3183 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
3184 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3186 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
3187 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
3188 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
3189 if disk_item
is not None:
3190 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
3193 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
3194 xml_declaration
=True)
3196 #Send PUT request to modify disk size
3197 headers
= vca
.vcloud_session
.get_vcloud_headers()
3198 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3200 response
= Http
.put(url
=disk_href
,
3203 verify
=vca
.verify
, logger
=self
.logger
)
3205 if response
.status_code
!= 202:
3206 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
3207 response
.status_code
))
3209 modify_disk_task
= taskType
.parseString(response
.content
, True)
3210 if type(modify_disk_task
) is GenericTask
:
3211 status
= vca
.block_until_completed(modify_disk_task
)
3216 except Exception as exp
:
3217 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
3220 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
3222 Method to attach pci devices to VM
3225 vapp_uuid - uuid of vApp/VM
3226 pci_devices - pci devices infromation as specified in VNFD (flavor)
3229 The status of add pci device task , vm object and
3230 vcenter_conect object
3233 vcenter_conect
= None
3234 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
3236 vm_vcenter_info
= self
.get_vm_vcenter_info(vapp_uuid
)
3237 except Exception as exp
:
3238 self
.logger
.error("Error occurred while getting vCenter infromationn"\
3239 " for VM : {}".format(exp
))
3240 raise vimconn
.vimconnException(message
=exp
)
3242 if vm_vcenter_info
["vm_moref_id"]:
3244 if hasattr(ssl
, '_create_unverified_context'):
3245 context
= ssl
._create
_unverified
_context
()
3247 no_of_pci_devices
= len(pci_devices
)
3248 if no_of_pci_devices
> 0:
3249 vcenter_conect
= SmartConnect(
3250 host
=vm_vcenter_info
["vm_vcenter_ip"],
3251 user
=vm_vcenter_info
["vm_vcenter_user"],
3252 pwd
=vm_vcenter_info
["vm_vcenter_password"],
3253 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
3255 atexit
.register(Disconnect
, vcenter_conect
)
3256 content
= vcenter_conect
.RetrieveContent()
3258 #Get VM and its host
3259 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_vcenter_info
["vm_moref_id"])
3260 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
3261 if host_obj
and vm_obj
:
3262 #get PCI devies from host on which vapp is currently installed
3263 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
3265 if avilable_pci_devices
is None:
3266 #find other hosts with active pci devices
3267 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
3272 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3273 #Migrate vm to the host where PCI devices are availble
3274 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
3275 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
3276 if task
is not None:
3277 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3278 self
.logger
.info("Migrate VM status: {}".format(result
))
3279 host_obj
= new_host_obj
3281 self
.logger
.info("Fail to migrate VM : {}".format(result
))
3282 raise vimconn
.vimconnNotFoundException(
3283 "Fail to migrate VM : {} to host {}".format(
3288 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3289 #Add PCI devices one by one
3290 for pci_device
in avilable_pci_devices
:
3291 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
3293 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3295 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3297 self
.logger
.error("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3298 return True, vm_obj
, vcenter_conect
3300 self
.logger
.error("Currently there is no host with"\
3301 " {} number of avaialble PCI devices required for VM {}".format(
3305 raise vimconn
.vimconnNotFoundException(
3306 "Currently there is no host with {} "\
3307 "number of avaialble PCI devices required for VM {}".format(
3311 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
3313 except vmodl
.MethodFault
as error
:
3314 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
3315 return None, vm_obj
, vcenter_conect
3317 def get_vm_obj(self
, content
, mob_id
):
3319 Method to get the vsphere VM object associated with a given morf ID
3321 vapp_uuid - uuid of vApp/VM
3322 content - vCenter content object
3323 mob_id - mob_id of VM
3331 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3332 [vim
.VirtualMachine
], True
3334 for vm
in container
.view
:
3335 mobID
= vm
._GetMoId
()
3338 host_obj
= vm_obj
.runtime
.host
3340 except Exception as exp
:
3341 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
3342 return host_obj
, vm_obj
3344 def get_pci_devices(self
, host
, need_devices
):
3346 Method to get the details of pci devices on given host
3348 host - vSphere host object
3349 need_devices - number of pci devices needed on host
3352 array of pci devices
3356 used_devices_ids
= []
3360 pciPassthruInfo
= host
.config
.pciPassthruInfo
3361 pciDevies
= host
.hardware
.pciDevice
3363 for pci_status
in pciPassthruInfo
:
3364 if pci_status
.passthruActive
:
3365 for device
in pciDevies
:
3366 if device
.id == pci_status
.id:
3367 all_device_ids
.append(device
.id)
3368 all_devices
.append(device
)
3370 #check if devices are in use
3371 avalible_devices
= all_devices
3373 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
3374 vm_devices
= vm
.config
.hardware
.device
3375 for device
in vm_devices
:
3376 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
3377 if device
.backing
.id in all_device_ids
:
3378 for use_device
in avalible_devices
:
3379 if use_device
.id == device
.backing
.id:
3380 avalible_devices
.remove(use_device
)
3381 used_devices_ids
.append(device
.backing
.id)
3382 self
.logger
.debug("Device {} from devices {}"\
3383 "is in use".format(device
.backing
.id,
3386 if len(avalible_devices
) < need_devices
:
3387 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
3389 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
3393 required_devices
= avalible_devices
[:need_devices
]
3394 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
3395 len(avalible_devices
),
3398 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
3400 return required_devices
3402 except Exception as exp
:
3403 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
3407 def get_host_and_PCIdevices(self
, content
, need_devices
):
3409 Method to get the details of pci devices infromation on all hosts
3412 content - vSphere host object
3413 need_devices - number of pci devices needed on host
3416 array of pci devices and host object
3419 pci_device_objs
= None
3422 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3423 [vim
.HostSystem
], True)
3424 for host
in container
.view
:
3425 devices
= self
.get_pci_devices(host
, need_devices
)
3428 pci_device_objs
= devices
3430 except Exception as exp
:
3431 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
3433 return host_obj
,pci_device_objs
3435 def relocate_vm(self
, dest_host
, vm
) :
3437 Method to get the relocate VM to new host
3440 dest_host - vSphere host object
3441 vm - vSphere VM object
3448 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
3449 task
= vm
.Relocate(relocate_spec
)
3450 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
3451 except Exception as exp
:
3452 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
3453 dest_host
, vm
, exp
))
3456 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
3458 Waits and provides updates on a vSphere task
3460 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
3463 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
3464 if task
.info
.result
is not None and not hideResult
:
3465 self
.logger
.info('{} completed successfully, result: {}'.format(
3469 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
3471 self
.logger
.error('{} did not complete successfully: {} '.format(
3476 return task
.info
.result
3478 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
3480 Method to add pci device in given VM
3483 host_object - vSphere host object
3484 vm_object - vSphere VM object
3485 host_pci_dev - host_pci_dev must be one of the devices from the
3486 host_object.hardware.pciDevice list
3487 which is configured as a PCI passthrough device
3493 if vm_object
and host_object
and host_pci_dev
:
3495 #Add PCI device to VM
3496 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
3497 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
3499 if host_pci_dev
.id not in systemid_by_pciid
:
3500 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
3503 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
3504 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
3506 systemId
=systemid_by_pciid
[host_pci_dev
.id],
3507 vendorId
=host_pci_dev
.vendorId
,
3508 deviceName
=host_pci_dev
.deviceName
)
3510 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
3512 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
3513 new_device_config
.operation
= "add"
3514 vmConfigSpec
= vim
.vm
.ConfigSpec()
3515 vmConfigSpec
.deviceChange
= [new_device_config
]
3517 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
3518 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
3519 host_pci_dev
, vm_object
, host_object
)
3521 except Exception as exp
:
3522 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3528 def get_vm_vcenter_info(self
, vapp_uuid
):
3530 Method to get details of vCenter and vm
3533 vapp_uuid - uuid of vApp or VM
3536 Moref Id of VM and deails of vCenter
3538 vm_vcenter_info
= {}
3540 if self
.vcenter_ip
is not None:
3541 vm_vcenter_info
["vm_vcenter_ip"] = self
.vcenter_ip
3543 raise vimconn
.vimconnException(message
="vCenter IP is not provided."\
3544 " Please provide vCenter IP while attaching datacenter to tenant in --config")
3545 if self
.vcenter_port
is not None:
3546 vm_vcenter_info
["vm_vcenter_port"] = self
.vcenter_port
3548 raise vimconn
.vimconnException(message
="vCenter port is not provided."\
3549 " Please provide vCenter port while attaching datacenter to tenant in --config")
3550 if self
.vcenter_user
is not None:
3551 vm_vcenter_info
["vm_vcenter_user"] = self
.vcenter_user
3553 raise vimconn
.vimconnException(message
="vCenter user is not provided."\
3554 " Please provide vCenter user while attaching datacenter to tenant in --config")
3556 if self
.vcenter_password
is not None:
3557 vm_vcenter_info
["vm_vcenter_password"] = self
.vcenter_password
3559 raise vimconn
.vimconnException(message
="vCenter user password is not provided."\
3560 " Please provide vCenter user password while attaching datacenter to tenant in --config")
3562 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
3563 if vm_details
and "vm_vcenter_info" in vm_details
:
3564 vm_vcenter_info
["vm_moref_id"] = vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
3566 return vm_vcenter_info
3568 except Exception as exp
:
3569 self
.logger
.error("Error occurred while getting vCenter infromationn"\
3570 " for VM : {}".format(exp
))
3573 def get_vm_pci_details(self
, vmuuid
):
3575 Method to get VM PCI device details from vCenter
3578 vm_obj - vSphere VM object
3581 dict of PCI devives attached to VM
3584 vm_pci_devices_info
= {}
3586 vm_vcenter_info
= self
.get_vm_vcenter_info(vmuuid
)
3587 if vm_vcenter_info
["vm_moref_id"]:
3589 if hasattr(ssl
, '_create_unverified_context'):
3590 context
= ssl
._create
_unverified
_context
()
3591 vcenter_conect
= SmartConnect(host
=vm_vcenter_info
["vm_vcenter_ip"],
3592 user
=vm_vcenter_info
["vm_vcenter_user"],
3593 pwd
=vm_vcenter_info
["vm_vcenter_password"],
3594 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
3597 atexit
.register(Disconnect
, vcenter_conect
)
3598 content
= vcenter_conect
.RetrieveContent()
3600 #Get VM and its host
3602 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_vcenter_info
["vm_moref_id"])
3603 if host_obj
and vm_obj
:
3604 vm_pci_devices_info
["host_name"]= host_obj
.name
3605 vm_pci_devices_info
["host_ip"]= host_obj
.config
.network
.vnic
[0].spec
.ip
.ipAddress
3606 for device
in vm_obj
.config
.hardware
.device
:
3607 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
3608 device_details
={'devide_id':device
.backing
.id,
3609 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
,
3611 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
3613 self
.logger
.error("Can not connect to vCenter while getting "\
3614 "PCI devices infromationn")
3615 return vm_pci_devices_info
3616 except Exception as exp
:
3617 self
.logger
.error("Error occurred while getting VM infromationn"\
3618 " for VM : {}".format(exp
))
3619 raise vimconn
.vimconnException(message
=exp
)
3621 def add_network_adapter_to_vms(self
, vapp
, network_name
, primary_nic_index
, nicIndex
, nic_type
=None):
3623 Method to add network adapter type to vm
3625 network_name - name of network
3626 primary_nic_index - int value for primary nic index
3627 nicIndex - int value for nic index
3628 nic_type - specify model name to which add to vm
3632 vca
= self
.connect()
3634 raise vimconn
.vimconnConnectionException("Failed to connect vCloud director")
3638 for vms
in vapp
._get
_vms
():
3639 vm_id
= (vms
.id).split(':')[-1]
3641 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca
.host
, vm_id
)
3643 response
= Http
.get(url
=url_rest_call
,
3644 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3647 if response
.status_code
!= 200:
3648 self
.logger
.error("REST call {} failed reason : {}"\
3649 "status code : {}".format(url_rest_call
,
3651 response
.status_code
))
3652 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to get "\
3653 "network connection section")
3655 data
= response
.content
3656 if '<PrimaryNetworkConnectionIndex>' not in data
:
3657 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3658 <NetworkConnection network="{}">
3659 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3660 <IsConnected>true</IsConnected>
3661 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
3662 </NetworkConnection>""".format(primary_nic_index
, network_name
, nicIndex
)
3663 data
= data
.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item
))
3665 new_item
= """<NetworkConnection network="{}">
3666 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3667 <IsConnected>true</IsConnected>
3668 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
3669 </NetworkConnection>""".format(network_name
, nicIndex
)
3670 data
= data
.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item
))
3672 headers
= vca
.vcloud_session
.get_vcloud_headers()
3673 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3674 response
= Http
.put(url
=url_rest_call
, headers
=headers
, data
=data
,
3677 if response
.status_code
!= 202:
3678 self
.logger
.error("REST call {} failed reason : {}"\
3679 "status code : {} ".format(url_rest_call
,
3681 response
.status_code
))
3682 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to update "\
3683 "network connection section")
3685 nic_task
= taskType
.parseString(response
.content
, True)
3686 if isinstance(nic_task
, GenericTask
):
3687 vca
.block_until_completed(nic_task
)
3688 self
.logger
.info("add_network_adapter_to_vms(): VM {} conneced to "\
3689 "default NIC type".format(vm_id
))
3691 self
.logger
.error("add_network_adapter_to_vms(): VM {} failed to "\
3692 "connect NIC type".format(vm_id
))
3694 for vms
in vapp
._get
_vms
():
3695 vm_id
= (vms
.id).split(':')[-1]
3697 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca
.host
, vm_id
)
3699 response
= Http
.get(url
=url_rest_call
,
3700 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3703 if response
.status_code
!= 200:
3704 self
.logger
.error("REST call {} failed reason : {}"\
3705 "status code : {}".format(url_rest_call
,
3707 response
.status_code
))
3708 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to get "\
3709 "network connection section")
3710 data
= response
.content
3711 if '<PrimaryNetworkConnectionIndex>' not in data
:
3712 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3713 <NetworkConnection network="{}">
3714 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3715 <IsConnected>true</IsConnected>
3716 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
3717 <NetworkAdapterType>{}</NetworkAdapterType>
3718 </NetworkConnection>""".format(primary_nic_index
, network_name
, nicIndex
, nic_type
)
3719 data
= data
.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item
))
3721 new_item
= """<NetworkConnection network="{}">
3722 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3723 <IsConnected>true</IsConnected>
3724 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
3725 <NetworkAdapterType>{}</NetworkAdapterType>
3726 </NetworkConnection>""".format(network_name
, nicIndex
, nic_type
)
3727 data
= data
.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item
))
3729 headers
= vca
.vcloud_session
.get_vcloud_headers()
3730 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3731 response
= Http
.put(url
=url_rest_call
, headers
=headers
, data
=data
,
3735 if response
.status_code
!= 202:
3736 self
.logger
.error("REST call {} failed reason : {}"\
3737 "status code : {}".format(url_rest_call
,
3739 response
.status_code
))
3740 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to update "\
3741 "network connection section")
3743 nic_task
= taskType
.parseString(response
.content
, True)
3744 if isinstance(nic_task
, GenericTask
):
3745 vca
.block_until_completed(nic_task
)
3746 self
.logger
.info("add_network_adapter_to_vms(): VM {} "\
3747 "conneced to NIC type {}".format(vm_id
, nic_type
))
3749 self
.logger
.error("add_network_adapter_to_vms(): VM {} "\
3750 "failed to connect NIC type {}".format(vm_id
, nic_type
))
3751 except Exception as exp
:
3752 self
.logger
.error("add_network_adapter_to_vms() : exception occurred "\
3753 "while adding Network adapter")
3754 raise vimconn
.vimconnException(message
=exp
)
3757 def set_numa_affinity(self
, vmuuid
, paired_threads_id
):
3759 Method to assign numa affinity in vm configuration parammeters
3762 paired_threads_id - one or more virtual processor
3768 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vmuuid
)
3769 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3771 if hasattr(ssl
, '_create_unverified_context'):
3772 context
= ssl
._create
_unverified
_context
()
3773 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3774 pwd
=self
.passwd
, port
=int(vm_vcenter_port
),
3776 atexit
.register(Disconnect
, vcenter_conect
)
3777 content
= vcenter_conect
.RetrieveContent()
3779 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3781 config_spec
= vim
.vm
.ConfigSpec()
3782 config_spec
.extraConfig
= []
3783 opt
= vim
.option
.OptionValue()
3784 opt
.key
= 'numa.nodeAffinity'
3785 opt
.value
= str(paired_threads_id
)
3786 config_spec
.extraConfig
.append(opt
)
3787 task
= vm_obj
.ReconfigVM_Task(config_spec
)
3789 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3790 extra_config
= vm_obj
.config
.extraConfig
3792 for opts
in extra_config
:
3793 if 'numa.nodeAffinity' in opts
.key
:
3795 self
.logger
.info("set_numa_affinity: Sucessfully assign numa affinity "\
3796 "value {} for vm {}".format(opt
.value
, vm_obj
))
3800 self
.logger
.error("set_numa_affinity: Failed to assign numa affinity")
3801 except Exception as exp
:
3802 self
.logger
.error("set_numa_affinity : exception occurred while setting numa affinity "\
3803 "for VM {} : {}".format(vm_obj
, vm_moref_id
))
3804 raise vimconn
.vimconnException("set_numa_affinity : Error {} failed to assign numa "\
3805 "affinity".format(exp
))