12581d62f81acf0b816e23f5ea5727e4cc38966f
[osm/RO.git] / osm_ro / vimconn_vmware.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2016-2017 VMware Inc.
5 # This file is part of ETSI OSM
6 # All Rights Reserved.
7 #
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
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
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
18 # under the License.
19 #
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact: osslegalrouting@vmware.com
22 ##
23
24 """
25 vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
26 mbayramov@vmware.com
27 """
28 from progressbar import Percentage, Bar, ETA, FileTransferSpeed, ProgressBar
29
30 from . import vimconn
31 import os
32 import traceback
33 import itertools
34 import requests
35 import ssl
36 import atexit
37
38 from pyVmomi import vim, vmodl
39 from pyVim.connect import SmartConnect, Disconnect
40
41 from xml.etree import ElementTree as XmlElementTree
42 from lxml import etree as lxmlElementTree
43
44 import yaml
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
51
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
56
57 import logging
58 import json
59 import time
60 import uuid
61 import http.client
62 import hashlib
63 import socket
64 import struct
65 import netaddr
66 import random
67
68 # global variable for vcd connector type
69 STANDALONE = 'standalone'
70
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,
76 'dhcp_enabled':True,
77 'ip_version':"IPv4"
78 }
79 # global variable for wait time
80 INTERVAL_TIME = 5
81 MAX_WAIT_TIME = 1800
82
83 VCAVERSION = '5.9'
84
85 __author__ = "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
86 __date__ = "$12-Jan-2017 11:09:29$"
87 __version__ = '0.1'
88
89 # -1: "Could not be created",
90 # 0: "Unresolved",
91 # 1: "Resolved",
92 # 2: "Deployed",
93 # 3: "Suspended",
94 # 4: "Powered on",
95 # 5: "Waiting for user input",
96 # 6: "Unknown state",
97 # 7: "Unrecognized state",
98 # 8: "Powered off",
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"
106
107 # mapping vCD status to MANO
108 vcdStatusCode2manoFormat = {4: 'ACTIVE',
109 7: 'PAUSED',
110 3: 'SUSPENDED',
111 8: 'INACTIVE',
112 12: 'BUILD',
113 -1: 'ERROR',
114 14: 'DELETED'}
115
116 #
117 netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
118 'ERROR': 'ERROR', 'DELETED': 'DELETED'
119 }
120
121 class vimconnector(vimconn.vimconnector):
122 # dict used to store flavor in memory
123 flavorlist = {}
124
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={}):
127 """
128 Constructor create vmware connector to vCloud director.
129
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.
132
133 a) It initialize organization UUID
134 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
135
136 Args:
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.
145
146 password - is password for a user.
147
148 VMware connector also requires PVDC administrative privileges and separate account.
149 This variables must be passed via config argument dict contains keys
150
151 dict['admin_username']
152 dict['admin_password']
153 config - Provide NSX and vCenter information
154
155 Returns:
156 Nothing.
157 """
158
159 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url,
160 url_admin, user, passwd, log_level, config)
161
162 self.logger = logging.getLogger('openmano.vim.vmware')
163 self.logger.setLevel(10)
164 self.persistent_info = persistent_info
165
166 self.name = name
167 self.id = uuid
168 self.url = url
169 self.url_admin = url_admin
170 self.tenant_id = tenant_id
171 self.tenant_name = tenant_name
172 self.user = user
173 self.passwd = passwd
174 self.config = config
175 self.admin_password = None
176 self.admin_user = None
177 self.org_name = ""
178 self.nsx_manager = None
179 self.nsx_user = None
180 self.nsx_password = None
181
182 if tenant_name is not None:
183 orgnameandtenant = tenant_name.split(":")
184 if len(orgnameandtenant) == 2:
185 self.tenant_name = orgnameandtenant[1]
186 self.org_name = orgnameandtenant[0]
187 else:
188 self.tenant_name = tenant_name
189 if "orgname" in config:
190 self.org_name = config['orgname']
191
192 if log_level:
193 self.logger.setLevel(getattr(logging, log_level))
194
195 try:
196 self.admin_user = config['admin_username']
197 self.admin_password = config['admin_password']
198 except KeyError:
199 raise vimconn.vimconnException(message="Error admin username or admin password is empty.")
200
201 try:
202 self.nsx_manager = config['nsx_manager']
203 self.nsx_user = config['nsx_user']
204 self.nsx_password = config['nsx_password']
205 except KeyError:
206 raise vimconn.vimconnException(message="Error: nsx manager or nsx user or nsx password is empty in Config")
207
208 self.vcenter_ip = config.get("vcenter_ip", None)
209 self.vcenter_port = config.get("vcenter_port", None)
210 self.vcenter_user = config.get("vcenter_user", None)
211 self.vcenter_password = config.get("vcenter_password", None)
212
213 # ############# Stub code for SRIOV #################
214 # try:
215 # self.dvs_name = config['dv_switch_name']
216 # except KeyError:
217 # raise vimconn.vimconnException(message="Error: distributed virtaul switch name is empty in Config")
218 #
219 # self.vlanID_range = config.get("vlanID_range", None)
220
221 self.org_uuid = None
222 self.vca = None
223
224 if not url:
225 raise vimconn.vimconnException('url param can not be NoneType')
226
227 if not self.url_admin: # try to use normal url
228 self.url_admin = self.url
229
230 logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.org_name,
231 self.tenant_id, self.tenant_name))
232 logging.debug("vcd url {} vcd username: {} vcd password: {}".format(self.url, self.user, self.passwd))
233 logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password))
234
235 # initialize organization
236 if self.user is not None and self.passwd is not None and self.url:
237 self.init_organization()
238
239 def __getitem__(self, index):
240 if index == 'name':
241 return self.name
242 if index == 'tenant_id':
243 return self.tenant_id
244 if index == 'tenant_name':
245 return self.tenant_name
246 elif index == 'id':
247 return self.id
248 elif index == 'org_name':
249 return self.org_name
250 elif index == 'org_uuid':
251 return self.org_uuid
252 elif index == 'user':
253 return self.user
254 elif index == 'passwd':
255 return self.passwd
256 elif index == 'url':
257 return self.url
258 elif index == 'url_admin':
259 return self.url_admin
260 elif index == "config":
261 return self.config
262 else:
263 raise KeyError("Invalid key '%s'" % str(index))
264
265 def __setitem__(self, index, value):
266 if index == 'name':
267 self.name = value
268 if index == 'tenant_id':
269 self.tenant_id = value
270 if index == 'tenant_name':
271 self.tenant_name = value
272 elif index == 'id':
273 self.id = value
274 elif index == 'org_name':
275 self.org_name = value
276 elif index == 'org_uuid':
277 self.org_uuid = value
278 elif index == 'user':
279 self.user = value
280 elif index == 'passwd':
281 self.passwd = value
282 elif index == 'url':
283 self.url = value
284 elif index == 'url_admin':
285 self.url_admin = value
286 else:
287 raise KeyError("Invalid key '%s'" % str(index))
288
289 def connect_as_admin(self):
290 """ Method connect as pvdc admin user to vCloud director.
291 There are certain action that can be done only by provider vdc admin user.
292 Organization creation / provider network creation etc.
293
294 Returns:
295 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
296 """
297
298 self.logger.debug("Logging in to a vca {} as admin.".format(self.org_name))
299
300 vca_admin = VCA(host=self.url,
301 username=self.admin_user,
302 service_type=STANDALONE,
303 version=VCAVERSION,
304 verify=False,
305 log=False)
306 result = vca_admin.login(password=self.admin_password, org='System')
307 if not result:
308 raise vimconn.vimconnConnectionException(
309 "Can't connect to a vCloud director as: {}".format(self.admin_user))
310 result = vca_admin.login(token=vca_admin.token, org='System', org_url=vca_admin.vcloud_session.org_url)
311 if result is True:
312 self.logger.info(
313 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self.admin_user))
314
315 return vca_admin
316
317 def connect(self):
318 """ Method connect as normal user to vCloud director.
319
320 Returns:
321 The return vca object that letter can be used to connect to vCloud director as admin for VDC
322 """
323
324 try:
325 self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.org_name,
326 self.user,
327 self.org_name))
328 vca = VCA(host=self.url,
329 username=self.user,
330 service_type=STANDALONE,
331 version=VCAVERSION,
332 verify=False,
333 log=False)
334
335 result = vca.login(password=self.passwd, org=self.org_name)
336 if not result:
337 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user))
338 result = vca.login(token=vca.token, org=self.org_name, org_url=vca.vcloud_session.org_url)
339 if result is True:
340 self.logger.info(
341 "Successfully logged to a vcloud direct org: {} as user: {}".format(self.org_name, self.user))
342
343 except:
344 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
345 "{} as user: {}".format(self.org_name, self.user))
346
347 return vca
348
349 def init_organization(self):
350 """ Method initialize organization UUID and VDC parameters.
351
352 At bare minimum client must provide organization name that present in vCloud director and VDC.
353
354 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
355 The Org - UUID will be initialized at the run time if data center present in vCloud director.
356
357 Returns:
358 The return vca object that letter can be used to connect to vcloud direct as admin
359 """
360 vca = self.connect()
361 if not vca:
362 raise vimconn.vimconnConnectionException("self.connect() is failed.")
363
364 self.vca = vca
365 try:
366 if self.org_uuid is None:
367 org_dict = self.get_org_list()
368 for org in org_dict:
369 # we set org UUID at the init phase but we can do it only when we have valid credential.
370 if org_dict[org] == self.org_name:
371 self.org_uuid = org
372 self.logger.debug("Setting organization UUID {}".format(self.org_uuid))
373 break
374 else:
375 raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name))
376
377 # if well good we require for org details
378 org_details_dict = self.get_org(org_uuid=self.org_uuid)
379
380 # we have two case if we want to initialize VDC ID or VDC name at run time
381 # tenant_name provided but no tenant id
382 if self.tenant_id is None and self.tenant_name is not None and 'vdcs' in org_details_dict:
383 vdcs_dict = org_details_dict['vdcs']
384 for vdc in vdcs_dict:
385 if vdcs_dict[vdc] == self.tenant_name:
386 self.tenant_id = vdc
387 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
388 self.org_name))
389 break
390 else:
391 raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.")
392 # case two we have tenant_id but we don't have tenant name so we find and set it.
393 if self.tenant_id is not None and self.tenant_name is None and 'vdcs' in org_details_dict:
394 vdcs_dict = org_details_dict['vdcs']
395 for vdc in vdcs_dict:
396 if vdc == self.tenant_id:
397 self.tenant_name = vdcs_dict[vdc]
398 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
399 self.org_name))
400 break
401 else:
402 raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director")
403 self.logger.debug("Setting organization uuid {}".format(self.org_uuid))
404 except:
405 self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name))
406 self.logger.debug(traceback.format_exc())
407 self.org_uuid = None
408
409 def new_tenant(self, tenant_name=None, tenant_description=None):
410 """ Method adds a new tenant to VIM with this name.
411 This action requires access to create VDC action in vCloud director.
412
413 Args:
414 tenant_name is tenant_name to be created.
415 tenant_description not used for this call
416
417 Return:
418 returns the tenant identifier in UUID format.
419 If action is failed method will throw vimconn.vimconnException method
420 """
421 vdc_task = self.create_vdc(vdc_name=tenant_name)
422 if vdc_task is not None:
423 vdc_uuid, value = vdc_task.popitem()
424 self.logger.info("Crated new vdc {} and uuid: {}".format(tenant_name, vdc_uuid))
425 return vdc_uuid
426 else:
427 raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name))
428
429 def delete_tenant(self, tenant_id=None):
430 """ Delete a tenant from VIM
431 Args:
432 tenant_id is tenant_id to be deleted.
433
434 Return:
435 returns the tenant identifier in UUID format.
436 If action is failed method will throw exception
437 """
438 vca = self.connect_as_admin()
439 if not vca:
440 raise vimconn.vimconnConnectionException("self.connect() is failed")
441
442 if tenant_id is not None:
443 if vca.vcloud_session and vca.vcloud_session.organization:
444 #Get OrgVDC
445 url_list = [self.vca.host, '/api/vdc/', tenant_id]
446 orgvdc_herf = ''.join(url_list)
447 response = Http.get(url=orgvdc_herf,
448 headers=vca.vcloud_session.get_vcloud_headers(),
449 verify=vca.verify,
450 logger=vca.logger)
451
452 if response.status_code != requests.codes.ok:
453 self.logger.debug("delete_tenant():GET REST API call {} failed. "\
454 "Return status code {}".format(orgvdc_herf,
455 response.status_code))
456 raise vimconn.vimconnNotFoundException("Fail to get tenant {}".format(tenant_id))
457
458 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
459 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
460 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
461 vdc_remove_href = lxmlroot_respond.find("xmlns:Link[@rel='remove']",namespaces).attrib['href']
462 vdc_remove_href = vdc_remove_href + '?recursive=true&force=true'
463
464 #Delete OrgVDC
465 response = Http.delete(url=vdc_remove_href,
466 headers=vca.vcloud_session.get_vcloud_headers(),
467 verify=vca.verify,
468 logger=vca.logger)
469
470 if response.status_code == 202:
471 delete_vdc_task = taskType.parseString(response.content, True)
472 if type(delete_vdc_task) is GenericTask:
473 self.vca.block_until_completed(delete_vdc_task)
474 self.logger.info("Deleted tenant with ID {}".format(tenant_id))
475 return tenant_id
476 else:
477 self.logger.debug("delete_tenant(): DELETE REST API call {} failed. "\
478 "Return status code {}".format(vdc_remove_href,
479 response.status_code))
480 raise vimconn.vimconnException("Fail to delete tenant with ID {}".format(tenant_id))
481 else:
482 self.logger.debug("delete_tenant():Incorrect tenant ID {}".format(tenant_id))
483 raise vimconn.vimconnNotFoundException("Fail to get tenant {}".format(tenant_id))
484
485
486 def get_tenant_list(self, filter_dict={}):
487 """Obtain tenants of VIM
488 filter_dict can contain the following keys:
489 name: filter by tenant name
490 id: filter by tenant uuid/id
491 <other VIM specific>
492 Returns the tenant list of dictionaries:
493 [{'name':'<name>, 'id':'<id>, ...}, ...]
494
495 """
496 org_dict = self.get_org(self.org_uuid)
497 vdcs_dict = org_dict['vdcs']
498
499 vdclist = []
500 try:
501 for k in vdcs_dict:
502 entry = {'name': vdcs_dict[k], 'id': k}
503 # if caller didn't specify dictionary we return all tenants.
504 if filter_dict is not None and filter_dict:
505 filtered_entry = entry.copy()
506 filtered_dict = set(entry.keys()) - set(filter_dict)
507 for unwanted_key in filtered_dict: del entry[unwanted_key]
508 if filter_dict == entry:
509 vdclist.append(filtered_entry)
510 else:
511 vdclist.append(entry)
512 except:
513 self.logger.debug("Error in get_tenant_list()")
514 self.logger.debug(traceback.format_exc())
515 raise vimconn.vimconnException("Incorrect state. {}")
516
517 return vdclist
518
519 def new_network(self, net_name, net_type, ip_profile=None, shared=False):
520 """Adds a tenant network to VIM
521 net_name is the name
522 net_type can be 'bridge','data'.'ptp'.
523 ip_profile is a dict containing the IP parameters of the network
524 shared is a boolean
525 Returns the network identifier"""
526
527 self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
528 .format(net_name, net_type, ip_profile, shared))
529
530 isshared = 'false'
531 if shared:
532 isshared = 'true'
533
534 # ############# Stub code for SRIOV #################
535 # if net_type == "data" or net_type == "ptp":
536 # if self.config.get('dv_switch_name') == None:
537 # raise vimconn.vimconnConflictException("You must provide 'dv_switch_name' at config value")
538 # network_uuid = self.create_dvPort_group(net_name)
539
540 network_uuid = self.create_network(network_name=net_name, net_type=net_type,
541 ip_profile=ip_profile, isshared=isshared)
542 if network_uuid is not None:
543 return network_uuid
544 else:
545 raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
546
547 def get_vcd_network_list(self):
548 """ Method available organization for a logged in tenant
549
550 Returns:
551 The return vca object that letter can be used to connect to vcloud direct as admin
552 """
553
554 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
555
556 if not self.tenant_name:
557 raise vimconn.vimconnConnectionException("Tenant name is empty.")
558
559 vdc = self.get_vdc_details()
560 if vdc is None:
561 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self.tenant_name))
562
563 vdc_uuid = vdc.get_id().split(":")[3]
564 networks = self.vca.get_networks(vdc.get_name())
565 network_list = []
566 try:
567 for network in networks:
568 filter_dict = {}
569 netid = network.get_id().split(":")
570 if len(netid) != 4:
571 continue
572
573 filter_dict["name"] = network.get_name()
574 filter_dict["id"] = netid[3]
575 filter_dict["shared"] = network.get_IsShared()
576 filter_dict["tenant_id"] = vdc_uuid
577 if network.get_status() == 1:
578 filter_dict["admin_state_up"] = True
579 else:
580 filter_dict["admin_state_up"] = False
581 filter_dict["status"] = "ACTIVE"
582 filter_dict["type"] = "bridge"
583 network_list.append(filter_dict)
584 self.logger.debug("get_vcd_network_list adding entry {}".format(filter_dict))
585 except:
586 self.logger.debug("Error in get_vcd_network_list")
587 self.logger.debug(traceback.format_exc())
588 pass
589
590 self.logger.debug("get_vcd_network_list returning {}".format(network_list))
591 return network_list
592
593 def get_network_list(self, filter_dict={}):
594 """Obtain tenant networks of VIM
595 Filter_dict can be:
596 name: network name OR/AND
597 id: network uuid OR/AND
598 shared: boolean OR/AND
599 tenant_id: tenant OR/AND
600 admin_state_up: boolean
601 status: 'ACTIVE'
602
603 [{key : value , key : value}]
604
605 Returns the network list of dictionaries:
606 [{<the fields at Filter_dict plus some VIM specific>}, ...]
607 List can be empty
608 """
609
610 self.logger.debug("get_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
611
612 if not self.tenant_name:
613 raise vimconn.vimconnConnectionException("Tenant name is empty.")
614
615 vdc = self.get_vdc_details()
616 if vdc is None:
617 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self.tenant_name))
618
619 try:
620 vdcid = vdc.get_id().split(":")[3]
621 networks = self.vca.get_networks(vdc.get_name())
622 network_list = []
623
624 for network in networks:
625 filter_entry = {}
626 net_uuid = network.get_id().split(":")
627 if len(net_uuid) != 4:
628 continue
629 else:
630 net_uuid = net_uuid[3]
631 # create dict entry
632 self.logger.debug("Adding {} to a list vcd id {} network {}".format(net_uuid,
633 vdcid,
634 network.get_name()))
635 filter_entry["name"] = network.get_name()
636 filter_entry["id"] = net_uuid
637 filter_entry["shared"] = network.get_IsShared()
638 filter_entry["tenant_id"] = vdcid
639 if network.get_status() == 1:
640 filter_entry["admin_state_up"] = True
641 else:
642 filter_entry["admin_state_up"] = False
643 filter_entry["status"] = "ACTIVE"
644 filter_entry["type"] = "bridge"
645 filtered_entry = filter_entry.copy()
646
647 if filter_dict is not None and filter_dict:
648 # we remove all the key : value we don't care and match only
649 # respected field
650 filtered_dict = set(filter_entry.keys()) - set(filter_dict)
651 for unwanted_key in filtered_dict: del filter_entry[unwanted_key]
652 if filter_dict == filter_entry:
653 network_list.append(filtered_entry)
654 else:
655 network_list.append(filtered_entry)
656 except:
657 self.logger.debug("Error in get_vcd_network_list")
658 self.logger.debug(traceback.format_exc())
659
660 self.logger.debug("Returning {}".format(network_list))
661 return network_list
662
663 def get_network(self, net_id):
664 """Method obtains network details of net_id VIM network
665 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
666
667 try:
668 vdc = self.get_vdc_details()
669 vdc_id = vdc.get_id().split(":")[3]
670
671 networks = self.vca.get_networks(vdc.get_name())
672 filter_dict = {}
673
674 for network in networks:
675 vdc_network_id = network.get_id().split(":")
676 if len(vdc_network_id) == 4 and vdc_network_id[3] == net_id:
677 filter_dict["name"] = network.get_name()
678 filter_dict["id"] = vdc_network_id[3]
679 filter_dict["shared"] = network.get_IsShared()
680 filter_dict["tenant_id"] = vdc_id
681 if network.get_status() == 1:
682 filter_dict["admin_state_up"] = True
683 else:
684 filter_dict["admin_state_up"] = False
685 filter_dict["status"] = "ACTIVE"
686 filter_dict["type"] = "bridge"
687 self.logger.debug("Returning {}".format(filter_dict))
688 return filter_dict
689 except:
690 self.logger.debug("Error in get_network")
691 self.logger.debug(traceback.format_exc())
692
693 return filter_dict
694
695 def delete_network(self, net_id):
696 """
697 Method Deletes a tenant network from VIM, provide the network id.
698
699 Returns the network identifier or raise an exception
700 """
701
702 # ############# Stub code for SRIOV #################
703 # dvport_group = self.get_dvport_group(net_id)
704 # if dvport_group:
705 # #delete portgroup
706 # status = self.destroy_dvport_group(net_id)
707 # if status:
708 # # Remove vlanID from persistent info
709 # if net_id in self.persistent_info["used_vlanIDs"]:
710 # del self.persistent_info["used_vlanIDs"][net_id]
711 #
712 # return net_id
713
714 vcd_network = self.get_vcd_network(network_uuid=net_id)
715 if vcd_network is not None and vcd_network:
716 if self.delete_network_action(network_uuid=net_id):
717 return net_id
718 else:
719 raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
720
721 def refresh_nets_status(self, net_list):
722 """Get the status of the networks
723 Params: the list of network identifiers
724 Returns a dictionary with:
725 net_id: #VIM id of this network
726 status: #Mandatory. Text with one of:
727 # DELETED (not found at vim)
728 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
729 # OTHER (Vim reported other status not understood)
730 # ERROR (VIM indicates an ERROR status)
731 # ACTIVE, INACTIVE, DOWN (admin down),
732 # BUILD (on building process)
733 #
734 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
735 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
736
737 """
738
739 dict_entry = {}
740 try:
741 for net in net_list:
742 errormsg = ''
743 vcd_network = self.get_vcd_network(network_uuid=net)
744 if vcd_network is not None and vcd_network:
745 if vcd_network['status'] == '1':
746 status = 'ACTIVE'
747 else:
748 status = 'DOWN'
749 else:
750 status = 'DELETED'
751 errormsg = 'Network not found.'
752
753 dict_entry[net] = {'status': status, 'error_msg': errormsg,
754 'vim_info': yaml.safe_dump(vcd_network)}
755 except:
756 self.logger.debug("Error in refresh_nets_status")
757 self.logger.debug(traceback.format_exc())
758
759 return dict_entry
760
761 def get_flavor(self, flavor_id):
762 """Obtain flavor details from the VIM
763 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
764 """
765 if flavor_id not in vimconnector.flavorlist:
766 raise vimconn.vimconnNotFoundException("Flavor not found.")
767 return vimconnector.flavorlist[flavor_id]
768
769 def new_flavor(self, flavor_data):
770 """Adds a tenant flavor to VIM
771 flavor_data contains a dictionary with information, keys:
772 name: flavor name
773 ram: memory (cloud type) in MBytes
774 vpcus: cpus (cloud type)
775 extended: EPA parameters
776 - numas: #items requested in same NUMA
777 memory: number of 1G huge pages memory
778 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
779 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
780 - name: interface name
781 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
782 bandwidth: X Gbps; requested guarantee bandwidth
783 vpci: requested virtual PCI address
784 disk: disk size
785 is_public:
786 #TODO to concrete
787 Returns the flavor identifier"""
788
789 # generate a new uuid put to internal dict and return it.
790 self.logger.debug("Creating new flavor - flavor_data: {}".format(flavor_data))
791 new_flavor=flavor_data
792 ram = flavor_data.get(FLAVOR_RAM_KEY, 1024)
793 cpu = flavor_data.get(FLAVOR_VCPUS_KEY, 1)
794 disk = flavor_data.get(FLAVOR_DISK_KEY, 1)
795
796 if not isinstance(ram, int):
797 raise vimconn.vimconnException("Non-integer value for ram")
798 elif not isinstance(cpu, int):
799 raise vimconn.vimconnException("Non-integer value for cpu")
800 elif not isinstance(disk, int):
801 raise vimconn.vimconnException("Non-integer value for disk")
802
803 extended_flv = flavor_data.get("extended")
804 if extended_flv:
805 numas=extended_flv.get("numas")
806 if numas:
807 for numa in numas:
808 #overwrite ram and vcpus
809 ram = numa['memory']*1024
810 if 'paired-threads' in numa:
811 cpu = numa['paired-threads']*2
812 elif 'cores' in numa:
813 cpu = numa['cores']
814 elif 'threads' in numa:
815 cpu = numa['threads']
816
817 new_flavor[FLAVOR_RAM_KEY] = ram
818 new_flavor[FLAVOR_VCPUS_KEY] = cpu
819 new_flavor[FLAVOR_DISK_KEY] = disk
820 # generate a new uuid put to internal dict and return it.
821 flavor_id = uuid.uuid4()
822 vimconnector.flavorlist[str(flavor_id)] = new_flavor
823 self.logger.debug("Created flavor - {} : {}".format(flavor_id, new_flavor))
824
825 return str(flavor_id)
826
827 def delete_flavor(self, flavor_id):
828 """Deletes a tenant flavor from VIM identify by its id
829
830 Returns the used id or raise an exception
831 """
832 if flavor_id not in vimconnector.flavorlist:
833 raise vimconn.vimconnNotFoundException("Flavor not found.")
834
835 vimconnector.flavorlist.pop(flavor_id, None)
836 return flavor_id
837
838 def new_image(self, image_dict):
839 """
840 Adds a tenant image to VIM
841 Returns:
842 200, image-id if the image is created
843 <0, message if there is an error
844 """
845
846 return self.get_image_id_from_path(image_dict['location'])
847
848 def delete_image(self, image_id):
849 """
850 Deletes a tenant image from VIM
851 Args:
852 image_id is ID of Image to be deleted
853 Return:
854 returns the image identifier in UUID format or raises an exception on error
855 """
856 vca = self.connect_as_admin()
857 if not vca:
858 raise vimconn.vimconnConnectionException("self.connect() is failed")
859 # Get Catalog details
860 url_list = [self.vca.host, '/api/catalog/', image_id]
861 catalog_herf = ''.join(url_list)
862 response = Http.get(url=catalog_herf,
863 headers=vca.vcloud_session.get_vcloud_headers(),
864 verify=vca.verify,
865 logger=vca.logger)
866
867 if response.status_code != requests.codes.ok:
868 self.logger.debug("delete_image():GET REST API call {} failed. "\
869 "Return status code {}".format(catalog_herf,
870 response.status_code))
871 raise vimconn.vimconnNotFoundException("Fail to get image {}".format(image_id))
872
873 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
874 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
875 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
876
877 catalogItems_section = lxmlroot_respond.find("xmlns:CatalogItems",namespaces)
878 catalogItems = catalogItems_section.iterfind("xmlns:CatalogItem",namespaces)
879 for catalogItem in catalogItems:
880 catalogItem_href = catalogItem.attrib['href']
881
882 #GET details of catalogItem
883 response = Http.get(url=catalogItem_href,
884 headers=vca.vcloud_session.get_vcloud_headers(),
885 verify=vca.verify,
886 logger=vca.logger)
887
888 if response.status_code != requests.codes.ok:
889 self.logger.debug("delete_image():GET REST API call {} failed. "\
890 "Return status code {}".format(catalog_herf,
891 response.status_code))
892 raise vimconn.vimconnNotFoundException("Fail to get catalogItem {} for catalog {}".format(
893 catalogItem,
894 image_id))
895
896 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
897 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
898 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
899 catalogitem_remove_href = lxmlroot_respond.find("xmlns:Link[@rel='remove']",namespaces).attrib['href']
900
901 #Remove catalogItem
902 response = Http.delete(url= catalogitem_remove_href,
903 headers=vca.vcloud_session.get_vcloud_headers(),
904 verify=vca.verify,
905 logger=vca.logger)
906 if response.status_code == requests.codes.no_content:
907 self.logger.debug("Deleted Catalog item {}".format(catalogItem))
908 else:
909 raise vimconn.vimconnException("Fail to delete Catalog Item {}".format(catalogItem))
910
911 #Remove catalog
912 url_list = [self.vca.host, '/api/admin/catalog/', image_id]
913 catalog_remove_herf = ''.join(url_list)
914 response = Http.delete(url= catalog_remove_herf,
915 headers=vca.vcloud_session.get_vcloud_headers(),
916 verify=vca.verify,
917 logger=vca.logger)
918
919 if response.status_code == requests.codes.no_content:
920 self.logger.debug("Deleted Catalog {}".format(image_id))
921 return image_id
922 else:
923 raise vimconn.vimconnException("Fail to delete Catalog {}".format(image_id))
924
925
926 def catalog_exists(self, catalog_name, catalogs):
927 """
928
929 :param catalog_name:
930 :param catalogs:
931 :return:
932 """
933 for catalog in catalogs:
934 if catalog.name == catalog_name:
935 return True
936 return False
937
938 def create_vimcatalog(self, vca=None, catalog_name=None):
939 """ Create new catalog entry in vCloud director.
940
941 Args
942 vca: vCloud director.
943 catalog_name catalog that client wish to create. Note no validation done for a name.
944 Client must make sure that provide valid string representation.
945
946 Return (bool) True if catalog created.
947
948 """
949 try:
950 task = vca.create_catalog(catalog_name, catalog_name)
951 result = vca.block_until_completed(task)
952 if not result:
953 return False
954 catalogs = vca.get_catalogs()
955 except:
956 return False
957 return self.catalog_exists(catalog_name, catalogs)
958
959 # noinspection PyIncorrectDocstring
960 def upload_ovf(self, vca=None, catalog_name=None, image_name=None, media_file_name=None,
961 description='', progress=False, chunk_bytes=128 * 1024):
962 """
963 Uploads a OVF file to a vCloud catalog
964
965 :param chunk_bytes:
966 :param progress:
967 :param description:
968 :param image_name:
969 :param vca:
970 :param catalog_name: (str): The name of the catalog to upload the media.
971 :param media_file_name: (str): The name of the local media file to upload.
972 :return: (bool) True if the media file was successfully uploaded, false otherwise.
973 """
974 os.path.isfile(media_file_name)
975 statinfo = os.stat(media_file_name)
976
977 # find a catalog entry where we upload OVF.
978 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
979 # status change.
980 # if VCD can parse OVF we upload VMDK file
981 try:
982 for catalog in vca.get_catalogs():
983 if catalog_name != catalog.name:
984 continue
985 link = [link for link in catalog.get_Link() if link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
986 link.get_rel() == 'add']
987 assert len(link) == 1
988 data = """
989 <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>
990 """ % (escape(catalog_name), escape(description))
991 headers = vca.vcloud_session.get_vcloud_headers()
992 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
993 response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
994 if response.status_code == requests.codes.created:
995 catalogItem = XmlElementTree.fromstring(response.content)
996 entity = [child for child in catalogItem if
997 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
998 href = entity.get('href')
999 template = href
1000 response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
1001 verify=vca.verify, logger=self.logger)
1002
1003 if response.status_code == requests.codes.ok:
1004 media = mediaType.parseString(response.content, True)
1005 link = filter(lambda link: link.get_rel() == 'upload:default',
1006 media.get_Files().get_File()[0].get_Link())[0]
1007 headers = vca.vcloud_session.get_vcloud_headers()
1008 headers['Content-Type'] = 'Content-Type text/xml'
1009 response = Http.put(link.get_href(),
1010 data=open(media_file_name, 'rb'),
1011 headers=headers,
1012 verify=vca.verify, logger=self.logger)
1013 if response.status_code != requests.codes.ok:
1014 self.logger.debug(
1015 "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
1016 media_file_name))
1017 return False
1018
1019 # TODO fix this with aync block
1020 time.sleep(5)
1021
1022 self.logger.debug("vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
1023
1024 # uploading VMDK file
1025 # check status of OVF upload and upload remaining files.
1026 response = Http.get(template,
1027 headers=vca.vcloud_session.get_vcloud_headers(),
1028 verify=vca.verify,
1029 logger=self.logger)
1030
1031 if response.status_code == requests.codes.ok:
1032 media = mediaType.parseString(response.content, True)
1033 number_of_files = len(media.get_Files().get_File())
1034 for index in range(0, number_of_files):
1035 links_list = [link for link in media.get_Files().get_File()[index].get_Link() if link.get_rel() == 'upload:default']
1036 for link in links_list:
1037 # we skip ovf since it already uploaded.
1038 if 'ovf' in link.get_href():
1039 continue
1040 # The OVF file and VMDK must be in a same directory
1041 head, tail = os.path.split(media_file_name)
1042 file_vmdk = head + '/' + link.get_href().split("/")[-1]
1043 if not os.path.isfile(file_vmdk):
1044 return False
1045 statinfo = os.stat(file_vmdk)
1046 if statinfo.st_size == 0:
1047 return False
1048 hrefvmdk = link.get_href()
1049
1050 if progress:
1051 print(("Uploading file: {}".format(file_vmdk)))
1052 if progress:
1053 widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
1054 FileTransferSpeed()]
1055 progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
1056
1057 bytes_transferred = 0
1058 f = open(file_vmdk, 'rb')
1059 while bytes_transferred < statinfo.st_size:
1060 my_bytes = f.read(chunk_bytes)
1061 if len(my_bytes) <= chunk_bytes:
1062 headers = vca.vcloud_session.get_vcloud_headers()
1063 headers['Content-Range'] = 'bytes %s-%s/%s' % (
1064 bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
1065 headers['Content-Length'] = str(len(my_bytes))
1066 response = Http.put(hrefvmdk,
1067 headers=headers,
1068 data=my_bytes,
1069 verify=vca.verify,
1070 logger=None)
1071
1072 if response.status_code == requests.codes.ok:
1073 bytes_transferred += len(my_bytes)
1074 if progress:
1075 progress_bar.update(bytes_transferred)
1076 else:
1077 self.logger.debug(
1078 'file upload failed with error: [%s] %s' % (response.status_code,
1079 response.content))
1080
1081 f.close()
1082 return False
1083 f.close()
1084 if progress:
1085 progress_bar.finish()
1086 time.sleep(10)
1087 return True
1088 else:
1089 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
1090 format(catalog_name, media_file_name))
1091 return False
1092 except Exception as exp:
1093 self.logger.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
1094 .format(catalog_name,media_file_name, exp))
1095 raise vimconn.vimconnException(
1096 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
1097 .format(catalog_name,media_file_name, exp))
1098
1099 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
1100 return False
1101
1102 def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False):
1103 """Upload media file"""
1104 # TODO add named parameters for readability
1105
1106 return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0],
1107 media_file_name=medial_file_name, description='medial_file_name', progress=progress)
1108
1109 def validate_uuid4(self, uuid_string=None):
1110 """ Method validate correct format of UUID.
1111
1112 Return: true if string represent valid uuid
1113 """
1114 try:
1115 val = uuid.UUID(uuid_string, version=4)
1116 except ValueError:
1117 return False
1118 return True
1119
1120 def get_catalogid(self, catalog_name=None, catalogs=None):
1121 """ Method check catalog and return catalog ID in UUID format.
1122
1123 Args
1124 catalog_name: catalog name as string
1125 catalogs: list of catalogs.
1126
1127 Return: catalogs uuid
1128 """
1129
1130 for catalog in catalogs:
1131 if catalog.name == catalog_name:
1132 catalog_id = catalog.get_id().split(":")
1133 return catalog_id[3]
1134 return None
1135
1136 def get_catalogbyid(self, catalog_uuid=None, catalogs=None):
1137 """ Method check catalog and return catalog name lookup done by catalog UUID.
1138
1139 Args
1140 catalog_name: catalog name as string
1141 catalogs: list of catalogs.
1142
1143 Return: catalogs name or None
1144 """
1145
1146 if not self.validate_uuid4(uuid_string=catalog_uuid):
1147 return None
1148
1149 for catalog in catalogs:
1150 catalog_id = catalog.get_id().split(":")[3]
1151 if catalog_id == catalog_uuid:
1152 return catalog.name
1153 return None
1154
1155 def get_catalog_obj(self, catalog_uuid=None, catalogs=None):
1156 """ Method check catalog and return catalog name lookup done by catalog UUID.
1157
1158 Args
1159 catalog_name: catalog name as string
1160 catalogs: list of catalogs.
1161
1162 Return: catalogs name or None
1163 """
1164
1165 if not self.validate_uuid4(uuid_string=catalog_uuid):
1166 return None
1167
1168 for catalog in catalogs:
1169 catalog_id = catalog.get_id().split(":")[3]
1170 if catalog_id == catalog_uuid:
1171 return catalog
1172 return None
1173
1174 def get_image_id_from_path(self, path=None, progress=False):
1175 """ Method upload OVF image to vCloud director.
1176
1177 Each OVF image represented as single catalog entry in vcloud director.
1178 The method check for existing catalog entry. The check done by file name without file extension.
1179
1180 if given catalog name already present method will respond with existing catalog uuid otherwise
1181 it will create new catalog entry and upload OVF file to newly created catalog.
1182
1183 If method can't create catalog entry or upload a file it will throw exception.
1184
1185 Method accept boolean flag progress that will output progress bar. It useful method
1186 for standalone upload use case. In case to test large file upload.
1187
1188 Args
1189 path: - valid path to OVF file.
1190 progress - boolean progress bar show progress bar.
1191
1192 Return: if image uploaded correct method will provide image catalog UUID.
1193 """
1194
1195 if not path:
1196 raise vimconn.vimconnException("Image path can't be None.")
1197
1198 if not os.path.isfile(path):
1199 raise vimconn.vimconnException("Can't read file. File not found.")
1200
1201 if not os.access(path, os.R_OK):
1202 raise vimconn.vimconnException("Can't read file. Check file permission to read.")
1203
1204 self.logger.debug("get_image_id_from_path() client requesting {} ".format(path))
1205
1206 dirpath, filename = os.path.split(path)
1207 flname, file_extension = os.path.splitext(path)
1208 if file_extension != '.ovf':
1209 self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
1210 raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.")
1211
1212 catalog_name = os.path.splitext(filename)[0]
1213 catalog_md5_name = hashlib.md5(path).hexdigest()
1214 self.logger.debug("File name {} Catalog Name {} file path {} "
1215 "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name))
1216
1217 try:
1218 catalogs = self.vca.get_catalogs()
1219 except Exception as exp:
1220 self.logger.debug("Failed get catalogs() with Exception {} ".format(exp))
1221 raise vimconn.vimconnException("Failed get catalogs() with Exception {} ".format(exp))
1222
1223 if len(catalogs) == 0:
1224 self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
1225 result = self.create_vimcatalog(self.vca, catalog_md5_name)
1226 if not result:
1227 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1228 result = self.upload_vimimage(vca=self.vca, catalog_name=catalog_md5_name,
1229 media_name=filename, medial_file_name=path, progress=progress)
1230 if not result:
1231 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name))
1232 return self.get_catalogid(catalog_name, self.vca.get_catalogs())
1233 else:
1234 for catalog in catalogs:
1235 # search for existing catalog if we find same name we return ID
1236 # TODO optimize this
1237 if catalog.name == catalog_md5_name:
1238 self.logger.debug("Found existing catalog entry for {} "
1239 "catalog id {}".format(catalog_name,
1240 self.get_catalogid(catalog_md5_name, catalogs)))
1241 return self.get_catalogid(catalog_md5_name, self.vca.get_catalogs())
1242
1243 # if we didn't find existing catalog we create a new one and upload image.
1244 self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name))
1245 result = self.create_vimcatalog(self.vca, catalog_md5_name)
1246 if not result:
1247 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1248
1249 result = self.upload_vimimage(vca=self.vca, catalog_name=catalog_md5_name,
1250 media_name=filename, medial_file_name=path, progress=progress)
1251 if not result:
1252 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name))
1253
1254 return self.get_catalogid(catalog_md5_name, self.vca.get_catalogs())
1255
1256 def get_image_list(self, filter_dict={}):
1257 '''Obtain tenant images from VIM
1258 Filter_dict can be:
1259 name: image name
1260 id: image uuid
1261 checksum: image checksum
1262 location: image path
1263 Returns the image list of dictionaries:
1264 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1265 List can be empty
1266 '''
1267
1268 try:
1269 image_list = []
1270 catalogs = self.vca.get_catalogs()
1271 if len(catalogs) == 0:
1272 return image_list
1273 else:
1274 for catalog in catalogs:
1275 catalog_uuid = catalog.get_id().split(":")[3]
1276 name = catalog.name
1277 filtered_dict = {}
1278 if filter_dict.get("name") and filter_dict["name"] != name:
1279 continue
1280 if filter_dict.get("id") and filter_dict["id"] != catalog_uuid:
1281 continue
1282 filtered_dict ["name"] = name
1283 filtered_dict ["id"] = catalog_uuid
1284 image_list.append(filtered_dict)
1285
1286 self.logger.debug("List of already created catalog items: {}".format(image_list))
1287 return image_list
1288 except Exception as exp:
1289 raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp))
1290
1291 def get_vappid(self, vdc=None, vapp_name=None):
1292 """ Method takes vdc object and vApp name and returns vapp uuid or None
1293
1294 Args:
1295 vdc: The VDC object.
1296 vapp_name: is application vappp name identifier
1297
1298 Returns:
1299 The return vApp name otherwise None
1300 """
1301 if vdc is None or vapp_name is None:
1302 return None
1303 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1304 try:
1305 refs = [ref for ref in vdc.ResourceEntities.ResourceEntity if ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml']
1306 if len(refs) == 1:
1307 return refs[0].href.split("vapp")[1][1:]
1308 except Exception as e:
1309 self.logger.exception(e)
1310 return False
1311 return None
1312
1313 def check_vapp(self, vdc=None, vapp_uuid=None):
1314 """ Method Method returns True or False if vapp deployed in vCloud director
1315
1316 Args:
1317 vca: Connector to VCA
1318 vdc: The VDC object.
1319 vappid: vappid is application identifier
1320
1321 Returns:
1322 The return True if vApp deployed
1323 :param vdc:
1324 :param vapp_uuid:
1325 """
1326 try:
1327 refs = [ref for ref in vdc.ResourceEntities.ResourceEntity if ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml']
1328 for ref in refs:
1329 vappid = ref.href.split("vapp")[1][1:]
1330 # find vapp with respected vapp uuid
1331 if vappid == vapp_uuid:
1332 return True
1333 except Exception as e:
1334 self.logger.exception(e)
1335 return False
1336 return False
1337
1338 def get_namebyvappid(self, vdc=None, vapp_uuid=None):
1339 """Method returns vApp name from vCD and lookup done by vapp_id.
1340
1341 Args:
1342 vca: Connector to VCA
1343 vdc: The VDC object.
1344 vapp_uuid: vappid is application identifier
1345
1346 Returns:
1347 The return vApp name otherwise None
1348 """
1349
1350 try:
1351 refs = [ref for ref in vdc.ResourceEntities.ResourceEntity if ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml']
1352 for ref in refs:
1353 # we care only about UUID the rest doesn't matter
1354 vappid = ref.href.split("vapp")[1][1:]
1355 if vappid == vapp_uuid:
1356 response = Http.get(ref.href, headers=self.vca.vcloud_session.get_vcloud_headers(), verify=self.vca.verify,
1357 logger=self.logger)
1358
1359 #Retry login if session expired & retry sending request
1360 if response.status_code == 403:
1361 response = self.retry_rest('GET', ref.href)
1362
1363 tree = XmlElementTree.fromstring(response.content)
1364 return tree.attrib['name']
1365 except Exception as e:
1366 self.logger.exception(e)
1367 return None
1368 return None
1369
1370 def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list=[],
1371 cloud_config=None, disk_list=None, availability_zone_index=None, availability_zone_list=None):
1372 """Adds a VM instance to VIM
1373 Params:
1374 'start': (boolean) indicates if VM must start or created in pause mode.
1375 'image_id','flavor_id': image and flavor VIM id to use for the VM
1376 'net_list': list of interfaces, each one is a dictionary with:
1377 'name': (optional) name for the interface.
1378 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
1379 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities
1380 'model': (optional and only have sense for type==virtual) interface model: virtio, e2000, ...
1381 'mac_address': (optional) mac address to assign to this interface
1382 #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided,
1383 the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF
1384 'type': (mandatory) can be one of:
1385 'virtual', in this case always connected to a network of type 'net_type=bridge'
1386 'PF' (passthrough): depending on VIM capabilities it can be connected to a data/ptp network ot it
1387 can created unconnected
1388 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
1389 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
1390 are allocated on the same physical NIC
1391 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
1392 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
1393 or True, it must apply the default VIM behaviour
1394 After execution the method will add the key:
1395 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
1396 interface. 'net_list' is modified
1397 'cloud_config': (optional) dictionary with:
1398 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
1399 'users': (optional) list of users to be inserted, each item is a dict with:
1400 'name': (mandatory) user name,
1401 'key-pairs': (optional) list of strings with the public key to be inserted to the user
1402 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
1403 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
1404 'config-files': (optional). List of files to be transferred. Each item is a dict with:
1405 'dest': (mandatory) string with the destination absolute path
1406 'encoding': (optional, by default text). Can be one of:
1407 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
1408 'content' (mandatory): string with the content of the file
1409 'permissions': (optional) string with file permissions, typically octal notation '0644'
1410 'owner': (optional) file owner, string with the format 'owner:group'
1411 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
1412 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
1413 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
1414 'size': (mandatory) string with the size of the disk in GB
1415 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
1416 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
1417 availability_zone_index is None
1418 Returns the instance identifier or raises an exception on error
1419 """
1420 self.logger.info("Creating new instance for entry {}".format(name))
1421 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {} disk_list {}".format(
1422 description, start, image_id, flavor_id, net_list, cloud_config, disk_list))
1423
1424 #new vm name = vmname + tenant_id + uuid
1425 new_vm_name = [name, '-', str(uuid.uuid4())]
1426 vmname_andid = ''.join(new_vm_name)
1427
1428 # if vm already deployed we return existing uuid
1429 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1430 # if vapp_uuid is not None:
1431 # return vapp_uuid
1432
1433 # we check for presence of VDC, Catalog entry and Flavor.
1434 vdc = self.get_vdc_details()
1435 if vdc is None:
1436 raise vimconn.vimconnNotFoundException(
1437 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
1438 catalogs = self.vca.get_catalogs()
1439 if catalogs is None:
1440 #Retry once, if failed by refreshing token
1441 self.get_token()
1442 catalogs = self.vca.get_catalogs()
1443 if catalogs is None:
1444 raise vimconn.vimconnNotFoundException(
1445 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
1446
1447 catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1448 if catalog_hash_name:
1449 self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
1450 else:
1451 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1452 "(Failed retrieve catalog information {})".format(name, image_id))
1453
1454
1455 # Set vCPU and Memory based on flavor.
1456 vm_cpus = None
1457 vm_memory = None
1458 vm_disk = None
1459 numas = None
1460
1461 if flavor_id is not None:
1462 if flavor_id not in vimconnector.flavorlist:
1463 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1464 "Failed retrieve flavor information "
1465 "flavor id {}".format(name, flavor_id))
1466 else:
1467 try:
1468 flavor = vimconnector.flavorlist[flavor_id]
1469 vm_cpus = flavor[FLAVOR_VCPUS_KEY]
1470 vm_memory = flavor[FLAVOR_RAM_KEY]
1471 vm_disk = flavor[FLAVOR_DISK_KEY]
1472 extended = flavor.get("extended", None)
1473 if extended:
1474 numas=extended.get("numas", None)
1475
1476 except Exception as exp:
1477 raise vimconn.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id, exp))
1478
1479 # image upload creates template name as catalog name space Template.
1480 templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1481 power_on = 'false'
1482 if start:
1483 power_on = 'true'
1484
1485 # client must provide at least one entry in net_list if not we report error
1486 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1487 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1488 primary_net = None
1489 primary_netname = None
1490 network_mode = 'bridged'
1491 if net_list is not None and len(net_list) > 0:
1492 for net in net_list:
1493 if 'use' in net and net['use'] == 'mgmt' and not primary_net:
1494 primary_net = net
1495 if primary_net is None:
1496 primary_net = net_list[0]
1497
1498 try:
1499 primary_net_id = primary_net['net_id']
1500 network_dict = self.get_vcd_network(network_uuid=primary_net_id)
1501 if 'name' in network_dict:
1502 primary_netname = network_dict['name']
1503
1504 except KeyError:
1505 raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
1506 else:
1507 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
1508
1509 # use: 'data', 'bridge', 'mgmt'
1510 # create vApp. Set vcpu and ram based on flavor id.
1511 try:
1512 for retry in (1,2):
1513 vapptask = self.vca.create_vapp(self.tenant_name, vmname_andid, templateName,
1514 self.get_catalogbyid(image_id, catalogs),
1515 network_name=None, # None while creating vapp
1516 network_mode=network_mode,
1517 vm_name=vmname_andid,
1518 vm_cpus=vm_cpus, # can be None if flavor is None
1519 vm_memory=vm_memory) # can be None if flavor is None
1520
1521 if not vapptask and retry==1:
1522 self.get_token() # Retry getting token
1523 continue
1524 else:
1525 break
1526
1527 if vapptask is None or vapptask is False:
1528 raise vimconn.vimconnUnexpectedResponse(
1529 "new_vminstance(): failed to create vApp {}".format(vmname_andid))
1530 if type(vapptask) is VappTask:
1531 self.vca.block_until_completed(vapptask)
1532
1533 except Exception as exp:
1534 raise vimconn.vimconnUnexpectedResponse(
1535 "new_vminstance(): failed to create vApp {} with Exception:{}".format(vmname_andid, exp))
1536
1537 # we should have now vapp in undeployed state.
1538 try:
1539 vapp_uuid = self.get_vappid(self.get_vdc_details(), vmname_andid)
1540
1541 except Exception as exp:
1542 raise vimconn.vimconnUnexpectedResponse(
1543 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1544 .format(vmname_andid, exp))
1545
1546 if vapp_uuid is None:
1547 raise vimconn.vimconnUnexpectedResponse(
1548 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
1549 vmname_andid))
1550
1551 #Add PCI passthrough/SRIOV configrations
1552 vm_obj = None
1553 pci_devices_info = []
1554 sriov_net_info = []
1555 reserve_memory = False
1556
1557 for net in net_list:
1558 if net["type"]=="PF":
1559 pci_devices_info.append(net)
1560 elif (net["type"]=="VF" or net["type"]=="VFnotShared") and 'net_id'in net:
1561 sriov_net_info.append(net)
1562
1563 #Add PCI
1564 if len(pci_devices_info) > 0:
1565 self.logger.info("Need to add PCI devices {} into VM {}".format(pci_devices_info,
1566 vmname_andid ))
1567 PCI_devices_status, vm_obj, vcenter_conect = self.add_pci_devices(vapp_uuid,
1568 pci_devices_info,
1569 vmname_andid)
1570 if PCI_devices_status:
1571 self.logger.info("Added PCI devives {} to VM {}".format(
1572 pci_devices_info,
1573 vmname_andid)
1574 )
1575 reserve_memory = True
1576 else:
1577 self.logger.info("Fail to add PCI devives {} to VM {}".format(
1578 pci_devices_info,
1579 vmname_andid)
1580 )
1581
1582 vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid)
1583 # Modify vm disk
1584 if vm_disk:
1585 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1586 result = self.modify_vm_disk(vapp_uuid, vm_disk)
1587 if result :
1588 self.logger.debug("Modified Disk size of VM {} ".format(vmname_andid))
1589
1590 #Add new or existing disks to vApp
1591 if disk_list:
1592 added_existing_disk = False
1593 for disk in disk_list:
1594 if 'device_type' in disk and disk['device_type'] == 'cdrom':
1595 image_id = disk['image_id']
1596 # Adding CD-ROM to VM
1597 # will revisit code once specification ready to support this feature
1598 self.insert_media_to_vm(vapp, image_id)
1599 elif "image_id" in disk and disk["image_id"] is not None:
1600 self.logger.debug("Adding existing disk from image {} to vm {} ".format(
1601 disk["image_id"] , vapp_uuid))
1602 self.add_existing_disk(catalogs=catalogs,
1603 image_id=disk["image_id"],
1604 size = disk["size"],
1605 template_name=templateName,
1606 vapp_uuid=vapp_uuid
1607 )
1608 added_existing_disk = True
1609 else:
1610 #Wait till added existing disk gets reflected into vCD database/API
1611 if added_existing_disk:
1612 time.sleep(5)
1613 added_existing_disk = False
1614 self.add_new_disk(vapp_uuid, disk['size'])
1615
1616 if numas:
1617 # Assigning numa affinity setting
1618 for numa in numas:
1619 if 'paired-threads-id' in numa:
1620 paired_threads_id = numa['paired-threads-id']
1621 self.set_numa_affinity(vapp_uuid, paired_threads_id)
1622
1623 # add NICs & connect to networks in netlist
1624 try:
1625 self.logger.info("Request to connect VM to a network: {}".format(net_list))
1626 nicIndex = 0
1627 primary_nic_index = 0
1628 for net in net_list:
1629 # openmano uses network id in UUID format.
1630 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1631 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1632 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1633
1634 if 'net_id' not in net:
1635 continue
1636
1637 #Using net_id as a vim_id i.e. vim interface id, as do not have saperate vim interface id
1638 #Same will be returned in refresh_vms_status() as vim_interface_id
1639 net['vim_id'] = net['net_id'] # Provide the same VIM identifier as the VIM network
1640
1641 interface_net_id = net['net_id']
1642 interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
1643 interface_network_mode = net['use']
1644
1645 if interface_network_mode == 'mgmt':
1646 primary_nic_index = nicIndex
1647
1648 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1649 - DHCP (The IP address is obtained from a DHCP service.)
1650 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1651 - NONE (No IP addressing mode specified.)"""
1652
1653 if primary_netname is not None:
1654 nets = [n for n in self.vca.get_networks(self.tenant_name) if n.name == interface_net_name]
1655 if len(nets) == 1:
1656 self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name))
1657
1658 vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid)
1659 task = vapp.connect_to_network(nets[0].name, nets[0].href)
1660 if type(task) is GenericTask:
1661 self.vca.block_until_completed(task)
1662 # connect network to VM - with all DHCP by default
1663
1664 type_list = ['PF','VF','VFnotShared']
1665 if 'type' in net and net['type'] not in type_list:
1666 # fetching nic type from vnf
1667 if 'model' in net:
1668 nic_type = net['model']
1669 self.logger.info("new_vminstance(): adding network adapter "\
1670 "to a network {}".format(nets[0].name))
1671 self.add_network_adapter_to_vms(vapp, nets[0].name,
1672 primary_nic_index,
1673 nicIndex,
1674 net,
1675 nic_type=nic_type)
1676 else:
1677 self.logger.info("new_vminstance(): adding network adapter "\
1678 "to a network {}".format(nets[0].name))
1679 self.add_network_adapter_to_vms(vapp, nets[0].name,
1680 primary_nic_index,
1681 nicIndex,
1682 net)
1683 nicIndex += 1
1684
1685 vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid)
1686 # cloud-init for ssh-key injection
1687 if cloud_config:
1688 self.cloud_init(vapp,cloud_config)
1689
1690 # deploy and power on vm
1691 self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name))
1692 deploytask = vapp.deploy(powerOn=False)
1693 if type(deploytask) is GenericTask:
1694 self.vca.block_until_completed(deploytask)
1695
1696 # ############# Stub code for SRIOV #################
1697 #Add SRIOV
1698 # if len(sriov_net_info) > 0:
1699 # self.logger.info("Need to add SRIOV adapters {} into VM {}".format(sriov_net_info,
1700 # vmname_andid ))
1701 # sriov_status, vm_obj, vcenter_conect = self.add_sriov(vapp_uuid,
1702 # sriov_net_info,
1703 # vmname_andid)
1704 # if sriov_status:
1705 # self.logger.info("Added SRIOV {} to VM {}".format(
1706 # sriov_net_info,
1707 # vmname_andid)
1708 # )
1709 # reserve_memory = True
1710 # else:
1711 # self.logger.info("Fail to add SRIOV {} to VM {}".format(
1712 # sriov_net_info,
1713 # vmname_andid)
1714 # )
1715
1716 # If VM has PCI devices or SRIOV reserve memory for VM
1717 if reserve_memory:
1718 memReserve = vm_obj.config.hardware.memoryMB
1719 spec = vim.vm.ConfigSpec()
1720 spec.memoryAllocation = vim.ResourceAllocationInfo(reservation=memReserve)
1721 task = vm_obj.ReconfigVM_Task(spec=spec)
1722 if task:
1723 result = self.wait_for_vcenter_task(task, vcenter_conect)
1724 self.logger.info("Reserved memory {} MB for "
1725 "VM VM status: {}".format(str(memReserve), result))
1726 else:
1727 self.logger.info("Fail to reserved memory {} to VM {}".format(
1728 str(memReserve), str(vm_obj)))
1729
1730 self.logger.debug("new_vminstance(): power on vApp {} ".format(name))
1731
1732 vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid)
1733 poweron_task = vapp.poweron()
1734 if type(poweron_task) is GenericTask:
1735 self.vca.block_until_completed(poweron_task)
1736
1737 except Exception as exp :
1738 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1739 self.logger.debug("new_vminstance(): Failed create new vm instance {} with exception {}"
1740 .format(name, exp))
1741 raise vimconn.vimconnException("new_vminstance(): Failed create new vm instance {} with exception {}"
1742 .format(name, exp))
1743
1744 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1745 wait_time = 0
1746 vapp_uuid = None
1747 while wait_time <= MAX_WAIT_TIME:
1748 try:
1749 vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid)
1750 except Exception as exp:
1751 raise vimconn.vimconnUnexpectedResponse(
1752 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1753 .format(vmname_andid, exp))
1754
1755 if vapp and vapp.me.deployed:
1756 vapp_uuid = self.get_vappid(self.get_vdc_details(), vmname_andid)
1757 break
1758 else:
1759 self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
1760 time.sleep(INTERVAL_TIME)
1761
1762 wait_time +=INTERVAL_TIME
1763
1764 if vapp_uuid is not None:
1765 return vapp_uuid
1766 else:
1767 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1768
1769 ##
1770 ##
1771 ## based on current discussion
1772 ##
1773 ##
1774 ## server:
1775 # created: '2016-09-08T11:51:58'
1776 # description: simple-instance.linux1.1
1777 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1778 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1779 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1780 # status: ACTIVE
1781 # error_msg:
1782 # interfaces: …
1783 #
1784 def get_vminstance(self, vim_vm_uuid=None):
1785 """Returns the VM instance information from VIM"""
1786
1787 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
1788
1789 vdc = self.get_vdc_details()
1790 if vdc is None:
1791 raise vimconn.vimconnConnectionException(
1792 "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1793
1794 vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
1795 if not vm_info_dict:
1796 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1797 raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1798
1799 status_key = vm_info_dict['status']
1800 error = ''
1801 try:
1802 vm_dict = {'created': vm_info_dict['created'],
1803 'description': vm_info_dict['name'],
1804 'status': vcdStatusCode2manoFormat[int(status_key)],
1805 'hostId': vm_info_dict['vmuuid'],
1806 'error_msg': error,
1807 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1808
1809 if 'interfaces' in vm_info_dict:
1810 vm_dict['interfaces'] = vm_info_dict['interfaces']
1811 else:
1812 vm_dict['interfaces'] = []
1813 except KeyError:
1814 vm_dict = {'created': '',
1815 'description': '',
1816 'status': vcdStatusCode2manoFormat[int(-1)],
1817 'hostId': vm_info_dict['vmuuid'],
1818 'error_msg': "Inconsistency state",
1819 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1820
1821 return vm_dict
1822
1823 def delete_vminstance(self, vm__vim_uuid):
1824 """Method poweroff and remove VM instance from vcloud director network.
1825
1826 Args:
1827 vm__vim_uuid: VM UUID
1828
1829 Returns:
1830 Returns the instance identifier
1831 """
1832
1833 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
1834
1835 vdc = self.get_vdc_details()
1836 if vdc is None:
1837 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1838 self.tenant_name))
1839 raise vimconn.vimconnException(
1840 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1841
1842 try:
1843 vapp_name = self.get_namebyvappid(vdc, vm__vim_uuid)
1844 if vapp_name is None:
1845 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1846 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1847 else:
1848 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1849
1850 # Delete vApp and wait for status change if task executed and vApp is None.
1851 vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name)
1852
1853 if vapp:
1854 if vapp.me.deployed:
1855 self.logger.info("Powering off vApp {}".format(vapp_name))
1856 #Power off vApp
1857 powered_off = False
1858 wait_time = 0
1859 while wait_time <= MAX_WAIT_TIME:
1860 vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name)
1861 if not vapp:
1862 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1863 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1864
1865 power_off_task = vapp.poweroff()
1866 if type(power_off_task) is GenericTask:
1867 result = self.vca.block_until_completed(power_off_task)
1868 if result:
1869 powered_off = True
1870 break
1871 else:
1872 self.logger.info("Wait for vApp {} to power off".format(vapp_name))
1873 time.sleep(INTERVAL_TIME)
1874
1875 wait_time +=INTERVAL_TIME
1876 if not powered_off:
1877 self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
1878 else:
1879 self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
1880
1881 #Undeploy vApp
1882 self.logger.info("Undeploy vApp {}".format(vapp_name))
1883 wait_time = 0
1884 undeployed = False
1885 while wait_time <= MAX_WAIT_TIME:
1886 vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name)
1887 if not vapp:
1888 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1889 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1890 undeploy_task = vapp.undeploy(action='powerOff')
1891
1892 if type(undeploy_task) is GenericTask:
1893 result = self.vca.block_until_completed(undeploy_task)
1894 if result:
1895 undeployed = True
1896 break
1897 else:
1898 self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
1899 time.sleep(INTERVAL_TIME)
1900
1901 wait_time +=INTERVAL_TIME
1902
1903 if not undeployed:
1904 self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
1905
1906 # delete vapp
1907 self.logger.info("Start deletion of vApp {} ".format(vapp_name))
1908 vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name)
1909
1910 if vapp is not None:
1911 wait_time = 0
1912 result = False
1913
1914 while wait_time <= MAX_WAIT_TIME:
1915 vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name)
1916 if not vapp:
1917 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1918 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1919
1920 delete_task = vapp.delete()
1921
1922 if type(delete_task) is GenericTask:
1923 self.vca.block_until_completed(delete_task)
1924 result = self.vca.block_until_completed(delete_task)
1925 if result:
1926 break
1927 else:
1928 self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
1929 time.sleep(INTERVAL_TIME)
1930
1931 wait_time +=INTERVAL_TIME
1932
1933 if not result:
1934 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
1935
1936 except:
1937 self.logger.debug(traceback.format_exc())
1938 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1939
1940 if self.vca.get_vapp(self.get_vdc_details(), vapp_name) is None:
1941 self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
1942 return vm__vim_uuid
1943 else:
1944 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1945
1946 def refresh_vms_status(self, vm_list):
1947 """Get the status of the virtual machines and their interfaces/ports
1948 Params: the list of VM identifiers
1949 Returns a dictionary with:
1950 vm_id: #VIM id of this Virtual Machine
1951 status: #Mandatory. Text with one of:
1952 # DELETED (not found at vim)
1953 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1954 # OTHER (Vim reported other status not understood)
1955 # ERROR (VIM indicates an ERROR status)
1956 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1957 # CREATING (on building process), ERROR
1958 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1959 #
1960 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1961 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1962 interfaces:
1963 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1964 mac_address: #Text format XX:XX:XX:XX:XX:XX
1965 vim_net_id: #network id where this interface is connected
1966 vim_interface_id: #interface/port VIM id
1967 ip_address: #null, or text with IPv4, IPv6 address
1968 """
1969
1970 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
1971
1972 vdc = self.get_vdc_details()
1973 if vdc is None:
1974 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1975
1976 vms_dict = {}
1977 nsx_edge_list = []
1978 for vmuuid in vm_list:
1979 vmname = self.get_namebyvappid(self.get_vdc_details(), vmuuid)
1980 if vmname is not None:
1981
1982 try:
1983 vm_pci_details = self.get_vm_pci_details(vmuuid)
1984 the_vapp = self.vca.get_vapp(self.get_vdc_details(), vmname)
1985 vm_info = the_vapp.get_vms_details()
1986 vm_status = vm_info[0]['status']
1987 vm_info[0].update(vm_pci_details)
1988
1989 vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1990 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1991 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
1992
1993 # get networks
1994 vm_app_networks = the_vapp.get_vms_network_info()
1995 for vapp_network in vm_app_networks:
1996 for vm_network in vapp_network:
1997 if vm_network['name'] == vmname:
1998 #Assign IP Address based on MAC Address in NSX DHCP lease info
1999 if vm_network['ip'] is None:
2000 if not nsx_edge_list:
2001 nsx_edge_list = self.get_edge_details()
2002 if nsx_edge_list is None:
2003 raise vimconn.vimconnException("refresh_vms_status:"\
2004 "Failed to get edge details from NSX Manager")
2005 if vm_network['mac'] is not None:
2006 vm_network['ip'] = self.get_ipaddr_from_NSXedge(nsx_edge_list, vm_network['mac'])
2007
2008 vm_net_id = self.get_network_id_by_name(vm_network['network_name'])
2009 interface = {"mac_address": vm_network['mac'],
2010 "vim_net_id": vm_net_id,
2011 "vim_interface_id": vm_net_id,
2012 'ip_address': vm_network['ip']}
2013 # interface['vim_info'] = yaml.safe_dump(vm_network)
2014 vm_dict["interfaces"].append(interface)
2015 # add a vm to vm dict
2016 vms_dict.setdefault(vmuuid, vm_dict)
2017 except Exception as exp:
2018 self.logger.debug("Error in response {}".format(exp))
2019 self.logger.debug(traceback.format_exc())
2020
2021 return vms_dict
2022
2023
2024 def get_edge_details(self):
2025 """Get the NSX edge list from NSX Manager
2026 Returns list of NSX edges
2027 """
2028 edge_list = []
2029 rheaders = {'Content-Type': 'application/xml'}
2030 nsx_api_url = '/api/4.0/edges'
2031
2032 self.logger.debug("Get edge details from NSX Manager {} {}".format(self.nsx_manager, nsx_api_url))
2033
2034 try:
2035 resp = requests.get(self.nsx_manager + nsx_api_url,
2036 auth = (self.nsx_user, self.nsx_password),
2037 verify = False, headers = rheaders)
2038 if resp.status_code == requests.codes.ok:
2039 paged_Edge_List = XmlElementTree.fromstring(resp.text)
2040 for edge_pages in paged_Edge_List:
2041 if edge_pages.tag == 'edgePage':
2042 for edge_summary in edge_pages:
2043 if edge_summary.tag == 'pagingInfo':
2044 for element in edge_summary:
2045 if element.tag == 'totalCount' and element.text == '0':
2046 raise vimconn.vimconnException("get_edge_details: No NSX edges details found: {}"
2047 .format(self.nsx_manager))
2048
2049 if edge_summary.tag == 'edgeSummary':
2050 for element in edge_summary:
2051 if element.tag == 'id':
2052 edge_list.append(element.text)
2053 else:
2054 raise vimconn.vimconnException("get_edge_details: No NSX edge details found: {}"
2055 .format(self.nsx_manager))
2056
2057 if not edge_list:
2058 raise vimconn.vimconnException("get_edge_details: "\
2059 "No NSX edge details found: {}"
2060 .format(self.nsx_manager))
2061 else:
2062 self.logger.debug("get_edge_details: Found NSX edges {}".format(edge_list))
2063 return edge_list
2064 else:
2065 self.logger.debug("get_edge_details: "
2066 "Failed to get NSX edge details from NSX Manager: {}"
2067 .format(resp.content))
2068 return None
2069
2070 except Exception as exp:
2071 self.logger.debug("get_edge_details: "\
2072 "Failed to get NSX edge details from NSX Manager: {}"
2073 .format(exp))
2074 raise vimconn.vimconnException("get_edge_details: "\
2075 "Failed to get NSX edge details from NSX Manager: {}"
2076 .format(exp))
2077
2078
2079 def get_ipaddr_from_NSXedge(self, nsx_edges, mac_address):
2080 """Get IP address details from NSX edges, using the MAC address
2081 PARAMS: nsx_edges : List of NSX edges
2082 mac_address : Find IP address corresponding to this MAC address
2083 Returns: IP address corrresponding to the provided MAC address
2084 """
2085
2086 ip_addr = None
2087 rheaders = {'Content-Type': 'application/xml'}
2088
2089 self.logger.debug("get_ipaddr_from_NSXedge: Finding IP addr from NSX edge")
2090
2091 try:
2092 for edge in nsx_edges:
2093 nsx_api_url = '/api/4.0/edges/'+ edge +'/dhcp/leaseInfo'
2094
2095 resp = requests.get(self.nsx_manager + nsx_api_url,
2096 auth = (self.nsx_user, self.nsx_password),
2097 verify = False, headers = rheaders)
2098
2099 if resp.status_code == requests.codes.ok:
2100 dhcp_leases = XmlElementTree.fromstring(resp.text)
2101 for child in dhcp_leases:
2102 if child.tag == 'dhcpLeaseInfo':
2103 dhcpLeaseInfo = child
2104 for leaseInfo in dhcpLeaseInfo:
2105 for elem in leaseInfo:
2106 if (elem.tag)=='macAddress':
2107 edge_mac_addr = elem.text
2108 if (elem.tag)=='ipAddress':
2109 ip_addr = elem.text
2110 if edge_mac_addr is not None:
2111 if edge_mac_addr == mac_address:
2112 self.logger.debug("Found ip addr {} for mac {} at NSX edge {}"
2113 .format(ip_addr, mac_address,edge))
2114 return ip_addr
2115 else:
2116 self.logger.debug("get_ipaddr_from_NSXedge: "\
2117 "Error occurred while getting DHCP lease info from NSX Manager: {}"
2118 .format(resp.content))
2119
2120 self.logger.debug("get_ipaddr_from_NSXedge: No IP addr found in any NSX edge")
2121 return None
2122
2123 except XmlElementTree.ParseError as Err:
2124 self.logger.debug("ParseError in response from NSX Manager {}".format(Err.message), exc_info=True)
2125
2126
2127 def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
2128 """Send and action over a VM instance from VIM
2129 Returns the vm_id if the action was successfully sent to the VIM"""
2130
2131 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
2132 if vm__vim_uuid is None or action_dict is None:
2133 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
2134
2135 vdc = self.get_vdc_details()
2136 if vdc is None:
2137 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
2138
2139 vapp_name = self.get_namebyvappid(vdc, vm__vim_uuid)
2140 if vapp_name is None:
2141 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
2142 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
2143 else:
2144 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
2145
2146 try:
2147 the_vapp = self.vca.get_vapp(vdc, vapp_name)
2148 # TODO fix all status
2149 if "start" in action_dict:
2150 vm_info = the_vapp.get_vms_details()
2151 vm_status = vm_info[0]['status']
2152 self.logger.info("action_vminstance: Power on vApp: {}".format(vapp_name))
2153 if vm_status == "Suspended" or vm_status == "Powered off":
2154 power_on_task = the_vapp.poweron()
2155 result = self.vca.block_until_completed(power_on_task)
2156 self.instance_actions_result("start", result, vapp_name)
2157 elif "rebuild" in action_dict:
2158 self.logger.info("action_vminstance: Rebuild vApp: {}".format(vapp_name))
2159 rebuild_task = the_vapp.deploy(powerOn=True)
2160 result = self.vca.block_until_completed(rebuild_task)
2161 self.instance_actions_result("rebuild", result, vapp_name)
2162 elif "pause" in action_dict:
2163 self.logger.info("action_vminstance: pause vApp: {}".format(vapp_name))
2164 pause_task = the_vapp.undeploy(action='suspend')
2165 result = self.vca.block_until_completed(pause_task)
2166 self.instance_actions_result("pause", result, vapp_name)
2167 elif "resume" in action_dict:
2168 self.logger.info("action_vminstance: resume vApp: {}".format(vapp_name))
2169 power_task = the_vapp.poweron()
2170 result = self.vca.block_until_completed(power_task)
2171 self.instance_actions_result("resume", result, vapp_name)
2172 elif "shutoff" in action_dict or "shutdown" in action_dict:
2173 action_name , value = list(action_dict.items())[0]
2174 self.logger.info("action_vminstance: {} vApp: {}".format(action_name, vapp_name))
2175 power_off_task = the_vapp.undeploy(action='powerOff')
2176 result = self.vca.block_until_completed(power_off_task)
2177 if action_name == "shutdown":
2178 self.instance_actions_result("shutdown", result, vapp_name)
2179 else:
2180 self.instance_actions_result("shutoff", result, vapp_name)
2181 elif "forceOff" in action_dict:
2182 result = the_vapp.undeploy(action='force')
2183 self.instance_actions_result("forceOff", result, vapp_name)
2184 elif "reboot" in action_dict:
2185 self.logger.info("action_vminstance: reboot vApp: {}".format(vapp_name))
2186 reboot_task = the_vapp.reboot()
2187 else:
2188 raise vimconn.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict))
2189 return vm__vim_uuid
2190 except Exception as exp :
2191 self.logger.debug("action_vminstance: Failed with Exception {}".format(exp))
2192 raise vimconn.vimconnException("action_vminstance: Failed with Exception {}".format(exp))
2193
2194 def instance_actions_result(self, action, result, vapp_name):
2195 if result:
2196 self.logger.info("action_vminstance: Sucessfully {} the vApp: {}".format(action, vapp_name))
2197 else:
2198 self.logger.error("action_vminstance: Failed to {} vApp: {}".format(action, vapp_name))
2199
2200 def get_vminstance_console(self, vm_id, console_type="vnc"):
2201 """
2202 Get a console for the virtual machine
2203 Params:
2204 vm_id: uuid of the VM
2205 console_type, can be:
2206 "novnc" (by default), "xvpvnc" for VNC types,
2207 "rdp-html5" for RDP types, "spice-html5" for SPICE types
2208 Returns dict with the console parameters:
2209 protocol: ssh, ftp, http, https, ...
2210 server: usually ip address
2211 port: the http, ssh, ... port
2212 suffix: extra text, e.g. the http path and query string
2213 """
2214 raise vimconn.vimconnNotImplemented("Should have implemented this")
2215
2216 # NOT USED METHODS in current version
2217
2218 def host_vim2gui(self, host, server_dict):
2219 """Transform host dictionary from VIM format to GUI format,
2220 and append to the server_dict
2221 """
2222 raise vimconn.vimconnNotImplemented("Should have implemented this")
2223
2224 def get_hosts_info(self):
2225 """Get the information of deployed hosts
2226 Returns the hosts content"""
2227 raise vimconn.vimconnNotImplemented("Should have implemented this")
2228
2229 def get_hosts(self, vim_tenant):
2230 """Get the hosts and deployed instances
2231 Returns the hosts content"""
2232 raise vimconn.vimconnNotImplemented("Should have implemented this")
2233
2234 def get_processor_rankings(self):
2235 """Get the processor rankings in the VIM database"""
2236 raise vimconn.vimconnNotImplemented("Should have implemented this")
2237
2238 def new_host(self, host_data):
2239 """Adds a new host to VIM"""
2240 '''Returns status code of the VIM response'''
2241 raise vimconn.vimconnNotImplemented("Should have implemented this")
2242
2243 def new_external_port(self, port_data):
2244 """Adds a external port to VIM"""
2245 '''Returns the port identifier'''
2246 raise vimconn.vimconnNotImplemented("Should have implemented this")
2247
2248 def new_external_network(self, net_name, net_type):
2249 """Adds a external network to VIM (shared)"""
2250 '''Returns the network identifier'''
2251 raise vimconn.vimconnNotImplemented("Should have implemented this")
2252
2253 def connect_port_network(self, port_id, network_id, admin=False):
2254 """Connects a external port to a network"""
2255 '''Returns status code of the VIM response'''
2256 raise vimconn.vimconnNotImplemented("Should have implemented this")
2257
2258 def new_vminstancefromJSON(self, vm_data):
2259 """Adds a VM instance to VIM"""
2260 '''Returns the instance identifier'''
2261 raise vimconn.vimconnNotImplemented("Should have implemented this")
2262
2263 def get_network_name_by_id(self, network_uuid=None):
2264 """Method gets vcloud director network named based on supplied uuid.
2265
2266 Args:
2267 network_uuid: network_id
2268
2269 Returns:
2270 The return network name.
2271 """
2272
2273 if not network_uuid:
2274 return None
2275
2276 try:
2277 org_dict = self.get_org(self.org_uuid)
2278 if 'networks' in org_dict:
2279 org_network_dict = org_dict['networks']
2280 for net_uuid in org_network_dict:
2281 if net_uuid == network_uuid:
2282 return org_network_dict[net_uuid]
2283 except:
2284 self.logger.debug("Exception in get_network_name_by_id")
2285 self.logger.debug(traceback.format_exc())
2286
2287 return None
2288
2289 def get_network_id_by_name(self, network_name=None):
2290 """Method gets vcloud director network uuid based on supplied name.
2291
2292 Args:
2293 network_name: network_name
2294 Returns:
2295 The return network uuid.
2296 network_uuid: network_id
2297 """
2298
2299 if not network_name:
2300 self.logger.debug("get_network_id_by_name() : Network name is empty")
2301 return None
2302
2303 try:
2304 org_dict = self.get_org(self.org_uuid)
2305 if org_dict and 'networks' in org_dict:
2306 org_network_dict = org_dict['networks']
2307 for net_uuid,net_name in org_network_dict.items():
2308 if net_name == network_name:
2309 return net_uuid
2310
2311 except KeyError as exp:
2312 self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
2313
2314 return None
2315
2316 def list_org_action(self):
2317 """
2318 Method leverages vCloud director and query for available organization for particular user
2319
2320 Args:
2321 vca - is active VCA connection.
2322 vdc_name - is a vdc name that will be used to query vms action
2323
2324 Returns:
2325 The return XML respond
2326 """
2327
2328 url_list = [self.vca.host, '/api/org']
2329 vm_list_rest_call = ''.join(url_list)
2330
2331 if not (not self.vca.vcloud_session or not self.vca.vcloud_session.organization):
2332 response = Http.get(url=vm_list_rest_call,
2333 headers=self.vca.vcloud_session.get_vcloud_headers(),
2334 verify=self.vca.verify,
2335 logger=self.vca.logger)
2336
2337 if response.status_code == 403:
2338 response = self.retry_rest('GET', vm_list_rest_call)
2339
2340 if response.status_code == requests.codes.ok:
2341 return response.content
2342
2343 return None
2344
2345 def get_org_action(self, org_uuid=None):
2346 """
2347 Method leverages vCloud director and retrieve available object fdr organization.
2348
2349 Args:
2350 vca - is active VCA connection.
2351 vdc_name - is a vdc name that will be used to query vms action
2352
2353 Returns:
2354 The return XML respond
2355 """
2356
2357 if org_uuid is None:
2358 return None
2359
2360 url_list = [self.vca.host, '/api/org/', org_uuid]
2361 vm_list_rest_call = ''.join(url_list)
2362
2363 if not (not self.vca.vcloud_session or not self.vca.vcloud_session.organization):
2364 response = Http.get(url=vm_list_rest_call,
2365 headers=self.vca.vcloud_session.get_vcloud_headers(),
2366 verify=self.vca.verify,
2367 logger=self.vca.logger)
2368
2369 #Retry login if session expired & retry sending request
2370 if response.status_code == 403:
2371 response = self.retry_rest('GET', vm_list_rest_call)
2372
2373 if response.status_code == requests.codes.ok:
2374 return response.content
2375
2376 return None
2377
2378 def get_org(self, org_uuid=None):
2379 """
2380 Method retrieves available organization in vCloud Director
2381
2382 Args:
2383 org_uuid - is a organization uuid.
2384
2385 Returns:
2386 The return dictionary with following key
2387 "network" - for network list under the org
2388 "catalogs" - for network list under the org
2389 "vdcs" - for vdc list under org
2390 """
2391
2392 org_dict = {}
2393
2394 if org_uuid is None:
2395 return org_dict
2396
2397 content = self.get_org_action(org_uuid=org_uuid)
2398 try:
2399 vdc_list = {}
2400 network_list = {}
2401 catalog_list = {}
2402 vm_list_xmlroot = XmlElementTree.fromstring(content)
2403 for child in vm_list_xmlroot:
2404 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2405 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2406 org_dict['vdcs'] = vdc_list
2407 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2408 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2409 org_dict['networks'] = network_list
2410 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2411 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2412 org_dict['catalogs'] = catalog_list
2413 except:
2414 pass
2415
2416 return org_dict
2417
2418 def get_org_list(self):
2419 """
2420 Method retrieves available organization in vCloud Director
2421
2422 Args:
2423 vca - is active VCA connection.
2424
2425 Returns:
2426 The return dictionary and key for each entry VDC UUID
2427 """
2428
2429 org_dict = {}
2430
2431 content = self.list_org_action()
2432 try:
2433 vm_list_xmlroot = XmlElementTree.fromstring(content)
2434 for vm_xml in vm_list_xmlroot:
2435 if vm_xml.tag.split("}")[1] == 'Org':
2436 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
2437 org_dict[org_uuid[0]] = vm_xml.attrib['name']
2438 except:
2439 pass
2440
2441 return org_dict
2442
2443 def vms_view_action(self, vdc_name=None):
2444 """ Method leverages vCloud director vms query call
2445
2446 Args:
2447 vca - is active VCA connection.
2448 vdc_name - is a vdc name that will be used to query vms action
2449
2450 Returns:
2451 The return XML respond
2452 """
2453 vca = self.connect()
2454 if vdc_name is None:
2455 return None
2456
2457 url_list = [vca.host, '/api/vms/query']
2458 vm_list_rest_call = ''.join(url_list)
2459
2460 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2461 refs = [ref for ref in vca.vcloud_session.organization.Link if ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml']
2462 if len(refs) == 1:
2463 response = Http.get(url=vm_list_rest_call,
2464 headers=vca.vcloud_session.get_vcloud_headers(),
2465 verify=vca.verify,
2466 logger=vca.logger)
2467 if response.status_code == requests.codes.ok:
2468 return response.content
2469
2470 return None
2471
2472 def get_vapp_list(self, vdc_name=None):
2473 """
2474 Method retrieves vApp list deployed vCloud director and returns a dictionary
2475 contains a list of all vapp deployed for queried VDC.
2476 The key for a dictionary is vApp UUID
2477
2478
2479 Args:
2480 vca - is active VCA connection.
2481 vdc_name - is a vdc name that will be used to query vms action
2482
2483 Returns:
2484 The return dictionary and key for each entry vapp UUID
2485 """
2486
2487 vapp_dict = {}
2488 if vdc_name is None:
2489 return vapp_dict
2490
2491 content = self.vms_view_action(vdc_name=vdc_name)
2492 try:
2493 vm_list_xmlroot = XmlElementTree.fromstring(content)
2494 for vm_xml in vm_list_xmlroot:
2495 if vm_xml.tag.split("}")[1] == 'VMRecord':
2496 if vm_xml.attrib['isVAppTemplate'] == 'true':
2497 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
2498 if 'vappTemplate-' in rawuuid[0]:
2499 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2500 # vm and use raw UUID as key
2501 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
2502 except:
2503 pass
2504
2505 return vapp_dict
2506
2507 def get_vm_list(self, vdc_name=None):
2508 """
2509 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2510 contains a list of all VM's deployed for queried VDC.
2511 The key for a dictionary is VM UUID
2512
2513
2514 Args:
2515 vca - is active VCA connection.
2516 vdc_name - is a vdc name that will be used to query vms action
2517
2518 Returns:
2519 The return dictionary and key for each entry vapp UUID
2520 """
2521 vm_dict = {}
2522
2523 if vdc_name is None:
2524 return vm_dict
2525
2526 content = self.vms_view_action(vdc_name=vdc_name)
2527 try:
2528 vm_list_xmlroot = XmlElementTree.fromstring(content)
2529 for vm_xml in vm_list_xmlroot:
2530 if vm_xml.tag.split("}")[1] == 'VMRecord':
2531 if vm_xml.attrib['isVAppTemplate'] == 'false':
2532 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2533 if 'vm-' in rawuuid[0]:
2534 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2535 # vm and use raw UUID as key
2536 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2537 except:
2538 pass
2539
2540 return vm_dict
2541
2542 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
2543 """
2544 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2545 contains a list of all VM's deployed for queried VDC.
2546 The key for a dictionary is VM UUID
2547
2548
2549 Args:
2550 vca - is active VCA connection.
2551 vdc_name - is a vdc name that will be used to query vms action
2552
2553 Returns:
2554 The return dictionary and key for each entry vapp UUID
2555 """
2556 vm_dict = {}
2557 vca = self.connect()
2558 if not vca:
2559 raise vimconn.vimconnConnectionException("self.connect() is failed")
2560
2561 if vdc_name is None:
2562 return vm_dict
2563
2564 content = self.vms_view_action(vdc_name=vdc_name)
2565 try:
2566 vm_list_xmlroot = XmlElementTree.fromstring(content)
2567 for vm_xml in vm_list_xmlroot:
2568 if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
2569 # lookup done by UUID
2570 if isuuid:
2571 if vapp_name in vm_xml.attrib['container']:
2572 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2573 if 'vm-' in rawuuid[0]:
2574 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2575 break
2576 # lookup done by Name
2577 else:
2578 if vapp_name in vm_xml.attrib['name']:
2579 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2580 if 'vm-' in rawuuid[0]:
2581 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2582 break
2583 except:
2584 pass
2585
2586 return vm_dict
2587
2588 def get_network_action(self, network_uuid=None):
2589 """
2590 Method leverages vCloud director and query network based on network uuid
2591
2592 Args:
2593 vca - is active VCA connection.
2594 network_uuid - is a network uuid
2595
2596 Returns:
2597 The return XML respond
2598 """
2599
2600 if network_uuid is None:
2601 return None
2602
2603 url_list = [self.vca.host, '/api/network/', network_uuid]
2604 vm_list_rest_call = ''.join(url_list)
2605
2606 if not (not self.vca.vcloud_session or not self.vca.vcloud_session.organization):
2607 response = Http.get(url=vm_list_rest_call,
2608 headers=self.vca.vcloud_session.get_vcloud_headers(),
2609 verify=self.vca.verify,
2610 logger=self.vca.logger)
2611
2612 #Retry login if session expired & retry sending request
2613 if response.status_code == 403:
2614 response = self.retry_rest('GET', vm_list_rest_call)
2615
2616 if response.status_code == requests.codes.ok:
2617 return response.content
2618
2619 return None
2620
2621 def get_vcd_network(self, network_uuid=None):
2622 """
2623 Method retrieves available network from vCloud Director
2624
2625 Args:
2626 network_uuid - is VCD network UUID
2627
2628 Each element serialized as key : value pair
2629
2630 Following keys available for access. network_configuration['Gateway'}
2631 <Configuration>
2632 <IpScopes>
2633 <IpScope>
2634 <IsInherited>true</IsInherited>
2635 <Gateway>172.16.252.100</Gateway>
2636 <Netmask>255.255.255.0</Netmask>
2637 <Dns1>172.16.254.201</Dns1>
2638 <Dns2>172.16.254.202</Dns2>
2639 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2640 <IsEnabled>true</IsEnabled>
2641 <IpRanges>
2642 <IpRange>
2643 <StartAddress>172.16.252.1</StartAddress>
2644 <EndAddress>172.16.252.99</EndAddress>
2645 </IpRange>
2646 </IpRanges>
2647 </IpScope>
2648 </IpScopes>
2649 <FenceMode>bridged</FenceMode>
2650
2651 Returns:
2652 The return dictionary and key for each entry vapp UUID
2653 """
2654
2655 network_configuration = {}
2656 if network_uuid is None:
2657 return network_uuid
2658
2659 try:
2660 content = self.get_network_action(network_uuid=network_uuid)
2661 vm_list_xmlroot = XmlElementTree.fromstring(content)
2662
2663 network_configuration['status'] = vm_list_xmlroot.get("status")
2664 network_configuration['name'] = vm_list_xmlroot.get("name")
2665 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
2666
2667 for child in vm_list_xmlroot:
2668 if child.tag.split("}")[1] == 'IsShared':
2669 network_configuration['isShared'] = child.text.strip()
2670 if child.tag.split("}")[1] == 'Configuration':
2671 for configuration in child.iter():
2672 tagKey = configuration.tag.split("}")[1].strip()
2673 if tagKey != "":
2674 network_configuration[tagKey] = configuration.text.strip()
2675 return network_configuration
2676 except Exception as exp :
2677 self.logger.debug("get_vcd_network: Failed with Exception {}".format(exp))
2678 raise vimconn.vimconnException("get_vcd_network: Failed with Exception {}".format(exp))
2679
2680 return network_configuration
2681
2682 def delete_network_action(self, network_uuid=None):
2683 """
2684 Method delete given network from vCloud director
2685
2686 Args:
2687 network_uuid - is a network uuid that client wish to delete
2688
2689 Returns:
2690 The return None or XML respond or false
2691 """
2692
2693 vca = self.connect_as_admin()
2694 if not vca:
2695 raise vimconn.vimconnConnectionException("self.connect() is failed")
2696 if network_uuid is None:
2697 return False
2698
2699 url_list = [vca.host, '/api/admin/network/', network_uuid]
2700 vm_list_rest_call = ''.join(url_list)
2701
2702 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2703 response = Http.delete(url=vm_list_rest_call,
2704 headers=vca.vcloud_session.get_vcloud_headers(),
2705 verify=vca.verify,
2706 logger=vca.logger)
2707
2708 if response.status_code == 202:
2709 return True
2710
2711 return False
2712
2713 def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2714 ip_profile=None, isshared='true'):
2715 """
2716 Method create network in vCloud director
2717
2718 Args:
2719 network_name - is network name to be created.
2720 net_type - can be 'bridge','data','ptp','mgmt'.
2721 ip_profile is a dict containing the IP parameters of the network
2722 isshared - is a boolean
2723 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2724 It optional attribute. by default if no parent network indicate the first available will be used.
2725
2726 Returns:
2727 The return network uuid or return None
2728 """
2729
2730 new_network_name = [network_name, '-', str(uuid.uuid4())]
2731 content = self.create_network_rest(network_name=''.join(new_network_name),
2732 ip_profile=ip_profile,
2733 net_type=net_type,
2734 parent_network_uuid=parent_network_uuid,
2735 isshared=isshared)
2736 if content is None:
2737 self.logger.debug("Failed create network {}.".format(network_name))
2738 return None
2739
2740 try:
2741 vm_list_xmlroot = XmlElementTree.fromstring(content)
2742 vcd_uuid = vm_list_xmlroot.get('id').split(":")
2743 if len(vcd_uuid) == 4:
2744 self.logger.info("Created new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
2745 return vcd_uuid[3]
2746 except:
2747 self.logger.debug("Failed create network {}".format(network_name))
2748 return None
2749
2750 def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2751 ip_profile=None, isshared='true'):
2752 """
2753 Method create network in vCloud director
2754
2755 Args:
2756 network_name - is network name to be created.
2757 net_type - can be 'bridge','data','ptp','mgmt'.
2758 ip_profile is a dict containing the IP parameters of the network
2759 isshared - is a boolean
2760 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2761 It optional attribute. by default if no parent network indicate the first available will be used.
2762
2763 Returns:
2764 The return network uuid or return None
2765 """
2766
2767 vca = self.connect_as_admin()
2768 if not vca:
2769 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2770 if network_name is None:
2771 return None
2772
2773 url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
2774 vm_list_rest_call = ''.join(url_list)
2775 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2776 response = Http.get(url=vm_list_rest_call,
2777 headers=vca.vcloud_session.get_vcloud_headers(),
2778 verify=vca.verify,
2779 logger=vca.logger)
2780
2781 provider_network = None
2782 available_networks = None
2783 add_vdc_rest_url = None
2784
2785 if response.status_code != requests.codes.ok:
2786 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2787 response.status_code))
2788 return None
2789 else:
2790 try:
2791 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2792 for child in vm_list_xmlroot:
2793 if child.tag.split("}")[1] == 'ProviderVdcReference':
2794 provider_network = child.attrib.get('href')
2795 # application/vnd.vmware.admin.providervdc+xml
2796 if child.tag.split("}")[1] == 'Link':
2797 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2798 and child.attrib.get('rel') == 'add':
2799 add_vdc_rest_url = child.attrib.get('href')
2800 except:
2801 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2802 self.logger.debug("Respond body {}".format(response.content))
2803 return None
2804
2805 # find pvdc provided available network
2806 response = Http.get(url=provider_network,
2807 headers=vca.vcloud_session.get_vcloud_headers(),
2808 verify=vca.verify,
2809 logger=vca.logger)
2810 if response.status_code != requests.codes.ok:
2811 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2812 response.status_code))
2813 return None
2814
2815 # available_networks.split("/")[-1]
2816
2817 if parent_network_uuid is None:
2818 try:
2819 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2820 for child in vm_list_xmlroot.iter():
2821 if child.tag.split("}")[1] == 'AvailableNetworks':
2822 for networks in child.iter():
2823 # application/vnd.vmware.admin.network+xml
2824 if networks.attrib.get('href') is not None:
2825 available_networks = networks.attrib.get('href')
2826 break
2827 except:
2828 return None
2829
2830 try:
2831 #Configure IP profile of the network
2832 ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
2833
2834 if 'subnet_address' not in ip_profile or ip_profile['subnet_address'] is None:
2835 subnet_rand = random.randint(0, 255)
2836 ip_base = "192.168.{}.".format(subnet_rand)
2837 ip_profile['subnet_address'] = ip_base + "0/24"
2838 else:
2839 ip_base = ip_profile['subnet_address'].rsplit('.',1)[0] + '.'
2840
2841 if 'gateway_address' not in ip_profile or ip_profile['gateway_address'] is None:
2842 ip_profile['gateway_address']=ip_base + "1"
2843 if 'dhcp_count' not in ip_profile or ip_profile['dhcp_count'] is None:
2844 ip_profile['dhcp_count']=DEFAULT_IP_PROFILE['dhcp_count']
2845 if 'dhcp_enabled' not in ip_profile or ip_profile['dhcp_enabled'] is None:
2846 ip_profile['dhcp_enabled']=DEFAULT_IP_PROFILE['dhcp_enabled']
2847 if 'dhcp_start_address' not in ip_profile or ip_profile['dhcp_start_address'] is None:
2848 ip_profile['dhcp_start_address']=ip_base + "3"
2849 if 'ip_version' not in ip_profile or ip_profile['ip_version'] is None:
2850 ip_profile['ip_version']=DEFAULT_IP_PROFILE['ip_version']
2851 if 'dns_address' not in ip_profile or ip_profile['dns_address'] is None:
2852 ip_profile['dns_address']=ip_base + "2"
2853
2854 gateway_address=ip_profile['gateway_address']
2855 dhcp_count=int(ip_profile['dhcp_count'])
2856 subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
2857
2858 if ip_profile['dhcp_enabled']==True:
2859 dhcp_enabled='true'
2860 else:
2861 dhcp_enabled='false'
2862 dhcp_start_address=ip_profile['dhcp_start_address']
2863
2864 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2865 end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
2866 end_ip_int += dhcp_count - 1
2867 dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
2868
2869 ip_version=ip_profile['ip_version']
2870 dns_address=ip_profile['dns_address']
2871 except KeyError as exp:
2872 self.logger.debug("Create Network REST: Key error {}".format(exp))
2873 raise vimconn.vimconnException("Create Network REST: Key error{}".format(exp))
2874
2875 # either use client provided UUID or search for a first available
2876 # if both are not defined we return none
2877 if parent_network_uuid is not None:
2878 url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
2879 add_vdc_rest_url = ''.join(url_list)
2880
2881 #Creating all networks as Direct Org VDC type networks.
2882 #Unused in case of Underlay (data/ptp) network interface.
2883 fence_mode="bridged"
2884 is_inherited='false'
2885 dns_list = dns_address.split(";")
2886 dns1 = dns_list[0]
2887 dns2_text = ""
2888 if len(dns_list) >= 2:
2889 dns2_text = "\n <Dns2>{}</Dns2>\n".format(dns_list[1])
2890 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2891 <Description>Openmano created</Description>
2892 <Configuration>
2893 <IpScopes>
2894 <IpScope>
2895 <IsInherited>{1:s}</IsInherited>
2896 <Gateway>{2:s}</Gateway>
2897 <Netmask>{3:s}</Netmask>
2898 <Dns1>{4:s}</Dns1>{5:s}
2899 <IsEnabled>{6:s}</IsEnabled>
2900 <IpRanges>
2901 <IpRange>
2902 <StartAddress>{7:s}</StartAddress>
2903 <EndAddress>{8:s}</EndAddress>
2904 </IpRange>
2905 </IpRanges>
2906 </IpScope>
2907 </IpScopes>
2908 <ParentNetwork href="{9:s}"/>
2909 <FenceMode>{10:s}</FenceMode>
2910 </Configuration>
2911 <IsShared>{11:s}</IsShared>
2912 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2913 subnet_address, dns1, dns2_text, dhcp_enabled,
2914 dhcp_start_address, dhcp_end_address, available_networks,
2915 fence_mode, isshared)
2916
2917 headers = vca.vcloud_session.get_vcloud_headers()
2918 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2919 try:
2920 response = Http.post(url=add_vdc_rest_url,
2921 headers=headers,
2922 data=data,
2923 verify=vca.verify,
2924 logger=vca.logger)
2925
2926 if response.status_code != 201:
2927 self.logger.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
2928 .format(response.status_code,response.content))
2929 else:
2930 network = networkType.parseString(response.content, True)
2931 create_nw_task = network.get_Tasks().get_Task()[0]
2932
2933 # if we all ok we respond with content after network creation completes
2934 # otherwise by default return None
2935 if create_nw_task is not None:
2936 self.logger.debug("Create Network REST : Waiting for Network creation complete")
2937 status = vca.block_until_completed(create_nw_task)
2938 if status:
2939 return response.content
2940 else:
2941 self.logger.debug("create_network_rest task failed. Network Create response : {}"
2942 .format(response.content))
2943 except Exception as exp:
2944 self.logger.debug("create_network_rest : Exception : {} ".format(exp))
2945
2946 return None
2947
2948 def convert_cidr_to_netmask(self, cidr_ip=None):
2949 """
2950 Method sets convert CIDR netmask address to normal IP format
2951 Args:
2952 cidr_ip : CIDR IP address
2953 Returns:
2954 netmask : Converted netmask
2955 """
2956 if cidr_ip is not None:
2957 if '/' in cidr_ip:
2958 network, net_bits = cidr_ip.split('/')
2959 netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
2960 else:
2961 netmask = cidr_ip
2962 return netmask
2963 return None
2964
2965 def get_provider_rest(self, vca=None):
2966 """
2967 Method gets provider vdc view from vcloud director
2968
2969 Args:
2970 network_name - is network name to be created.
2971 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2972 It optional attribute. by default if no parent network indicate the first available will be used.
2973
2974 Returns:
2975 The return xml content of respond or None
2976 """
2977
2978 url_list = [vca.host, '/api/admin']
2979 response = Http.get(url=''.join(url_list),
2980 headers=vca.vcloud_session.get_vcloud_headers(),
2981 verify=vca.verify,
2982 logger=vca.logger)
2983
2984 if response.status_code == requests.codes.ok:
2985 return response.content
2986 return None
2987
2988 def create_vdc(self, vdc_name=None):
2989
2990 vdc_dict = {}
2991
2992 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
2993 if xml_content is not None:
2994 try:
2995 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
2996 for child in task_resp_xmlroot:
2997 if child.tag.split("}")[1] == 'Owner':
2998 vdc_id = child.attrib.get('href').split("/")[-1]
2999 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
3000 return vdc_dict
3001 except:
3002 self.logger.debug("Respond body {}".format(xml_content))
3003
3004 return None
3005
3006 def create_vdc_from_tmpl_rest(self, vdc_name=None):
3007 """
3008 Method create vdc in vCloud director based on VDC template.
3009 it uses pre-defined template that must be named openmano
3010
3011 Args:
3012 vdc_name - name of a new vdc.
3013
3014 Returns:
3015 The return xml content of respond or None
3016 """
3017
3018 self.logger.info("Creating new vdc {}".format(vdc_name))
3019 vca = self.connect()
3020 if not vca:
3021 raise vimconn.vimconnConnectionException("self.connect() is failed")
3022 if vdc_name is None:
3023 return None
3024
3025 url_list = [vca.host, '/api/vdcTemplates']
3026 vm_list_rest_call = ''.join(url_list)
3027 response = Http.get(url=vm_list_rest_call,
3028 headers=vca.vcloud_session.get_vcloud_headers(),
3029 verify=vca.verify,
3030 logger=vca.logger)
3031
3032 # container url to a template
3033 vdc_template_ref = None
3034 try:
3035 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
3036 for child in vm_list_xmlroot:
3037 # application/vnd.vmware.admin.providervdc+xml
3038 # we need find a template from witch we instantiate VDC
3039 if child.tag.split("}")[1] == 'VdcTemplate':
3040 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml':
3041 vdc_template_ref = child.attrib.get('href')
3042 except:
3043 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
3044 self.logger.debug("Respond body {}".format(response.content))
3045 return None
3046
3047 # if we didn't found required pre defined template we return None
3048 if vdc_template_ref is None:
3049 return None
3050
3051 try:
3052 # instantiate vdc
3053 url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
3054 vm_list_rest_call = ''.join(url_list)
3055 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
3056 <Source href="{1:s}"></Source>
3057 <Description>opnemano</Description>
3058 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
3059 headers = vca.vcloud_session.get_vcloud_headers()
3060 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
3061 response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
3062 logger=vca.logger)
3063
3064 vdc_task = taskType.parseString(response.content, True)
3065 if type(vdc_task) is GenericTask:
3066 self.vca.block_until_completed(vdc_task)
3067
3068 # if we all ok we respond with content otherwise by default None
3069 if response.status_code >= 200 and response.status_code < 300:
3070 return response.content
3071 return None
3072 except:
3073 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
3074 self.logger.debug("Respond body {}".format(response.content))
3075
3076 return None
3077
3078 def create_vdc_rest(self, vdc_name=None):
3079 """
3080 Method create network in vCloud director
3081
3082 Args:
3083 network_name - is network name to be created.
3084 parent_network_uuid - is parent provider vdc network that will be used for mapping.
3085 It optional attribute. by default if no parent network indicate the first available will be used.
3086
3087 Returns:
3088 The return network uuid or return None
3089 """
3090
3091 self.logger.info("Creating new vdc {}".format(vdc_name))
3092
3093 vca = self.connect_as_admin()
3094 if not vca:
3095 raise vimconn.vimconnConnectionException("self.connect() is failed")
3096 if vdc_name is None:
3097 return None
3098
3099 url_list = [vca.host, '/api/admin/org/', self.org_uuid]
3100 vm_list_rest_call = ''.join(url_list)
3101 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
3102 response = Http.get(url=vm_list_rest_call,
3103 headers=vca.vcloud_session.get_vcloud_headers(),
3104 verify=vca.verify,
3105 logger=vca.logger)
3106
3107 provider_vdc_ref = None
3108 add_vdc_rest_url = None
3109 available_networks = None
3110
3111 if response.status_code != requests.codes.ok:
3112 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
3113 response.status_code))
3114 return None
3115 else:
3116 try:
3117 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
3118 for child in vm_list_xmlroot:
3119 # application/vnd.vmware.admin.providervdc+xml
3120 if child.tag.split("}")[1] == 'Link':
3121 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
3122 and child.attrib.get('rel') == 'add':
3123 add_vdc_rest_url = child.attrib.get('href')
3124 except:
3125 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
3126 self.logger.debug("Respond body {}".format(response.content))
3127 return None
3128
3129 response = self.get_provider_rest(vca=vca)
3130 try:
3131 vm_list_xmlroot = XmlElementTree.fromstring(response)
3132 for child in vm_list_xmlroot:
3133 if child.tag.split("}")[1] == 'ProviderVdcReferences':
3134 for sub_child in child:
3135 provider_vdc_ref = sub_child.attrib.get('href')
3136 except:
3137 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
3138 self.logger.debug("Respond body {}".format(response))
3139 return None
3140
3141 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
3142 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
3143 <AllocationModel>ReservationPool</AllocationModel>
3144 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
3145 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
3146 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
3147 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
3148 <ProviderVdcReference
3149 name="Main Provider"
3150 href="{2:s}" />
3151 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
3152 escape(vdc_name),
3153 provider_vdc_ref)
3154
3155 headers = vca.vcloud_session.get_vcloud_headers()
3156 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
3157 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
3158 logger=vca.logger)
3159
3160 # if we all ok we respond with content otherwise by default None
3161 if response.status_code == 201:
3162 return response.content
3163 return None
3164
3165 def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
3166 """
3167 Method retrieve vapp detail from vCloud director
3168
3169 Args:
3170 vapp_uuid - is vapp identifier.
3171
3172 Returns:
3173 The return network uuid or return None
3174 """
3175
3176 parsed_respond = {}
3177 vca = None
3178
3179 if need_admin_access:
3180 vca = self.connect_as_admin()
3181 else:
3182 vca = self.vca
3183
3184 if not vca:
3185 raise vimconn.vimconnConnectionException("self.connect() is failed")
3186 if vapp_uuid is None:
3187 return None
3188
3189 url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
3190 get_vapp_restcall = ''.join(url_list)
3191
3192 if vca.vcloud_session and vca.vcloud_session.organization:
3193 response = Http.get(url=get_vapp_restcall,
3194 headers=vca.vcloud_session.get_vcloud_headers(),
3195 verify=vca.verify,
3196 logger=vca.logger)
3197
3198 if response.status_code == 403:
3199 if need_admin_access == False:
3200 response = self.retry_rest('GET', get_vapp_restcall)
3201
3202 if response.status_code != requests.codes.ok:
3203 self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
3204 response.status_code))
3205 return parsed_respond
3206
3207 try:
3208 xmlroot_respond = XmlElementTree.fromstring(response.content)
3209 parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
3210
3211 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
3212 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
3213 'vmw': 'http://www.vmware.com/schema/ovf',
3214 'vm': 'http://www.vmware.com/vcloud/v1.5',
3215 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
3216 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3217 "xmlns":"http://www.vmware.com/vcloud/v1.5"
3218 }
3219
3220 created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
3221 if created_section is not None:
3222 parsed_respond['created'] = created_section.text
3223
3224 network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
3225 if network_section is not None and 'networkName' in network_section.attrib:
3226 parsed_respond['networkname'] = network_section.attrib['networkName']
3227
3228 ipscopes_section = \
3229 xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
3230 namespaces)
3231 if ipscopes_section is not None:
3232 for ipscope in ipscopes_section:
3233 for scope in ipscope:
3234 tag_key = scope.tag.split("}")[1]
3235 if tag_key == 'IpRanges':
3236 ip_ranges = scope.getchildren()
3237 for ipblock in ip_ranges:
3238 for block in ipblock:
3239 parsed_respond[block.tag.split("}")[1]] = block.text
3240 else:
3241 parsed_respond[tag_key] = scope.text
3242
3243 # parse children section for other attrib
3244 children_section = xmlroot_respond.find('vm:Children/', namespaces)
3245 if children_section is not None:
3246 parsed_respond['name'] = children_section.attrib['name']
3247 parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
3248 if "nestedHypervisorEnabled" in children_section.attrib else None
3249 parsed_respond['deployed'] = children_section.attrib['deployed']
3250 parsed_respond['status'] = children_section.attrib['status']
3251 parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
3252 network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
3253 nic_list = []
3254 for adapters in network_adapter:
3255 adapter_key = adapters.tag.split("}")[1]
3256 if adapter_key == 'PrimaryNetworkConnectionIndex':
3257 parsed_respond['primarynetwork'] = adapters.text
3258 if adapter_key == 'NetworkConnection':
3259 vnic = {}
3260 if 'network' in adapters.attrib:
3261 vnic['network'] = adapters.attrib['network']
3262 for adapter in adapters:
3263 setting_key = adapter.tag.split("}")[1]
3264 vnic[setting_key] = adapter.text
3265 nic_list.append(vnic)
3266
3267 for link in children_section:
3268 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
3269 if link.attrib['rel'] == 'screen:acquireTicket':
3270 parsed_respond['acquireTicket'] = link.attrib
3271 if link.attrib['rel'] == 'screen:acquireMksTicket':
3272 parsed_respond['acquireMksTicket'] = link.attrib
3273
3274 parsed_respond['interfaces'] = nic_list
3275 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
3276 if vCloud_extension_section is not None:
3277 vm_vcenter_info = {}
3278 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
3279 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
3280 if vmext is not None:
3281 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
3282 parsed_respond["vm_vcenter_info"]= vm_vcenter_info
3283
3284 virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
3285 vm_virtual_hardware_info = {}
3286 if virtual_hardware_section is not None:
3287 for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
3288 if item.find("rasd:Description",namespaces).text == "Hard disk":
3289 disk_size = item.find("rasd:HostResource" ,namespaces
3290 ).attrib["{"+namespaces['vm']+"}capacity"]
3291
3292 vm_virtual_hardware_info["disk_size"]= disk_size
3293 break
3294
3295 for link in virtual_hardware_section:
3296 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
3297 if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
3298 vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
3299 break
3300
3301 parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
3302 except Exception as exp :
3303 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
3304 return parsed_respond
3305
3306 def acuire_console(self, vm_uuid=None):
3307
3308 if vm_uuid is None:
3309 return None
3310
3311 if not (not self.vca.vcloud_session or not self.vca.vcloud_session.organization):
3312 vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
3313 console_dict = vm_dict['acquireTicket']
3314 console_rest_call = console_dict['href']
3315
3316 response = Http.post(url=console_rest_call,
3317 headers=self.vca.vcloud_session.get_vcloud_headers(),
3318 verify=self.vca.verify,
3319 logger=self.vca.logger)
3320 if response.status_code == 403:
3321 response = self.retry_rest('POST', console_rest_call)
3322
3323 if response.status_code == requests.codes.ok:
3324 return response.content
3325
3326 return None
3327
3328 def modify_vm_disk(self, vapp_uuid, flavor_disk):
3329 """
3330 Method retrieve vm disk details
3331
3332 Args:
3333 vapp_uuid - is vapp identifier.
3334 flavor_disk - disk size as specified in VNFD (flavor)
3335
3336 Returns:
3337 The return network uuid or return None
3338 """
3339 status = None
3340 try:
3341 #Flavor disk is in GB convert it into MB
3342 flavor_disk = int(flavor_disk) * 1024
3343 vm_details = self.get_vapp_details_rest(vapp_uuid)
3344 if vm_details:
3345 vm_name = vm_details["name"]
3346 self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
3347
3348 if vm_details and "vm_virtual_hardware" in vm_details:
3349 vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
3350 disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
3351
3352 self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
3353
3354 if flavor_disk > vm_disk:
3355 status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
3356 self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
3357 vm_disk, flavor_disk ))
3358 else:
3359 status = True
3360 self.logger.info("No need to modify disk of VM {}".format(vm_name))
3361
3362 return status
3363 except Exception as exp:
3364 self.logger.info("Error occurred while modifing disk size {}".format(exp))
3365
3366
3367 def modify_vm_disk_rest(self, disk_href , disk_size):
3368 """
3369 Method retrieve modify vm disk size
3370
3371 Args:
3372 disk_href - vCD API URL to GET and PUT disk data
3373 disk_size - disk size as specified in VNFD (flavor)
3374
3375 Returns:
3376 The return network uuid or return None
3377 """
3378 if disk_href is None or disk_size is None:
3379 return None
3380
3381 if self.vca.vcloud_session and self.vca.vcloud_session.organization:
3382 response = Http.get(url=disk_href,
3383 headers=self.vca.vcloud_session.get_vcloud_headers(),
3384 verify=self.vca.verify,
3385 logger=self.vca.logger)
3386
3387 if response.status_code == 403:
3388 response = self.retry_rest('GET', disk_href)
3389
3390 if response.status_code != requests.codes.ok:
3391 self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
3392 response.status_code))
3393 return None
3394 try:
3395 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
3396 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
3397 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3398
3399 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
3400 if item.find("rasd:Description",namespaces).text == "Hard disk":
3401 disk_item = item.find("rasd:HostResource" ,namespaces )
3402 if disk_item is not None:
3403 disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
3404 break
3405
3406 data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
3407 xml_declaration=True)
3408
3409 #Send PUT request to modify disk size
3410 headers = self.vca.vcloud_session.get_vcloud_headers()
3411 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3412
3413 response = Http.put(url=disk_href,
3414 data=data,
3415 headers=headers,
3416 verify=self.vca.verify, logger=self.logger)
3417
3418 if response.status_code == 403:
3419 add_headers = {'Content-Type': headers['Content-Type']}
3420 response = self.retry_rest('PUT', disk_href, add_headers, data)
3421
3422 if response.status_code != 202:
3423 self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
3424 response.status_code))
3425 else:
3426 modify_disk_task = taskType.parseString(response.content, True)
3427 if type(modify_disk_task) is GenericTask:
3428 status = self.vca.block_until_completed(modify_disk_task)
3429 return status
3430
3431 return None
3432
3433 except Exception as exp :
3434 self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
3435 return None
3436
3437 def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
3438 """
3439 Method to attach pci devices to VM
3440
3441 Args:
3442 vapp_uuid - uuid of vApp/VM
3443 pci_devices - pci devices infromation as specified in VNFD (flavor)
3444
3445 Returns:
3446 The status of add pci device task , vm object and
3447 vcenter_conect object
3448 """
3449 vm_obj = None
3450 self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
3451 vcenter_conect, content = self.get_vcenter_content()
3452 vm_moref_id = self.get_vm_moref_id(vapp_uuid)
3453
3454 if vm_moref_id:
3455 try:
3456 no_of_pci_devices = len(pci_devices)
3457 if no_of_pci_devices > 0:
3458 #Get VM and its host
3459 host_obj, vm_obj = self.get_vm_obj(content, vm_moref_id)
3460 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
3461 if host_obj and vm_obj:
3462 #get PCI devies from host on which vapp is currently installed
3463 avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
3464
3465 if avilable_pci_devices is None:
3466 #find other hosts with active pci devices
3467 new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
3468 content,
3469 no_of_pci_devices
3470 )
3471
3472 if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3473 #Migrate vm to the host where PCI devices are availble
3474 self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
3475 task = self.relocate_vm(new_host_obj, vm_obj)
3476 if task is not None:
3477 result = self.wait_for_vcenter_task(task, vcenter_conect)
3478 self.logger.info("Migrate VM status: {}".format(result))
3479 host_obj = new_host_obj
3480 else:
3481 self.logger.info("Fail to migrate VM : {}".format(result))
3482 raise vimconn.vimconnNotFoundException(
3483 "Fail to migrate VM : {} to host {}".format(
3484 vmname_andid,
3485 new_host_obj)
3486 )
3487
3488 if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3489 #Add PCI devices one by one
3490 for pci_device in avilable_pci_devices:
3491 task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
3492 if task:
3493 status= self.wait_for_vcenter_task(task, vcenter_conect)
3494 if status:
3495 self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3496 else:
3497 self.logger.error("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3498 return True, vm_obj, vcenter_conect
3499 else:
3500 self.logger.error("Currently there is no host with"\
3501 " {} number of avaialble PCI devices required for VM {}".format(
3502 no_of_pci_devices,
3503 vmname_andid)
3504 )
3505 raise vimconn.vimconnNotFoundException(
3506 "Currently there is no host with {} "\
3507 "number of avaialble PCI devices required for VM {}".format(
3508 no_of_pci_devices,
3509 vmname_andid))
3510 else:
3511 self.logger.debug("No infromation about PCI devices {} ",pci_devices)
3512
3513 except vmodl.MethodFault as error:
3514 self.logger.error("Error occurred while adding PCI devices {} ",error)
3515 return None, vm_obj, vcenter_conect
3516
3517 def get_vm_obj(self, content, mob_id):
3518 """
3519 Method to get the vsphere VM object associated with a given morf ID
3520 Args:
3521 vapp_uuid - uuid of vApp/VM
3522 content - vCenter content object
3523 mob_id - mob_id of VM
3524
3525 Returns:
3526 VM and host object
3527 """
3528 vm_obj = None
3529 host_obj = None
3530 try :
3531 container = content.viewManager.CreateContainerView(content.rootFolder,
3532 [vim.VirtualMachine], True
3533 )
3534 for vm in container.view:
3535 mobID = vm._GetMoId()
3536 if mobID == mob_id:
3537 vm_obj = vm
3538 host_obj = vm_obj.runtime.host
3539 break
3540 except Exception as exp:
3541 self.logger.error("Error occurred while finding VM object : {}".format(exp))
3542 return host_obj, vm_obj
3543
3544 def get_pci_devices(self, host, need_devices):
3545 """
3546 Method to get the details of pci devices on given host
3547 Args:
3548 host - vSphere host object
3549 need_devices - number of pci devices needed on host
3550
3551 Returns:
3552 array of pci devices
3553 """
3554 all_devices = []
3555 all_device_ids = []
3556 used_devices_ids = []
3557
3558 try:
3559 if host:
3560 pciPassthruInfo = host.config.pciPassthruInfo
3561 pciDevies = host.hardware.pciDevice
3562
3563 for pci_status in pciPassthruInfo:
3564 if pci_status.passthruActive:
3565 for device in pciDevies:
3566 if device.id == pci_status.id:
3567 all_device_ids.append(device.id)
3568 all_devices.append(device)
3569
3570 #check if devices are in use
3571 avalible_devices = all_devices
3572 for vm in host.vm:
3573 if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
3574 vm_devices = vm.config.hardware.device
3575 for device in vm_devices:
3576 if type(device) is vim.vm.device.VirtualPCIPassthrough:
3577 if device.backing.id in all_device_ids:
3578 for use_device in avalible_devices:
3579 if use_device.id == device.backing.id:
3580 avalible_devices.remove(use_device)
3581 used_devices_ids.append(device.backing.id)
3582 self.logger.debug("Device {} from devices {}"\
3583 "is in use".format(device.backing.id,
3584 device)
3585 )
3586 if len(avalible_devices) < need_devices:
3587 self.logger.debug("Host {} don't have {} number of active devices".format(host,
3588 need_devices))
3589 self.logger.debug("found only {} devives {}".format(len(avalible_devices),
3590 avalible_devices))
3591 return None
3592 else:
3593 required_devices = avalible_devices[:need_devices]
3594 self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
3595 len(avalible_devices),
3596 host,
3597 need_devices))
3598 self.logger.info("Retruning {} devices as {}".format(need_devices,
3599 required_devices ))
3600 return required_devices
3601
3602 except Exception as exp:
3603 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
3604
3605 return None
3606
3607 def get_host_and_PCIdevices(self, content, need_devices):
3608 """
3609 Method to get the details of pci devices infromation on all hosts
3610
3611 Args:
3612 content - vSphere host object
3613 need_devices - number of pci devices needed on host
3614
3615 Returns:
3616 array of pci devices and host object
3617 """
3618 host_obj = None
3619 pci_device_objs = None
3620 try:
3621 if content:
3622 container = content.viewManager.CreateContainerView(content.rootFolder,
3623 [vim.HostSystem], True)
3624 for host in container.view:
3625 devices = self.get_pci_devices(host, need_devices)
3626 if devices:
3627 host_obj = host
3628 pci_device_objs = devices
3629 break
3630 except Exception as exp:
3631 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
3632
3633 return host_obj,pci_device_objs
3634
3635 def relocate_vm(self, dest_host, vm) :
3636 """
3637 Method to get the relocate VM to new host
3638
3639 Args:
3640 dest_host - vSphere host object
3641 vm - vSphere VM object
3642
3643 Returns:
3644 task object
3645 """
3646 task = None
3647 try:
3648 relocate_spec = vim.vm.RelocateSpec(host=dest_host)
3649 task = vm.Relocate(relocate_spec)
3650 self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
3651 except Exception as exp:
3652 self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
3653 dest_host, vm, exp))
3654 return task
3655
3656 def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
3657 """
3658 Waits and provides updates on a vSphere task
3659 """
3660 while task.info.state == vim.TaskInfo.State.running:
3661 time.sleep(2)
3662
3663 if task.info.state == vim.TaskInfo.State.success:
3664 if task.info.result is not None and not hideResult:
3665 self.logger.info('{} completed successfully, result: {}'.format(
3666 actionName,
3667 task.info.result))
3668 else:
3669 self.logger.info('Task {} completed successfully.'.format(actionName))
3670 else:
3671 self.logger.error('{} did not complete successfully: {} '.format(
3672 actionName,
3673 task.info.error)
3674 )
3675
3676 return task.info.result
3677
3678 def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
3679 """
3680 Method to add pci device in given VM
3681
3682 Args:
3683 host_object - vSphere host object
3684 vm_object - vSphere VM object
3685 host_pci_dev - host_pci_dev must be one of the devices from the
3686 host_object.hardware.pciDevice list
3687 which is configured as a PCI passthrough device
3688
3689 Returns:
3690 task object
3691 """
3692 task = None
3693 if vm_object and host_object and host_pci_dev:
3694 try :
3695 #Add PCI device to VM
3696 pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
3697 systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
3698
3699 if host_pci_dev.id not in systemid_by_pciid:
3700 self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
3701 return None
3702
3703 deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
3704 backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
3705 id=host_pci_dev.id,
3706 systemId=systemid_by_pciid[host_pci_dev.id],
3707 vendorId=host_pci_dev.vendorId,
3708 deviceName=host_pci_dev.deviceName)
3709
3710 hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
3711
3712 new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
3713 new_device_config.operation = "add"
3714 vmConfigSpec = vim.vm.ConfigSpec()
3715 vmConfigSpec.deviceChange = [new_device_config]
3716
3717 task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
3718 self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
3719 host_pci_dev, vm_object, host_object)
3720 )
3721 except Exception as exp:
3722 self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3723 host_pci_dev,
3724 vm_object,
3725 exp))
3726 return task
3727
3728 def get_vm_vcenter_info(self):
3729 """
3730 Method to get details of vCenter and vm
3731
3732 Args:
3733 vapp_uuid - uuid of vApp or VM
3734
3735 Returns:
3736 Moref Id of VM and deails of vCenter
3737 """
3738 vm_vcenter_info = {}
3739
3740 if self.vcenter_ip is not None:
3741 vm_vcenter_info["vm_vcenter_ip"] = self.vcenter_ip
3742 else:
3743 raise vimconn.vimconnException(message="vCenter IP is not provided."\
3744 " Please provide vCenter IP while attaching datacenter to tenant in --config")
3745 if self.vcenter_port is not None:
3746 vm_vcenter_info["vm_vcenter_port"] = self.vcenter_port
3747 else:
3748 raise vimconn.vimconnException(message="vCenter port is not provided."\
3749 " Please provide vCenter port while attaching datacenter to tenant in --config")
3750 if self.vcenter_user is not None:
3751 vm_vcenter_info["vm_vcenter_user"] = self.vcenter_user
3752 else:
3753 raise vimconn.vimconnException(message="vCenter user is not provided."\
3754 " Please provide vCenter user while attaching datacenter to tenant in --config")
3755
3756 if self.vcenter_password is not None:
3757 vm_vcenter_info["vm_vcenter_password"] = self.vcenter_password
3758 else:
3759 raise vimconn.vimconnException(message="vCenter user password is not provided."\
3760 " Please provide vCenter user password while attaching datacenter to tenant in --config")
3761
3762 return vm_vcenter_info
3763
3764
3765 def get_vm_pci_details(self, vmuuid):
3766 """
3767 Method to get VM PCI device details from vCenter
3768
3769 Args:
3770 vm_obj - vSphere VM object
3771
3772 Returns:
3773 dict of PCI devives attached to VM
3774
3775 """
3776 vm_pci_devices_info = {}
3777 try:
3778 vcenter_conect, content = self.get_vcenter_content()
3779 vm_moref_id = self.get_vm_moref_id(vmuuid)
3780 if vm_moref_id:
3781 #Get VM and its host
3782 if content:
3783 host_obj, vm_obj = self.get_vm_obj(content, vm_moref_id)
3784 if host_obj and vm_obj:
3785 vm_pci_devices_info["host_name"]= host_obj.name
3786 vm_pci_devices_info["host_ip"]= host_obj.config.network.vnic[0].spec.ip.ipAddress
3787 for device in vm_obj.config.hardware.device:
3788 if type(device) == vim.vm.device.VirtualPCIPassthrough:
3789 device_details={'devide_id':device.backing.id,
3790 'pciSlotNumber':device.slotInfo.pciSlotNumber,
3791 }
3792 vm_pci_devices_info[device.deviceInfo.label] = device_details
3793 else:
3794 self.logger.error("Can not connect to vCenter while getting "\
3795 "PCI devices infromationn")
3796 return vm_pci_devices_info
3797 except Exception as exp:
3798 self.logger.error("Error occurred while getting VM infromationn"\
3799 " for VM : {}".format(exp))
3800 raise vimconn.vimconnException(message=exp)
3801
3802 def add_network_adapter_to_vms(self, vapp, network_name, primary_nic_index, nicIndex, net, nic_type=None):
3803 """
3804 Method to add network adapter type to vm
3805 Args :
3806 network_name - name of network
3807 primary_nic_index - int value for primary nic index
3808 nicIndex - int value for nic index
3809 nic_type - specify model name to which add to vm
3810 Returns:
3811 None
3812 """
3813
3814 try:
3815 ip_address = None
3816 floating_ip = False
3817 if 'floating_ip' in net: floating_ip = net['floating_ip']
3818
3819 # Stub for ip_address feature
3820 if 'ip_address' in net: ip_address = net['ip_address']
3821
3822 if floating_ip:
3823 allocation_mode = "POOL"
3824 elif ip_address:
3825 allocation_mode = "MANUAL"
3826 else:
3827 allocation_mode = "DHCP"
3828
3829 if not nic_type:
3830 for vms in vapp._get_vms():
3831 vm_id = (vms.id).split(':')[-1]
3832
3833 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(self.vca.host, vm_id)
3834
3835 response = Http.get(url=url_rest_call,
3836 headers=self.vca.vcloud_session.get_vcloud_headers(),
3837 verify=self.vca.verify,
3838 logger=self.vca.logger)
3839
3840 if response.status_code == 403:
3841 response = self.retry_rest('GET', url_rest_call)
3842
3843 if response.status_code != 200:
3844 self.logger.error("REST call {} failed reason : {}"\
3845 "status code : {}".format(url_rest_call,
3846 response.content,
3847 response.status_code))
3848 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to get "\
3849 "network connection section")
3850
3851 data = response.content
3852 if '<PrimaryNetworkConnectionIndex>' not in data:
3853 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3854 <NetworkConnection network="{}">
3855 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3856 <IsConnected>true</IsConnected>
3857 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3858 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex,
3859 allocation_mode)
3860 # Stub for ip_address feature
3861 if ip_address:
3862 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
3863 item = item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
3864
3865 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item))
3866 else:
3867 new_item = """<NetworkConnection network="{}">
3868 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3869 <IsConnected>true</IsConnected>
3870 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3871 </NetworkConnection>""".format(network_name, nicIndex,
3872 allocation_mode)
3873 # Stub for ip_address feature
3874 if ip_address:
3875 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
3876 new_item = new_item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
3877
3878 data = data.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item))
3879
3880 headers = self.vca.vcloud_session.get_vcloud_headers()
3881 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3882 response = Http.put(url=url_rest_call, headers=headers, data=data,
3883 verify=self.vca.verify,
3884 logger=self.vca.logger)
3885
3886 if response.status_code == 403:
3887 add_headers = {'Content-Type': headers['Content-Type']}
3888 response = self.retry_rest('PUT', url_rest_call, add_headers, data)
3889
3890 if response.status_code != 202:
3891 self.logger.error("REST call {} failed reason : {}"\
3892 "status code : {} ".format(url_rest_call,
3893 response.content,
3894 response.status_code))
3895 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to update "\
3896 "network connection section")
3897 else:
3898 nic_task = taskType.parseString(response.content, True)
3899 if isinstance(nic_task, GenericTask):
3900 self.vca.block_until_completed(nic_task)
3901 self.logger.info("add_network_adapter_to_vms(): VM {} conneced to "\
3902 "default NIC type".format(vm_id))
3903 else:
3904 self.logger.error("add_network_adapter_to_vms(): VM {} failed to "\
3905 "connect NIC type".format(vm_id))
3906 else:
3907 for vms in vapp._get_vms():
3908 vm_id = (vms.id).split(':')[-1]
3909
3910 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(self.vca.host, vm_id)
3911
3912 response = Http.get(url=url_rest_call,
3913 headers=self.vca.vcloud_session.get_vcloud_headers(),
3914 verify=self.vca.verify,
3915 logger=self.vca.logger)
3916
3917 if response.status_code == 403:
3918 response = self.retry_rest('GET', url_rest_call)
3919
3920 if response.status_code != 200:
3921 self.logger.error("REST call {} failed reason : {}"\
3922 "status code : {}".format(url_rest_call,
3923 response.content,
3924 response.status_code))
3925 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to get "\
3926 "network connection section")
3927 data = response.content
3928 if '<PrimaryNetworkConnectionIndex>' not in data:
3929 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3930 <NetworkConnection network="{}">
3931 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3932 <IsConnected>true</IsConnected>
3933 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3934 <NetworkAdapterType>{}</NetworkAdapterType>
3935 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex,
3936 allocation_mode, nic_type)
3937 # Stub for ip_address feature
3938 if ip_address:
3939 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
3940 item = item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
3941
3942 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item))
3943 else:
3944 new_item = """<NetworkConnection network="{}">
3945 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3946 <IsConnected>true</IsConnected>
3947 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3948 <NetworkAdapterType>{}</NetworkAdapterType>
3949 </NetworkConnection>""".format(network_name, nicIndex,
3950 allocation_mode, nic_type)
3951 # Stub for ip_address feature
3952 if ip_address:
3953 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
3954 new_item = new_item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
3955
3956 data = data.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item))
3957
3958 headers = self.vca.vcloud_session.get_vcloud_headers()
3959 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3960 response = Http.put(url=url_rest_call, headers=headers, data=data,
3961 verify=self.vca.verify,
3962 logger=self.vca.logger)
3963
3964 if response.status_code == 403:
3965 add_headers = {'Content-Type': headers['Content-Type']}
3966 response = self.retry_rest('PUT', url_rest_call, add_headers, data)
3967
3968 if response.status_code != 202:
3969 self.logger.error("REST call {} failed reason : {}"\
3970 "status code : {}".format(url_rest_call,
3971 response.content,
3972 response.status_code))
3973 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to update "\
3974 "network connection section")
3975 else:
3976 nic_task = taskType.parseString(response.content, True)
3977 if isinstance(nic_task, GenericTask):
3978 self.vca.block_until_completed(nic_task)
3979 self.logger.info("add_network_adapter_to_vms(): VM {} "\
3980 "conneced to NIC type {}".format(vm_id, nic_type))
3981 else:
3982 self.logger.error("add_network_adapter_to_vms(): VM {} "\
3983 "failed to connect NIC type {}".format(vm_id, nic_type))
3984 except Exception as exp:
3985 self.logger.error("add_network_adapter_to_vms() : exception occurred "\
3986 "while adding Network adapter")
3987 raise vimconn.vimconnException(message=exp)
3988
3989
3990 def set_numa_affinity(self, vmuuid, paired_threads_id):
3991 """
3992 Method to assign numa affinity in vm configuration parammeters
3993 Args :
3994 vmuuid - vm uuid
3995 paired_threads_id - one or more virtual processor
3996 numbers
3997 Returns:
3998 return if True
3999 """
4000 try:
4001 vm_moref_id , vm_vcenter_host , vm_vcenter_username, vm_vcenter_port = self.get_vcenter_info_rest(vmuuid)
4002 if vm_moref_id and vm_vcenter_host and vm_vcenter_username:
4003 context = None
4004 if hasattr(ssl, '_create_unverified_context'):
4005 context = ssl._create_unverified_context()
4006 vcenter_conect = SmartConnect(host=vm_vcenter_host, user=vm_vcenter_username,
4007 pwd=self.passwd, port=int(vm_vcenter_port),
4008 sslContext=context)
4009 atexit.register(Disconnect, vcenter_conect)
4010 content = vcenter_conect.RetrieveContent()
4011
4012 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
4013 if vm_obj:
4014 config_spec = vim.vm.ConfigSpec()
4015 config_spec.extraConfig = []
4016 opt = vim.option.OptionValue()
4017 opt.key = 'numa.nodeAffinity'
4018 opt.value = str(paired_threads_id)
4019 config_spec.extraConfig.append(opt)
4020 task = vm_obj.ReconfigVM_Task(config_spec)
4021 if task:
4022 result = self.wait_for_vcenter_task(task, vcenter_conect)
4023 extra_config = vm_obj.config.extraConfig
4024 flag = False
4025 for opts in extra_config:
4026 if 'numa.nodeAffinity' in opts.key:
4027 flag = True
4028 self.logger.info("set_numa_affinity: Sucessfully assign numa affinity "\
4029 "value {} for vm {}".format(opt.value, vm_obj))
4030 if flag:
4031 return
4032 else:
4033 self.logger.error("set_numa_affinity: Failed to assign numa affinity")
4034 except Exception as exp:
4035 self.logger.error("set_numa_affinity : exception occurred while setting numa affinity "\
4036 "for VM {} : {}".format(vm_obj, vm_moref_id))
4037 raise vimconn.vimconnException("set_numa_affinity : Error {} failed to assign numa "\
4038 "affinity".format(exp))
4039
4040
4041 def cloud_init(self, vapp, cloud_config):
4042 """
4043 Method to inject ssh-key
4044 vapp - vapp object
4045 cloud_config a dictionary with:
4046 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
4047 'users': (optional) list of users to be inserted, each item is a dict with:
4048 'name': (mandatory) user name,
4049 'key-pairs': (optional) list of strings with the public key to be inserted to the user
4050 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
4051 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
4052 'config-files': (optional). List of files to be transferred. Each item is a dict with:
4053 'dest': (mandatory) string with the destination absolute path
4054 'encoding': (optional, by default text). Can be one of:
4055 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
4056 'content' (mandatory): string with the content of the file
4057 'permissions': (optional) string with file permissions, typically octal notation '0644'
4058 'owner': (optional) file owner, string with the format 'owner:group'
4059 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk
4060 """
4061 try:
4062 if not isinstance(cloud_config, dict):
4063 raise Exception("cloud_init : parameter cloud_config is not a dictionary")
4064 else:
4065 key_pairs = []
4066 userdata = []
4067 if "key-pairs" in cloud_config:
4068 key_pairs = cloud_config["key-pairs"]
4069
4070 if "users" in cloud_config:
4071 userdata = cloud_config["users"]
4072
4073 self.logger.debug("cloud_init : Guest os customization started..")
4074 customize_script = self.format_script(key_pairs=key_pairs, users_list=userdata)
4075 self.guest_customization(vapp, customize_script)
4076
4077 except Exception as exp:
4078 self.logger.error("cloud_init : exception occurred while injecting "\
4079 "ssh-key")
4080 raise vimconn.vimconnException("cloud_init : Error {} failed to inject "\
4081 "ssh-key".format(exp))
4082
4083 def format_script(self, key_pairs=[], users_list=[]):
4084 bash_script = """
4085 #!/bin/bash
4086 echo performing customization tasks with param $1 at `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
4087 if [ "$1" = "precustomization" ];then
4088 echo performing precustomization tasks on `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
4089 """
4090
4091 keys = "\n".join(key_pairs)
4092 if keys:
4093 keys_data = """
4094 if [ ! -d /root/.ssh ];then
4095 mkdir /root/.ssh
4096 chown root:root /root/.ssh
4097 chmod 700 /root/.ssh
4098 touch /root/.ssh/authorized_keys
4099 chown root:root /root/.ssh/authorized_keys
4100 chmod 600 /root/.ssh/authorized_keys
4101 # make centos with selinux happy
4102 which restorecon && restorecon -Rv /root/.ssh
4103 else
4104 touch /root/.ssh/authorized_keys
4105 chown root:root /root/.ssh/authorized_keys
4106 chmod 600 /root/.ssh/authorized_keys
4107 fi
4108 echo '{key}' >> /root/.ssh/authorized_keys
4109 """.format(key=keys)
4110
4111 bash_script+= keys_data
4112
4113 for user in users_list:
4114 if 'name' in user: user_name = user['name']
4115 if 'key-pairs' in user:
4116 user_keys = "\n".join(user['key-pairs'])
4117 else:
4118 user_keys = None
4119
4120 add_user_name = """
4121 useradd -d /home/{user_name} -m -g users -s /bin/bash {user_name}
4122 """.format(user_name=user_name)
4123
4124 bash_script+= add_user_name
4125
4126 if user_keys:
4127 user_keys_data = """
4128 mkdir /home/{user_name}/.ssh
4129 chown {user_name}:{user_name} /home/{user_name}/.ssh
4130 chmod 700 /home/{user_name}/.ssh
4131 touch /home/{user_name}/.ssh/authorized_keys
4132 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
4133 chmod 600 /home/{user_name}/.ssh/authorized_keys
4134 # make centos with selinux happy
4135 which restorecon && restorecon -Rv /home/{user_name}/.ssh
4136 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
4137 """.format(user_name=user_name,user_key=user_keys)
4138
4139 bash_script+= user_keys_data
4140
4141 return bash_script+"\n\tfi"
4142
4143 def guest_customization(self, vapp, customize_script):
4144 """
4145 Method to customize guest os
4146 vapp - Vapp object
4147 customize_script - Customize script to be run at first boot of VM.
4148 """
4149 for vm in vapp._get_vms():
4150 vm_name = vm.name
4151 task = vapp.customize_guest_os(vm_name, customization_script=customize_script)
4152 if isinstance(task, GenericTask):
4153 self.vca.block_until_completed(task)
4154 self.logger.info("guest_customization : customized guest os task "\
4155 "completed for VM {}".format(vm_name))
4156 else:
4157 self.logger.error("guest_customization : task for customized guest os"\
4158 "failed for VM {}".format(vm_name))
4159 raise vimconn.vimconnException("guest_customization : failed to perform"\
4160 "guest os customization on VM {}".format(vm_name))
4161
4162 def add_new_disk(self, vapp_uuid, disk_size):
4163 """
4164 Method to create an empty vm disk
4165
4166 Args:
4167 vapp_uuid - is vapp identifier.
4168 disk_size - size of disk to be created in GB
4169
4170 Returns:
4171 None
4172 """
4173 status = False
4174 vm_details = None
4175 try:
4176 #Disk size in GB, convert it into MB
4177 if disk_size is not None:
4178 disk_size_mb = int(disk_size) * 1024
4179 vm_details = self.get_vapp_details_rest(vapp_uuid)
4180
4181 if vm_details and "vm_virtual_hardware" in vm_details:
4182 self.logger.info("Adding disk to VM: {} disk size:{}GB".format(vm_details["name"], disk_size))
4183 disk_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
4184 status = self.add_new_disk_rest(disk_href, disk_size_mb)
4185
4186 except Exception as exp:
4187 msg = "Error occurred while creating new disk {}.".format(exp)
4188 self.rollback_newvm(vapp_uuid, msg)
4189
4190 if status:
4191 self.logger.info("Added new disk to VM: {} disk size:{}GB".format(vm_details["name"], disk_size))
4192 else:
4193 #If failed to add disk, delete VM
4194 msg = "add_new_disk: Failed to add new disk to {}".format(vm_details["name"])
4195 self.rollback_newvm(vapp_uuid, msg)
4196
4197
4198 def add_new_disk_rest(self, disk_href, disk_size_mb):
4199 """
4200 Retrives vApp Disks section & add new empty disk
4201
4202 Args:
4203 disk_href: Disk section href to addd disk
4204 disk_size_mb: Disk size in MB
4205
4206 Returns: Status of add new disk task
4207 """
4208 status = False
4209 if self.vca.vcloud_session and self.vca.vcloud_session.organization:
4210 response = Http.get(url=disk_href,
4211 headers=self.vca.vcloud_session.get_vcloud_headers(),
4212 verify=self.vca.verify,
4213 logger=self.vca.logger)
4214
4215 if response.status_code == 403:
4216 response = self.retry_rest('GET', disk_href)
4217
4218 if response.status_code != requests.codes.ok:
4219 self.logger.error("add_new_disk_rest: GET REST API call {} failed. Return status code {}"
4220 .format(disk_href, response.status_code))
4221 return status
4222 try:
4223 #Find but type & max of instance IDs assigned to disks
4224 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
4225 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
4226 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
4227 instance_id = 0
4228 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
4229 if item.find("rasd:Description",namespaces).text == "Hard disk":
4230 inst_id = int(item.find("rasd:InstanceID" ,namespaces).text)
4231 if inst_id > instance_id:
4232 instance_id = inst_id
4233 disk_item = item.find("rasd:HostResource" ,namespaces)
4234 bus_subtype = disk_item.attrib["{"+namespaces['xmlns']+"}busSubType"]
4235 bus_type = disk_item.attrib["{"+namespaces['xmlns']+"}busType"]
4236
4237 instance_id = instance_id + 1
4238 new_item = """<Item>
4239 <rasd:Description>Hard disk</rasd:Description>
4240 <rasd:ElementName>New disk</rasd:ElementName>
4241 <rasd:HostResource
4242 xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
4243 vcloud:capacity="{}"
4244 vcloud:busSubType="{}"
4245 vcloud:busType="{}"></rasd:HostResource>
4246 <rasd:InstanceID>{}</rasd:InstanceID>
4247 <rasd:ResourceType>17</rasd:ResourceType>
4248 </Item>""".format(disk_size_mb, bus_subtype, bus_type, instance_id)
4249
4250 new_data = response.content
4251 #Add new item at the bottom
4252 new_data = new_data.replace('</Item>\n</RasdItemsList>', '</Item>\n{}\n</RasdItemsList>'.format(new_item))
4253
4254 # Send PUT request to modify virtual hardware section with new disk
4255 headers = self.vca.vcloud_session.get_vcloud_headers()
4256 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
4257
4258 response = Http.put(url=disk_href,
4259 data=new_data,
4260 headers=headers,
4261 verify=self.vca.verify, logger=self.logger)
4262
4263 if response.status_code == 403:
4264 add_headers = {'Content-Type': headers['Content-Type']}
4265 response = self.retry_rest('PUT', disk_href, add_headers, new_data)
4266
4267 if response.status_code != 202:
4268 self.logger.error("PUT REST API call {} failed. Return status code {}. Response Content:{}"
4269 .format(disk_href, response.status_code, response.content))
4270 else:
4271 add_disk_task = taskType.parseString(response.content, True)
4272 if type(add_disk_task) is GenericTask:
4273 status = self.vca.block_until_completed(add_disk_task)
4274 if not status:
4275 self.logger.error("Add new disk REST task failed to add {} MB disk".format(disk_size_mb))
4276
4277 except Exception as exp:
4278 self.logger.error("Error occurred calling rest api for creating new disk {}".format(exp))
4279
4280 return status
4281
4282
4283 def add_existing_disk(self, catalogs=None, image_id=None, size=None, template_name=None, vapp_uuid=None):
4284 """
4285 Method to add existing disk to vm
4286 Args :
4287 catalogs - List of VDC catalogs
4288 image_id - Catalog ID
4289 template_name - Name of template in catalog
4290 vapp_uuid - UUID of vApp
4291 Returns:
4292 None
4293 """
4294 disk_info = None
4295 vcenter_conect, content = self.get_vcenter_content()
4296 #find moref-id of vm in image
4297 catalog_vm_info = self.get_vapp_template_details(catalogs=catalogs,
4298 image_id=image_id,
4299 )
4300
4301 if catalog_vm_info and "vm_vcenter_info" in catalog_vm_info:
4302 if "vm_moref_id" in catalog_vm_info["vm_vcenter_info"]:
4303 catalog_vm_moref_id = catalog_vm_info["vm_vcenter_info"].get("vm_moref_id", None)
4304 if catalog_vm_moref_id:
4305 self.logger.info("Moref_id of VM in catalog : {}" .format(catalog_vm_moref_id))
4306 host, catalog_vm_obj = self.get_vm_obj(content, catalog_vm_moref_id)
4307 if catalog_vm_obj:
4308 #find existing disk
4309 disk_info = self.find_disk(catalog_vm_obj)
4310 else:
4311 exp_msg = "No VM with image id {} found".format(image_id)
4312 self.rollback_newvm(vapp_uuid, exp_msg, exp_type="NotFound")
4313 else:
4314 exp_msg = "No Image found with image ID {} ".format(image_id)
4315 self.rollback_newvm(vapp_uuid, exp_msg, exp_type="NotFound")
4316
4317 if disk_info:
4318 self.logger.info("Existing disk_info : {}".format(disk_info))
4319 #get VM
4320 vm_moref_id = self.get_vm_moref_id(vapp_uuid)
4321 host, vm_obj = self.get_vm_obj(content, vm_moref_id)
4322 if vm_obj:
4323 status = self.add_disk(vcenter_conect=vcenter_conect,
4324 vm=vm_obj,
4325 disk_info=disk_info,
4326 size=size,
4327 vapp_uuid=vapp_uuid
4328 )
4329 if status:
4330 self.logger.info("Disk from image id {} added to {}".format(image_id,
4331 vm_obj.config.name)
4332 )
4333 else:
4334 msg = "No disk found with image id {} to add in VM {}".format(
4335 image_id,
4336 vm_obj.config.name)
4337 self.rollback_newvm(vapp_uuid, msg, exp_type="NotFound")
4338
4339
4340 def find_disk(self, vm_obj):
4341 """
4342 Method to find details of existing disk in VM
4343 Args :
4344 vm_obj - vCenter object of VM
4345 image_id - Catalog ID
4346 Returns:
4347 disk_info : dict of disk details
4348 """
4349 disk_info = {}
4350 if vm_obj:
4351 try:
4352 devices = vm_obj.config.hardware.device
4353 for device in devices:
4354 if type(device) is vim.vm.device.VirtualDisk:
4355 if isinstance(device.backing,vim.vm.device.VirtualDisk.FlatVer2BackingInfo) and hasattr(device.backing, 'fileName'):
4356 disk_info["full_path"] = device.backing.fileName
4357 disk_info["datastore"] = device.backing.datastore
4358 disk_info["capacityKB"] = device.capacityInKB
4359 break
4360 except Exception as exp:
4361 self.logger.error("find_disk() : exception occurred while "\
4362 "getting existing disk details :{}".format(exp))
4363 return disk_info
4364
4365
4366 def add_disk(self, vcenter_conect=None, vm=None, size=None, vapp_uuid=None, disk_info={}):
4367 """
4368 Method to add existing disk in VM
4369 Args :
4370 vcenter_conect - vCenter content object
4371 vm - vCenter vm object
4372 disk_info : dict of disk details
4373 Returns:
4374 status : status of add disk task
4375 """
4376 datastore = disk_info["datastore"] if "datastore" in disk_info else None
4377 fullpath = disk_info["full_path"] if "full_path" in disk_info else None
4378 capacityKB = disk_info["capacityKB"] if "capacityKB" in disk_info else None
4379 if size is not None:
4380 #Convert size from GB to KB
4381 sizeKB = int(size) * 1024 * 1024
4382 #compare size of existing disk and user given size.Assign whicherver is greater
4383 self.logger.info("Add Existing disk : sizeKB {} , capacityKB {}".format(
4384 sizeKB, capacityKB))
4385 if sizeKB > capacityKB:
4386 capacityKB = sizeKB
4387
4388 if datastore and fullpath and capacityKB:
4389 try:
4390 spec = vim.vm.ConfigSpec()
4391 # get all disks on a VM, set unit_number to the next available
4392 unit_number = 0
4393 for dev in vm.config.hardware.device:
4394 if hasattr(dev.backing, 'fileName'):
4395 unit_number = int(dev.unitNumber) + 1
4396 # unit_number 7 reserved for scsi controller
4397 if unit_number == 7:
4398 unit_number += 1
4399 if isinstance(dev, vim.vm.device.VirtualDisk):
4400 #vim.vm.device.VirtualSCSIController
4401 controller_key = dev.controllerKey
4402
4403 self.logger.info("Add Existing disk : unit number {} , controller key {}".format(
4404 unit_number, controller_key))
4405 # add disk here
4406 dev_changes = []
4407 disk_spec = vim.vm.device.VirtualDeviceSpec()
4408 disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
4409 disk_spec.device = vim.vm.device.VirtualDisk()
4410 disk_spec.device.backing = \
4411 vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
4412 disk_spec.device.backing.thinProvisioned = True
4413 disk_spec.device.backing.diskMode = 'persistent'
4414 disk_spec.device.backing.datastore = datastore
4415 disk_spec.device.backing.fileName = fullpath
4416
4417 disk_spec.device.unitNumber = unit_number
4418 disk_spec.device.capacityInKB = capacityKB
4419 disk_spec.device.controllerKey = controller_key
4420 dev_changes.append(disk_spec)
4421 spec.deviceChange = dev_changes
4422 task = vm.ReconfigVM_Task(spec=spec)
4423 status = self.wait_for_vcenter_task(task, vcenter_conect)
4424 return status
4425 except Exception as exp:
4426 exp_msg = "add_disk() : exception {} occurred while adding disk "\
4427 "{} to vm {}".format(exp,
4428 fullpath,
4429 vm.config.name)
4430 self.rollback_newvm(vapp_uuid, exp_msg)
4431 else:
4432 msg = "add_disk() : Can not add disk to VM with disk info {} ".format(disk_info)
4433 self.rollback_newvm(vapp_uuid, msg)
4434
4435
4436 def get_vcenter_content(self):
4437 """
4438 Get the vsphere content object
4439 """
4440 try:
4441 vm_vcenter_info = self.get_vm_vcenter_info()
4442 except Exception as exp:
4443 self.logger.error("Error occurred while getting vCenter infromationn"\
4444 " for VM : {}".format(exp))
4445 raise vimconn.vimconnException(message=exp)
4446
4447 context = None
4448 if hasattr(ssl, '_create_unverified_context'):
4449 context = ssl._create_unverified_context()
4450
4451 vcenter_conect = SmartConnect(
4452 host=vm_vcenter_info["vm_vcenter_ip"],
4453 user=vm_vcenter_info["vm_vcenter_user"],
4454 pwd=vm_vcenter_info["vm_vcenter_password"],
4455 port=int(vm_vcenter_info["vm_vcenter_port"]),
4456 sslContext=context
4457 )
4458 atexit.register(Disconnect, vcenter_conect)
4459 content = vcenter_conect.RetrieveContent()
4460 return vcenter_conect, content
4461
4462
4463 def get_vm_moref_id(self, vapp_uuid):
4464 """
4465 Get the moref_id of given VM
4466 """
4467 try:
4468 if vapp_uuid:
4469 vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
4470 if vm_details and "vm_vcenter_info" in vm_details:
4471 vm_moref_id = vm_details["vm_vcenter_info"].get("vm_moref_id", None)
4472
4473 return vm_moref_id
4474
4475 except Exception as exp:
4476 self.logger.error("Error occurred while getting VM moref ID "\
4477 " for VM : {}".format(exp))
4478 return None
4479
4480
4481 def get_vapp_template_details(self, catalogs=None, image_id=None , template_name=None):
4482 """
4483 Method to get vApp template details
4484 Args :
4485 catalogs - list of VDC catalogs
4486 image_id - Catalog ID to find
4487 template_name : template name in catalog
4488 Returns:
4489 parsed_respond : dict of vApp tempalte details
4490 """
4491 parsed_response = {}
4492
4493 vca = self.connect_as_admin()
4494 if not vca:
4495 raise vimconn.vimconnConnectionException("self.connect() is failed")
4496
4497 try:
4498 catalog = self.get_catalog_obj(image_id, catalogs)
4499 if catalog:
4500 template_name = self.get_catalogbyid(image_id, catalogs)
4501 catalog_items = [catalogItemRef for catalogItemRef in catalog.get_CatalogItems().get_CatalogItem() if catalogItemRef.get_name() == template_name]
4502 if len(catalog_items) == 1:
4503 response = Http.get(catalog_items[0].get_href(),
4504 headers=vca.vcloud_session.get_vcloud_headers(),
4505 verify=vca.verify,
4506 logger=vca.logger)
4507 catalogItem = XmlElementTree.fromstring(response.content)
4508 entity = [child for child in catalogItem if child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
4509 vapp_tempalte_href = entity.get("href")
4510 #get vapp details and parse moref id
4511
4512 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
4513 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
4514 'vmw': 'http://www.vmware.com/schema/ovf',
4515 'vm': 'http://www.vmware.com/vcloud/v1.5',
4516 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
4517 'vmext':"http://www.vmware.com/vcloud/extension/v1.5",
4518 'xmlns':"http://www.vmware.com/vcloud/v1.5"
4519 }
4520
4521 if vca.vcloud_session and vca.vcloud_session.organization:
4522 response = Http.get(url=vapp_tempalte_href,
4523 headers=vca.vcloud_session.get_vcloud_headers(),
4524 verify=vca.verify,
4525 logger=vca.logger
4526 )
4527
4528 if response.status_code != requests.codes.ok:
4529 self.logger.debug("REST API call {} failed. Return status code {}".format(
4530 vapp_tempalte_href, response.status_code))
4531
4532 else:
4533 xmlroot_respond = XmlElementTree.fromstring(response.content)
4534 children_section = xmlroot_respond.find('vm:Children/', namespaces)
4535 if children_section is not None:
4536 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
4537 if vCloud_extension_section is not None:
4538 vm_vcenter_info = {}
4539 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
4540 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
4541 if vmext is not None:
4542 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
4543 parsed_response["vm_vcenter_info"]= vm_vcenter_info
4544
4545 except Exception as exp :
4546 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
4547
4548 return parsed_response
4549
4550
4551 def rollback_newvm(self, vapp_uuid, msg , exp_type="Genric"):
4552 """
4553 Method to delete vApp
4554 Args :
4555 vapp_uuid - vApp UUID
4556 msg - Error message to be logged
4557 exp_type : Exception type
4558 Returns:
4559 None
4560 """
4561 if vapp_uuid:
4562 status = self.delete_vminstance(vapp_uuid)
4563 else:
4564 msg = "No vApp ID"
4565 self.logger.error(msg)
4566 if exp_type == "Genric":
4567 raise vimconn.vimconnException(msg)
4568 elif exp_type == "NotFound":
4569 raise vimconn.vimconnNotFoundException(message=msg)
4570
4571 def add_sriov(self, vapp_uuid, sriov_nets, vmname_andid):
4572 """
4573 Method to attach SRIOV adapters to VM
4574
4575 Args:
4576 vapp_uuid - uuid of vApp/VM
4577 sriov_nets - SRIOV devices infromation as specified in VNFD (flavor)
4578 vmname_andid - vmname
4579
4580 Returns:
4581 The status of add SRIOV adapter task , vm object and
4582 vcenter_conect object
4583 """
4584 vm_obj = None
4585 vcenter_conect, content = self.get_vcenter_content()
4586 vm_moref_id = self.get_vm_moref_id(vapp_uuid)
4587
4588 if vm_moref_id:
4589 try:
4590 no_of_sriov_devices = len(sriov_nets)
4591 if no_of_sriov_devices > 0:
4592 #Get VM and its host
4593 host_obj, vm_obj = self.get_vm_obj(content, vm_moref_id)
4594 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
4595 if host_obj and vm_obj:
4596 #get SRIOV devies from host on which vapp is currently installed
4597 avilable_sriov_devices = self.get_sriov_devices(host_obj,
4598 no_of_sriov_devices,
4599 )
4600
4601 if len(avilable_sriov_devices) == 0:
4602 #find other hosts with active pci devices
4603 new_host_obj , avilable_sriov_devices = self.get_host_and_sriov_devices(
4604 content,
4605 no_of_sriov_devices,
4606 )
4607
4608 if new_host_obj is not None and len(avilable_sriov_devices)> 0:
4609 #Migrate vm to the host where SRIOV devices are available
4610 self.logger.info("Relocate VM {} on new host {}".format(vm_obj,
4611 new_host_obj))
4612 task = self.relocate_vm(new_host_obj, vm_obj)
4613 if task is not None:
4614 result = self.wait_for_vcenter_task(task, vcenter_conect)
4615 self.logger.info("Migrate VM status: {}".format(result))
4616 host_obj = new_host_obj
4617 else:
4618 self.logger.info("Fail to migrate VM : {}".format(result))
4619 raise vimconn.vimconnNotFoundException(
4620 "Fail to migrate VM : {} to host {}".format(
4621 vmname_andid,
4622 new_host_obj)
4623 )
4624
4625 if host_obj is not None and avilable_sriov_devices is not None and len(avilable_sriov_devices)> 0:
4626 #Add SRIOV devices one by one
4627 for sriov_net in sriov_nets:
4628 network_name = sriov_net.get('net_id')
4629 dvs_portgr_name = self.create_dvPort_group(network_name)
4630 if sriov_net.get('type') == "VF":
4631 #add vlan ID ,Modify portgroup for vlan ID
4632 self.configure_vlanID(content, vcenter_conect, network_name)
4633
4634 task = self.add_sriov_to_vm(content,
4635 vm_obj,
4636 host_obj,
4637 network_name,
4638 avilable_sriov_devices[0]
4639 )
4640 if task:
4641 status= self.wait_for_vcenter_task(task, vcenter_conect)
4642 if status:
4643 self.logger.info("Added SRIOV {} to VM {}".format(
4644 no_of_sriov_devices,
4645 str(vm_obj)))
4646 else:
4647 self.logger.error("Fail to add SRIOV {} to VM {}".format(
4648 no_of_sriov_devices,
4649 str(vm_obj)))
4650 raise vimconn.vimconnUnexpectedResponse(
4651 "Fail to add SRIOV adapter in VM ".format(str(vm_obj))
4652 )
4653 return True, vm_obj, vcenter_conect
4654 else:
4655 self.logger.error("Currently there is no host with"\
4656 " {} number of avaialble SRIOV "\
4657 "VFs required for VM {}".format(
4658 no_of_sriov_devices,
4659 vmname_andid)
4660 )
4661 raise vimconn.vimconnNotFoundException(
4662 "Currently there is no host with {} "\
4663 "number of avaialble SRIOV devices required for VM {}".format(
4664 no_of_sriov_devices,
4665 vmname_andid))
4666 else:
4667 self.logger.debug("No infromation about SRIOV devices {} ",sriov_nets)
4668
4669 except vmodl.MethodFault as error:
4670 self.logger.error("Error occurred while adding SRIOV {} ",error)
4671 return None, vm_obj, vcenter_conect
4672
4673
4674 def get_sriov_devices(self,host, no_of_vfs):
4675 """
4676 Method to get the details of SRIOV devices on given host
4677 Args:
4678 host - vSphere host object
4679 no_of_vfs - number of VFs needed on host
4680
4681 Returns:
4682 array of SRIOV devices
4683 """
4684 sriovInfo=[]
4685 if host:
4686 for device in host.config.pciPassthruInfo:
4687 if isinstance(device,vim.host.SriovInfo) and device.sriovActive:
4688 if device.numVirtualFunction >= no_of_vfs:
4689 sriovInfo.append(device)
4690 break
4691 return sriovInfo
4692
4693
4694 def get_host_and_sriov_devices(self, content, no_of_vfs):
4695 """
4696 Method to get the details of SRIOV devices infromation on all hosts
4697
4698 Args:
4699 content - vSphere host object
4700 no_of_vfs - number of pci VFs needed on host
4701
4702 Returns:
4703 array of SRIOV devices and host object
4704 """
4705 host_obj = None
4706 sriov_device_objs = None
4707 try:
4708 if content:
4709 container = content.viewManager.CreateContainerView(content.rootFolder,
4710 [vim.HostSystem], True)
4711 for host in container.view:
4712 devices = self.get_sriov_devices(host, no_of_vfs)
4713 if devices:
4714 host_obj = host
4715 sriov_device_objs = devices
4716 break
4717 except Exception as exp:
4718 self.logger.error("Error {} occurred while finding SRIOV devices on host: {}".format(exp, host_obj))
4719
4720 return host_obj,sriov_device_objs
4721
4722
4723 def add_sriov_to_vm(self,content, vm_obj, host_obj, network_name, sriov_device):
4724 """
4725 Method to add SRIOV adapter to vm
4726
4727 Args:
4728 host_obj - vSphere host object
4729 vm_obj - vSphere vm object
4730 content - vCenter content object
4731 network_name - name of distributed virtaul portgroup
4732 sriov_device - SRIOV device info
4733
4734 Returns:
4735 task object
4736 """
4737 devices = []
4738 vnic_label = "sriov nic"
4739 try:
4740 dvs_portgr = self.get_dvport_group(network_name)
4741 network_name = dvs_portgr.name
4742 nic = vim.vm.device.VirtualDeviceSpec()
4743 # VM device
4744 nic.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
4745 nic.device = vim.vm.device.VirtualSriovEthernetCard()
4746 nic.device.addressType = 'assigned'
4747 #nic.device.key = 13016
4748 nic.device.deviceInfo = vim.Description()
4749 nic.device.deviceInfo.label = vnic_label
4750 nic.device.deviceInfo.summary = network_name
4751 nic.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
4752
4753 nic.device.backing.network = self.get_obj(content, [vim.Network], network_name)
4754 nic.device.backing.deviceName = network_name
4755 nic.device.backing.useAutoDetect = False
4756 nic.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
4757 nic.device.connectable.startConnected = True
4758 nic.device.connectable.allowGuestControl = True
4759
4760 nic.device.sriovBacking = vim.vm.device.VirtualSriovEthernetCard.SriovBackingInfo()
4761 nic.device.sriovBacking.physicalFunctionBacking = vim.vm.device.VirtualPCIPassthrough.DeviceBackingInfo()
4762 nic.device.sriovBacking.physicalFunctionBacking.id = sriov_device.id
4763
4764 devices.append(nic)
4765 vmconf = vim.vm.ConfigSpec(deviceChange=devices)
4766 task = vm_obj.ReconfigVM_Task(vmconf)
4767 return task
4768 except Exception as exp:
4769 self.logger.error("Error {} occurred while adding SRIOV adapter in VM: {}".format(exp, vm_obj))
4770 return None
4771
4772
4773 def create_dvPort_group(self, network_name):
4774 """
4775 Method to create disributed virtual portgroup
4776
4777 Args:
4778 network_name - name of network/portgroup
4779
4780 Returns:
4781 portgroup key
4782 """
4783 try:
4784 new_network_name = [network_name, '-', str(uuid.uuid4())]
4785 network_name=''.join(new_network_name)
4786 vcenter_conect, content = self.get_vcenter_content()
4787
4788 dv_switch = self.get_obj(content, [vim.DistributedVirtualSwitch], self.dvs_name)
4789 if dv_switch:
4790 dv_pg_spec = vim.dvs.DistributedVirtualPortgroup.ConfigSpec()
4791 dv_pg_spec.name = network_name
4792
4793 dv_pg_spec.type = vim.dvs.DistributedVirtualPortgroup.PortgroupType.earlyBinding
4794 dv_pg_spec.defaultPortConfig = vim.dvs.VmwareDistributedVirtualSwitch.VmwarePortConfigPolicy()
4795 dv_pg_spec.defaultPortConfig.securityPolicy = vim.dvs.VmwareDistributedVirtualSwitch.SecurityPolicy()
4796 dv_pg_spec.defaultPortConfig.securityPolicy.allowPromiscuous = vim.BoolPolicy(value=False)
4797 dv_pg_spec.defaultPortConfig.securityPolicy.forgedTransmits = vim.BoolPolicy(value=False)
4798 dv_pg_spec.defaultPortConfig.securityPolicy.macChanges = vim.BoolPolicy(value=False)
4799
4800 task = dv_switch.AddDVPortgroup_Task([dv_pg_spec])
4801 self.wait_for_vcenter_task(task, vcenter_conect)
4802
4803 dvPort_group = self.get_obj(content, [vim.dvs.DistributedVirtualPortgroup], network_name)
4804 if dvPort_group:
4805 self.logger.info("Created disributed virtaul port group: {}".format(dvPort_group))
4806 return dvPort_group.key
4807 else:
4808 self.logger.debug("No disributed virtual switch found with name {}".format(network_name))
4809
4810 except Exception as exp:
4811 self.logger.error("Error occurred while creating disributed virtaul port group {}"\
4812 " : {}".format(network_name, exp))
4813 return None
4814
4815 def reconfig_portgroup(self, content, dvPort_group_name , config_info={}):
4816 """
4817 Method to reconfigure disributed virtual portgroup
4818
4819 Args:
4820 dvPort_group_name - name of disributed virtual portgroup
4821 content - vCenter content object
4822 config_info - disributed virtual portgroup configuration
4823
4824 Returns:
4825 task object
4826 """
4827 try:
4828 dvPort_group = self.get_dvport_group(dvPort_group_name)
4829 if dvPort_group:
4830 dv_pg_spec = vim.dvs.DistributedVirtualPortgroup.ConfigSpec()
4831 dv_pg_spec.configVersion = dvPort_group.config.configVersion
4832 dv_pg_spec.defaultPortConfig = vim.dvs.VmwareDistributedVirtualSwitch.VmwarePortConfigPolicy()
4833 if "vlanID" in config_info:
4834 dv_pg_spec.defaultPortConfig.vlan = vim.dvs.VmwareDistributedVirtualSwitch.VlanIdSpec()
4835 dv_pg_spec.defaultPortConfig.vlan.vlanId = config_info.get('vlanID')
4836
4837 task = dvPort_group.ReconfigureDVPortgroup_Task(spec=dv_pg_spec)
4838 return task
4839 else:
4840 return None
4841 except Exception as exp:
4842 self.logger.error("Error occurred while reconfiguraing disributed virtaul port group {}"\
4843 " : {}".format(dvPort_group_name, exp))
4844 return None
4845
4846
4847 def destroy_dvport_group(self , dvPort_group_name):
4848 """
4849 Method to destroy disributed virtual portgroup
4850
4851 Args:
4852 network_name - name of network/portgroup
4853
4854 Returns:
4855 True if portgroup successfully got deleted else false
4856 """
4857 vcenter_conect, content = self.get_vcenter_content()
4858 try:
4859 status = None
4860 dvPort_group = self.get_dvport_group(dvPort_group_name)
4861 if dvPort_group:
4862 task = dvPort_group.Destroy_Task()
4863 status = self.wait_for_vcenter_task(task, vcenter_conect)
4864 return status
4865 except vmodl.MethodFault as exp:
4866 self.logger.error("Caught vmodl fault {} while deleting disributed virtaul port group {}".format(
4867 exp, dvPort_group_name))
4868 return None
4869
4870
4871 def get_dvport_group(self, dvPort_group_name):
4872 """
4873 Method to get disributed virtual portgroup
4874
4875 Args:
4876 network_name - name of network/portgroup
4877
4878 Returns:
4879 portgroup object
4880 """
4881 vcenter_conect, content = self.get_vcenter_content()
4882 dvPort_group = None
4883 try:
4884 container = content.viewManager.CreateContainerView(content.rootFolder, [vim.dvs.DistributedVirtualPortgroup], True)
4885 for item in container.view:
4886 if item.key == dvPort_group_name:
4887 dvPort_group = item
4888 break
4889 return dvPort_group
4890 except vmodl.MethodFault as exp:
4891 self.logger.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
4892 exp, dvPort_group_name))
4893 return None
4894
4895 def get_vlanID_from_dvs_portgr(self, dvPort_group_name):
4896 """
4897 Method to get disributed virtual portgroup vlanID
4898
4899 Args:
4900 network_name - name of network/portgroup
4901
4902 Returns:
4903 vlan ID
4904 """
4905 vlanId = None
4906 try:
4907 dvPort_group = self.get_dvport_group(dvPort_group_name)
4908 if dvPort_group:
4909 vlanId = dvPort_group.config.defaultPortConfig.vlan.vlanId
4910 except vmodl.MethodFault as exp:
4911 self.logger.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
4912 exp, dvPort_group_name))
4913 return vlanId
4914
4915
4916 def configure_vlanID(self, content, vcenter_conect, dvPort_group_name):
4917 """
4918 Method to configure vlanID in disributed virtual portgroup vlanID
4919
4920 Args:
4921 network_name - name of network/portgroup
4922
4923 Returns:
4924 None
4925 """
4926 vlanID = self.get_vlanID_from_dvs_portgr(dvPort_group_name)
4927 if vlanID == 0:
4928 #configure vlanID
4929 vlanID = self.genrate_vlanID(dvPort_group_name)
4930 config = {"vlanID":vlanID}
4931 task = self.reconfig_portgroup(content, dvPort_group_name,
4932 config_info=config)
4933 if task:
4934 status= self.wait_for_vcenter_task(task, vcenter_conect)
4935 if status:
4936 self.logger.info("Reconfigured Port group {} for vlan ID {}".format(
4937 dvPort_group_name,vlanID))
4938 else:
4939 self.logger.error("Fail reconfigure portgroup {} for vlanID{}".format(
4940 dvPort_group_name, vlanID))
4941
4942
4943 def genrate_vlanID(self, network_name):
4944 """
4945 Method to get unused vlanID
4946 Args:
4947 network_name - name of network/portgroup
4948 Returns:
4949 vlanID
4950 """
4951 vlan_id = None
4952 used_ids = []
4953 if self.config.get('vlanID_range') == None:
4954 raise vimconn.vimconnConflictException("You must provide a 'vlanID_range' "\
4955 "at config value before creating sriov network with vlan tag")
4956 if "used_vlanIDs" not in self.persistent_info:
4957 self.persistent_info["used_vlanIDs"] = {}
4958 else:
4959 used_ids = list(self.persistent_info["used_vlanIDs"].values())
4960
4961 for vlanID_range in self.config.get('vlanID_range'):
4962 start_vlanid , end_vlanid = vlanID_range.split("-")
4963 if start_vlanid > end_vlanid:
4964 raise vimconn.vimconnConflictException("Invalid vlan ID range {}".format(
4965 vlanID_range))
4966
4967 for id in range(int(start_vlanid), int(end_vlanid) + 1):
4968 if id not in used_ids:
4969 vlan_id = id
4970 self.persistent_info["used_vlanIDs"][network_name] = vlan_id
4971 return vlan_id
4972 if vlan_id is None:
4973 raise vimconn.vimconnConflictException("All Vlan IDs are in use")
4974
4975
4976 def get_obj(self, content, vimtype, name):
4977 """
4978 Get the vsphere object associated with a given text name
4979 """
4980 obj = None
4981 container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True)
4982 for item in container.view:
4983 if item.name == name:
4984 obj = item
4985 break
4986 return obj
4987
4988
4989 def insert_media_to_vm(self, vapp, image_id):
4990 """
4991 Method to insert media CD-ROM (ISO image) from catalog to vm.
4992 vapp - vapp object to get vm id
4993 Image_id - image id for cdrom to be inerted to vm
4994 """
4995 # create connection object
4996 vca = self.connect()
4997 try:
4998 # fetching catalog details
4999 rest_url = "{}/api/catalog/{}".format(vca.host,image_id)
5000 response = Http.get(url=rest_url,
5001 headers=vca.vcloud_session.get_vcloud_headers(),
5002 verify=vca.verify,
5003 logger=vca.logger)
5004
5005 if response.status_code != 200:
5006 self.logger.error("REST call {} failed reason : {}"\
5007 "status code : {}".format(url_rest_call,
5008 response.content,
5009 response.status_code))
5010 raise vimconn.vimconnException("insert_media_to_vm(): Failed to get "\
5011 "catalog details")
5012 # searching iso name and id
5013 iso_name,media_id = self.get_media_details(vca, response.content)
5014
5015 if iso_name and media_id:
5016 data ="""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
5017 <ns6:MediaInsertOrEjectParams
5018 xmlns="http://www.vmware.com/vcloud/versions" xmlns:ns2="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns3="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ns6="http://www.vmware.com/vcloud/v1.5" xmlns:ns7="http://www.vmware.com/schema/ovf" xmlns:ns8="http://schemas.dmtf.org/ovf/environment/1" xmlns:ns9="http://www.vmware.com/vcloud/extension/v1.5">
5019 <ns6:Media
5020 type="application/vnd.vmware.vcloud.media+xml"
5021 name="{}.iso"
5022 id="urn:vcloud:media:{}"
5023 href="https://{}/api/media/{}"/>
5024 </ns6:MediaInsertOrEjectParams>""".format(iso_name, media_id,
5025 vca.host,media_id)
5026
5027 for vms in vapp._get_vms():
5028 vm_id = (vms.id).split(':')[-1]
5029
5030 headers = vca.vcloud_session.get_vcloud_headers()
5031 headers['Content-Type'] = 'application/vnd.vmware.vcloud.mediaInsertOrEjectParams+xml'
5032 rest_url = "{}/api/vApp/vm-{}/media/action/insertMedia".format(vca.host,vm_id)
5033
5034 response = Http.post(url=rest_url,
5035 headers=headers,
5036 data=data,
5037 verify=vca.verify,
5038 logger=vca.logger)
5039
5040 if response.status_code != 202:
5041 self.logger.error("Failed to insert CD-ROM to vm")
5042 raise vimconn.vimconnException("insert_media_to_vm() : Failed to insert"\
5043 "ISO image to vm")
5044 else:
5045 task = taskType.parseString(response.content, True)
5046 if isinstance(task, GenericTask):
5047 vca.block_until_completed(task)
5048 self.logger.info("insert_media_to_vm(): Sucessfully inserted media ISO"\
5049 " image to vm {}".format(vm_id))
5050 except Exception as exp:
5051 self.logger.error("insert_media_to_vm() : exception occurred "\
5052 "while inserting media CD-ROM")
5053 raise vimconn.vimconnException(message=exp)
5054
5055
5056 def get_media_details(self, vca, content):
5057 """
5058 Method to get catalog item details
5059 vca - connection object
5060 content - Catalog details
5061 Return - Media name, media id
5062 """
5063 cataloghref_list = []
5064 try:
5065 if content:
5066 vm_list_xmlroot = XmlElementTree.fromstring(content)
5067 for child in vm_list_xmlroot.iter():
5068 if 'CatalogItem' in child.tag:
5069 cataloghref_list.append(child.attrib.get('href'))
5070 if cataloghref_list is not None:
5071 for href in cataloghref_list:
5072 if href:
5073 response = Http.get(url=href,
5074 headers=vca.vcloud_session.get_vcloud_headers(),
5075 verify=vca.verify,
5076 logger=vca.logger)
5077 if response.status_code != 200:
5078 self.logger.error("REST call {} failed reason : {}"\
5079 "status code : {}".format(href,
5080 response.content,
5081 response.status_code))
5082 raise vimconn.vimconnException("get_media_details : Failed to get "\
5083 "catalogitem details")
5084 list_xmlroot = XmlElementTree.fromstring(response.content)
5085 for child in list_xmlroot.iter():
5086 if 'Entity' in child.tag:
5087 if 'media' in child.attrib.get('href'):
5088 name = child.attrib.get('name')
5089 media_id = child.attrib.get('href').split('/').pop()
5090 return name,media_id
5091 else:
5092 self.logger.debug("Media name and id not found")
5093 return False,False
5094 except Exception as exp:
5095 self.logger.error("get_media_details : exception occurred "\
5096 "getting media details")
5097 raise vimconn.vimconnException(message=exp)
5098
5099
5100 def retry_rest(self, method, url, add_headers=None, data=None):
5101 """ Method to get Token & retry respective REST request
5102 Args:
5103 api - REST API - Can be one of 'GET' or 'PUT' or 'POST'
5104 url - request url to be used
5105 add_headers - Additional headers (optional)
5106 data - Request payload data to be passed in request
5107 Returns:
5108 response - Response of request
5109 """
5110 response = None
5111
5112 #Get token
5113 self.get_token()
5114
5115 headers=self.vca.vcloud_session.get_vcloud_headers()
5116
5117 if add_headers:
5118 headers.update(add_headers)
5119
5120 if method == 'GET':
5121 response = Http.get(url=url,
5122 headers=headers,
5123 verify=self.vca.verify,
5124 logger=self.vca.logger)
5125 elif method == 'PUT':
5126 response = Http.put(url=url,
5127 data=data,
5128 headers=headers,
5129 verify=self.vca.verify,
5130 logger=self.logger)
5131 elif method == 'POST':
5132 response = Http.post(url=url,
5133 headers=headers,
5134 data=data,
5135 verify=self.vca.verify,
5136 logger=self.vca.logger)
5137 elif method == 'DELETE':
5138 response = Http.delete(url=url,
5139 headers=headers,
5140 verify=self.vca.verify,
5141 logger=self.vca.logger)
5142 return response
5143
5144
5145 def get_token(self):
5146 """ Generate a new token if expired
5147
5148 Returns:
5149 The return vca object that letter can be used to connect to vCloud director as admin for VDC
5150 """
5151 vca = None
5152
5153 try:
5154 self.logger.debug("Generate token for vca {} as {} to datacenter {}.".format(self.org_name,
5155 self.user,
5156 self.org_name))
5157 vca = VCA(host=self.url,
5158 username=self.user,
5159 service_type=STANDALONE,
5160 version=VCAVERSION,
5161 verify=False,
5162 log=False)
5163
5164 result = vca.login(password=self.passwd, org=self.org_name)
5165 if result is True:
5166 result = vca.login(token=vca.token, org=self.org_name, org_url=vca.vcloud_session.org_url)
5167 if result is True:
5168 self.logger.info(
5169 "Successfully generated token for vcloud direct org: {} as user: {}".format(self.org_name, self.user))
5170 #Update vca
5171 self.vca = vca
5172 return
5173
5174 except:
5175 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
5176 "{} as user: {}".format(self.org_name, self.user))
5177
5178 if not vca or not result:
5179 raise vimconn.vimconnConnectionException("self.connect() is failed while reconnecting")
5180
5181
5182 def get_vdc_details(self):
5183 """ Get VDC details using pyVcloud Lib
5184
5185 Returns vdc object
5186 """
5187 vdc = self.vca.get_vdc(self.tenant_name)
5188
5189 #Retry once, if failed by refreshing token
5190 if vdc is None:
5191 self.get_token()
5192 vdc = self.vca.get_vdc(self.tenant_name)
5193
5194 return vdc
5195
5196