Merge branch 'v1.0'
[osm/RO.git] / vimconn_vmware.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
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 with: nfvlabs@tid.es
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 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 httplib
62 import hashlib
63 import socket
64 import struct
65 import netaddr
66
67 # global variable for vcd connector type
68 STANDALONE = 'standalone'
69
70 # key for flavor dicts
71 FLAVOR_RAM_KEY = 'ram'
72 FLAVOR_VCPUS_KEY = 'vcpus'
73 FLAVOR_DISK_KEY = 'disk'
74 DEFAULT_IP_PROFILE = {'gateway_address':"192.168.1.1",
75 'dhcp_count':50,
76 'subnet_address':"192.168.1.0/24",
77 'dhcp_enabled':True,
78 'dhcp_start_address':"192.168.1.3",
79 'ip_version':"IPv4",
80 'dns_address':"192.168.1.2"
81 }
82 # global variable for wait time
83 INTERVAL_TIME = 5
84 MAX_WAIT_TIME = 1800
85
86 VCAVERSION = '5.9'
87
88 __author__ = "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
89 __date__ = "$12-Jan-2017 11:09:29$"
90 __version__ = '0.1'
91
92 # -1: "Could not be created",
93 # 0: "Unresolved",
94 # 1: "Resolved",
95 # 2: "Deployed",
96 # 3: "Suspended",
97 # 4: "Powered on",
98 # 5: "Waiting for user input",
99 # 6: "Unknown state",
100 # 7: "Unrecognized state",
101 # 8: "Powered off",
102 # 9: "Inconsistent state",
103 # 10: "Children do not all have the same status",
104 # 11: "Upload initiated, OVF descriptor pending",
105 # 12: "Upload initiated, copying contents",
106 # 13: "Upload initiated , disk contents pending",
107 # 14: "Upload has been quarantined",
108 # 15: "Upload quarantine period has expired"
109
110 # mapping vCD status to MANO
111 vcdStatusCode2manoFormat = {4: 'ACTIVE',
112 7: 'PAUSED',
113 3: 'SUSPENDED',
114 8: 'INACTIVE',
115 12: 'BUILD',
116 -1: 'ERROR',
117 14: 'DELETED'}
118
119 #
120 netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
121 'ERROR': 'ERROR', 'DELETED': 'DELETED'
122 }
123
124 # dict used to store flavor in memory
125 flavorlist = {}
126
127
128 class vimconnector(vimconn.vimconnector):
129 def __init__(self, uuid=None, name=None, tenant_id=None, tenant_name=None,
130 url=None, url_admin=None, user=None, passwd=None, log_level=None, config={}):
131 """
132 Constructor create vmware connector to vCloud director.
133
134 By default construct doesn't validate connection state. So client can create object with None arguments.
135 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
136
137 a) It initialize organization UUID
138 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
139
140 Args:
141 uuid - is organization uuid.
142 name - is organization name that must be presented in vCloud director.
143 tenant_id - is VDC uuid it must be presented in vCloud director
144 tenant_name - is VDC name.
145 url - is hostname or ip address of vCloud director
146 url_admin - same as above.
147 user - is user that administrator for organization. Caller must make sure that
148 username has right privileges.
149
150 password - is password for a user.
151
152 VMware connector also requires PVDC administrative privileges and separate account.
153 This variables must be passed via config argument dict contains keys
154
155 dict['admin_username']
156 dict['admin_password']
157
158 Returns:
159 Nothing.
160 """
161
162 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url,
163 url_admin, user, passwd, log_level, config)
164
165 self.logger = logging.getLogger('openmano.vim.vmware')
166 self.logger.setLevel(10)
167
168 self.name = name
169 self.id = uuid
170 self.url = url
171 self.url_admin = url_admin
172 self.tenant_id = tenant_id
173 self.tenant_name = tenant_name
174 self.user = user
175 self.passwd = passwd
176 self.config = config
177 self.admin_password = None
178 self.admin_user = None
179 self.org_name = ""
180
181 if tenant_name is not None:
182 orgnameandtenant = tenant_name.split(":")
183 if len(orgnameandtenant) == 2:
184 self.tenant_name = orgnameandtenant[1]
185 self.org_name = orgnameandtenant[0]
186 else:
187 self.tenant_name = tenant_name
188 if "orgname" in config:
189 self.org_name = config['orgname']
190
191 if log_level:
192 self.logger.setLevel(getattr(logging, log_level))
193
194 try:
195 self.admin_user = config['admin_username']
196 self.admin_password = config['admin_password']
197 except KeyError:
198 raise vimconn.vimconnException(message="Error admin username or admin password is empty.")
199
200 self.org_uuid = None
201 self.vca = None
202
203 if not url:
204 raise vimconn.vimconnException('url param can not be NoneType')
205
206 if not self.url_admin: # try to use normal url
207 self.url_admin = self.url
208
209 logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.org_name,
210 self.tenant_id, self.tenant_name))
211 logging.debug("vcd url {} vcd username: {} vcd password: {}".format(self.url, self.user, self.passwd))
212 logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password))
213
214 # initialize organization
215 if self.user is not None and self.passwd is not None and self.url:
216 self.init_organization()
217
218 def __getitem__(self, index):
219 if index == 'name':
220 return self.name
221 if index == 'tenant_id':
222 return self.tenant_id
223 if index == 'tenant_name':
224 return self.tenant_name
225 elif index == 'id':
226 return self.id
227 elif index == 'org_name':
228 return self.org_name
229 elif index == 'org_uuid':
230 return self.org_uuid
231 elif index == 'user':
232 return self.user
233 elif index == 'passwd':
234 return self.passwd
235 elif index == 'url':
236 return self.url
237 elif index == 'url_admin':
238 return self.url_admin
239 elif index == "config":
240 return self.config
241 else:
242 raise KeyError("Invalid key '%s'" % str(index))
243
244 def __setitem__(self, index, value):
245 if index == 'name':
246 self.name = value
247 if index == 'tenant_id':
248 self.tenant_id = value
249 if index == 'tenant_name':
250 self.tenant_name = value
251 elif index == 'id':
252 self.id = value
253 elif index == 'org_name':
254 self.org_name = value
255 elif index == 'org_uuid':
256 self.org_uuid = value
257 elif index == 'user':
258 self.user = value
259 elif index == 'passwd':
260 self.passwd = value
261 elif index == 'url':
262 self.url = value
263 elif index == 'url_admin':
264 self.url_admin = value
265 else:
266 raise KeyError("Invalid key '%s'" % str(index))
267
268 def connect_as_admin(self):
269 """ Method connect as pvdc admin user to vCloud director.
270 There are certain action that can be done only by provider vdc admin user.
271 Organization creation / provider network creation etc.
272
273 Returns:
274 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
275 """
276
277 self.logger.debug("Logging in to a vca {} as admin.".format(self.org_name))
278
279 vca_admin = VCA(host=self.url,
280 username=self.admin_user,
281 service_type=STANDALONE,
282 version=VCAVERSION,
283 verify=False,
284 log=False)
285 result = vca_admin.login(password=self.admin_password, org='System')
286 if not result:
287 raise vimconn.vimconnConnectionException(
288 "Can't connect to a vCloud director as: {}".format(self.admin_user))
289 result = vca_admin.login(token=vca_admin.token, org='System', org_url=vca_admin.vcloud_session.org_url)
290 if result is True:
291 self.logger.info(
292 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self.admin_user))
293
294 return vca_admin
295
296 def connect(self):
297 """ Method connect as normal user to vCloud director.
298
299 Returns:
300 The return vca object that letter can be used to connect to vCloud director as admin for VDC
301 """
302
303 try:
304 self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.org_name,
305 self.user,
306 self.org_name))
307 vca = VCA(host=self.url,
308 username=self.user,
309 service_type=STANDALONE,
310 version=VCAVERSION,
311 verify=False,
312 log=False)
313
314 result = vca.login(password=self.passwd, org=self.org_name)
315 if not result:
316 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user))
317 result = vca.login(token=vca.token, org=self.org_name, org_url=vca.vcloud_session.org_url)
318 if result is True:
319 self.logger.info(
320 "Successfully logged to a vcloud direct org: {} as user: {}".format(self.org_name, self.user))
321
322 except:
323 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
324 "{} as user: {}".format(self.org_name, self.user))
325
326 return vca
327
328 def init_organization(self):
329 """ Method initialize organization UUID and VDC parameters.
330
331 At bare minimum client must provide organization name that present in vCloud director and VDC.
332
333 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
334 The Org - UUID will be initialized at the run time if data center present in vCloud director.
335
336 Returns:
337 The return vca object that letter can be used to connect to vcloud direct as admin
338 """
339 try:
340 if self.org_uuid is None:
341 org_dict = self.get_org_list()
342 for org in org_dict:
343 # we set org UUID at the init phase but we can do it only when we have valid credential.
344 if org_dict[org] == self.org_name:
345 self.org_uuid = org
346 self.logger.debug("Setting organization UUID {}".format(self.org_uuid))
347 break
348 else:
349 raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name))
350
351 # if well good we require for org details
352 org_details_dict = self.get_org(org_uuid=self.org_uuid)
353
354 # we have two case if we want to initialize VDC ID or VDC name at run time
355 # tenant_name provided but no tenant id
356 if self.tenant_id is None and self.tenant_name is not None and 'vdcs' in org_details_dict:
357 vdcs_dict = org_details_dict['vdcs']
358 for vdc in vdcs_dict:
359 if vdcs_dict[vdc] == self.tenant_name:
360 self.tenant_id = vdc
361 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
362 self.org_name))
363 break
364 else:
365 raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.")
366 # case two we have tenant_id but we don't have tenant name so we find and set it.
367 if self.tenant_id is not None and self.tenant_name is None and 'vdcs' in org_details_dict:
368 vdcs_dict = org_details_dict['vdcs']
369 for vdc in vdcs_dict:
370 if vdc == self.tenant_id:
371 self.tenant_name = vdcs_dict[vdc]
372 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
373 self.org_name))
374 break
375 else:
376 raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director")
377 self.logger.debug("Setting organization uuid {}".format(self.org_uuid))
378 except:
379 self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name))
380 self.logger.debug(traceback.format_exc())
381 self.org_uuid = None
382
383 def new_tenant(self, tenant_name=None, tenant_description=None):
384 """ Method adds a new tenant to VIM with this name.
385 This action requires access to create VDC action in vCloud director.
386
387 Args:
388 tenant_name is tenant_name to be created.
389 tenant_description not used for this call
390
391 Return:
392 returns the tenant identifier in UUID format.
393 If action is failed method will throw vimconn.vimconnException method
394 """
395 vdc_task = self.create_vdc(vdc_name=tenant_name)
396 if vdc_task is not None:
397 vdc_uuid, value = vdc_task.popitem()
398 self.logger.info("Crated new vdc {} and uuid: {}".format(tenant_name, vdc_uuid))
399 return vdc_uuid
400 else:
401 raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name))
402
403 def delete_tenant(self, tenant_id=None):
404 """Delete a tenant from VIM"""
405 'Returns the tenant identifier'
406 raise vimconn.vimconnNotImplemented("Should have implemented this")
407
408 def get_tenant_list(self, filter_dict={}):
409 """Obtain tenants of VIM
410 filter_dict can contain the following keys:
411 name: filter by tenant name
412 id: filter by tenant uuid/id
413 <other VIM specific>
414 Returns the tenant list of dictionaries:
415 [{'name':'<name>, 'id':'<id>, ...}, ...]
416
417 """
418 org_dict = self.get_org(self.org_uuid)
419 vdcs_dict = org_dict['vdcs']
420
421 vdclist = []
422 try:
423 for k in vdcs_dict:
424 entry = {'name': vdcs_dict[k], 'id': k}
425 # if caller didn't specify dictionary we return all tenants.
426 if filter_dict is not None and filter_dict:
427 filtered_entry = entry.copy()
428 filtered_dict = set(entry.keys()) - set(filter_dict)
429 for unwanted_key in filtered_dict: del entry[unwanted_key]
430 if filter_dict == entry:
431 vdclist.append(filtered_entry)
432 else:
433 vdclist.append(entry)
434 except:
435 self.logger.debug("Error in get_tenant_list()")
436 self.logger.debug(traceback.format_exc())
437 raise vimconn.vimconnException("Incorrect state. {}")
438
439 return vdclist
440
441 def new_network(self, net_name, net_type, ip_profile=None, shared=False):
442 """Adds a tenant network to VIM
443 net_name is the name
444 net_type can be 'bridge','data'.'ptp'.
445 ip_profile is a dict containing the IP parameters of the network
446 shared is a boolean
447 Returns the network identifier"""
448
449 self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
450 .format(net_name, net_type, ip_profile, shared))
451
452 isshared = 'false'
453 if shared:
454 isshared = 'true'
455
456 network_uuid = self.create_network(network_name=net_name, net_type=net_type,
457 ip_profile=ip_profile, isshared=isshared)
458 if network_uuid is not None:
459 return network_uuid
460 else:
461 raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
462
463 def get_vcd_network_list(self):
464 """ Method available organization for a logged in tenant
465
466 Returns:
467 The return vca object that letter can be used to connect to vcloud direct as admin
468 """
469
470 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
471 vca = self.connect()
472 if not vca:
473 raise vimconn.vimconnConnectionException("self.connect() is failed.")
474
475 if not self.tenant_name:
476 raise vimconn.vimconnConnectionException("Tenant name is empty.")
477
478 vdc = vca.get_vdc(self.tenant_name)
479 if vdc is None:
480 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self.tenant_name))
481
482 vdc_uuid = vdc.get_id().split(":")[3]
483 networks = vca.get_networks(vdc.get_name())
484 network_list = []
485 try:
486 for network in networks:
487 filter_dict = {}
488 netid = network.get_id().split(":")
489 if len(netid) != 4:
490 continue
491
492 filter_dict["name"] = network.get_name()
493 filter_dict["id"] = netid[3]
494 filter_dict["shared"] = network.get_IsShared()
495 filter_dict["tenant_id"] = vdc_uuid
496 if network.get_status() == 1:
497 filter_dict["admin_state_up"] = True
498 else:
499 filter_dict["admin_state_up"] = False
500 filter_dict["status"] = "ACTIVE"
501 filter_dict["type"] = "bridge"
502 network_list.append(filter_dict)
503 self.logger.debug("get_vcd_network_list adding entry {}".format(filter_dict))
504 except:
505 self.logger.debug("Error in get_vcd_network_list")
506 self.logger.debug(traceback.format_exc())
507 pass
508
509 self.logger.debug("get_vcd_network_list returning {}".format(network_list))
510 return network_list
511
512 def get_network_list(self, filter_dict={}):
513 """Obtain tenant networks of VIM
514 Filter_dict can be:
515 name: network name OR/AND
516 id: network uuid OR/AND
517 shared: boolean OR/AND
518 tenant_id: tenant OR/AND
519 admin_state_up: boolean
520 status: 'ACTIVE'
521
522 [{key : value , key : value}]
523
524 Returns the network list of dictionaries:
525 [{<the fields at Filter_dict plus some VIM specific>}, ...]
526 List can be empty
527 """
528
529 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
530 vca = self.connect()
531 if not vca:
532 raise vimconn.vimconnConnectionException("self.connect() is failed.")
533
534 if not self.tenant_name:
535 raise vimconn.vimconnConnectionException("Tenant name is empty.")
536
537 vdc = vca.get_vdc(self.tenant_name)
538 if vdc is None:
539 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self.tenant_name))
540
541 vdcid = vdc.get_id().split(":")[3]
542 networks = vca.get_networks(vdc.get_name())
543 network_list = []
544
545 try:
546 for network in networks:
547 filter_entry = {}
548 net_uuid = network.get_id().split(":")
549 if len(net_uuid) != 4:
550 continue
551 else:
552 net_uuid = net_uuid[3]
553 # create dict entry
554 self.logger.debug("Adding {} to a list vcd id {} network {}".format(net_uuid,
555 vdcid,
556 network.get_name()))
557 filter_entry["name"] = network.get_name()
558 filter_entry["id"] = net_uuid
559 filter_entry["shared"] = network.get_IsShared()
560 filter_entry["tenant_id"] = vdcid
561 if network.get_status() == 1:
562 filter_entry["admin_state_up"] = True
563 else:
564 filter_entry["admin_state_up"] = False
565 filter_entry["status"] = "ACTIVE"
566 filter_entry["type"] = "bridge"
567 filtered_entry = filter_entry.copy()
568
569 if filter_dict is not None and filter_dict:
570 # we remove all the key : value we don't care and match only
571 # respected field
572 filtered_dict = set(filter_entry.keys()) - set(filter_dict)
573 for unwanted_key in filtered_dict: del filter_entry[unwanted_key]
574 if filter_dict == filter_entry:
575 network_list.append(filtered_entry)
576 else:
577 network_list.append(filtered_entry)
578 except:
579 self.logger.debug("Error in get_vcd_network_list")
580 self.logger.debug(traceback.format_exc())
581
582 self.logger.debug("Returning {}".format(network_list))
583 return network_list
584
585 def get_network(self, net_id):
586 """Method obtains network details of net_id VIM network
587 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
588
589 vca = self.connect()
590 if not vca:
591 raise vimconn.vimconnConnectionException("self.connect() is failed")
592
593 vdc = vca.get_vdc(self.tenant_name)
594 vdc_id = vdc.get_id().split(":")[3]
595
596 networks = vca.get_networks(vdc.get_name())
597 filter_dict = {}
598
599 try:
600 for network in networks:
601 vdc_network_id = network.get_id().split(":")
602 if len(vdc_network_id) == 4 and vdc_network_id[3] == net_id:
603 filter_dict["name"] = network.get_name()
604 filter_dict["id"] = vdc_network_id[3]
605 filter_dict["shared"] = network.get_IsShared()
606 filter_dict["tenant_id"] = vdc_id
607 if network.get_status() == 1:
608 filter_dict["admin_state_up"] = True
609 else:
610 filter_dict["admin_state_up"] = False
611 filter_dict["status"] = "ACTIVE"
612 filter_dict["type"] = "bridge"
613 self.logger.debug("Returning {}".format(filter_dict))
614 return filter_dict
615 except:
616 self.logger.debug("Error in get_network")
617 self.logger.debug(traceback.format_exc())
618
619 return filter_dict
620
621 def delete_network(self, net_id):
622 """
623 Method Deletes a tenant network from VIM, provide the network id.
624
625 Returns the network identifier or raise an exception
626 """
627
628 vca = self.connect()
629 if not vca:
630 raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self.tenant_name))
631
632 vcd_network = self.get_vcd_network(network_uuid=net_id)
633 if vcd_network is not None and vcd_network:
634 if self.delete_network_action(network_uuid=net_id):
635 return net_id
636 else:
637 raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
638
639 def refresh_nets_status(self, net_list):
640 """Get the status of the networks
641 Params: the list of network identifiers
642 Returns a dictionary with:
643 net_id: #VIM id of this network
644 status: #Mandatory. Text with one of:
645 # DELETED (not found at vim)
646 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
647 # OTHER (Vim reported other status not understood)
648 # ERROR (VIM indicates an ERROR status)
649 # ACTIVE, INACTIVE, DOWN (admin down),
650 # BUILD (on building process)
651 #
652 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
653 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
654
655 """
656
657 vca = self.connect()
658 if not vca:
659 raise vimconn.vimconnConnectionException("self.connect() is failed")
660
661 dict_entry = {}
662 try:
663 for net in net_list:
664 errormsg = ''
665 vcd_network = self.get_vcd_network(network_uuid=net)
666 if vcd_network is not None and vcd_network:
667 if vcd_network['status'] == '1':
668 status = 'ACTIVE'
669 else:
670 status = 'DOWN'
671 else:
672 status = 'DELETED'
673 errormsg = 'Network not found.'
674
675 dict_entry[net] = {'status': status, 'error_msg': errormsg,
676 'vim_info': yaml.safe_dump(vcd_network)}
677 except:
678 self.logger.debug("Error in refresh_nets_status")
679 self.logger.debug(traceback.format_exc())
680
681 return dict_entry
682
683 def get_flavor(self, flavor_id):
684 """Obtain flavor details from the VIM
685 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
686 """
687 if flavor_id not in flavorlist:
688 raise vimconn.vimconnNotFoundException("Flavor not found.")
689 return flavorlist[flavor_id]
690
691 def new_flavor(self, flavor_data):
692 """Adds a tenant flavor to VIM
693 flavor_data contains a dictionary with information, keys:
694 name: flavor name
695 ram: memory (cloud type) in MBytes
696 vpcus: cpus (cloud type)
697 extended: EPA parameters
698 - numas: #items requested in same NUMA
699 memory: number of 1G huge pages memory
700 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
701 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
702 - name: interface name
703 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
704 bandwidth: X Gbps; requested guarantee bandwidth
705 vpci: requested virtual PCI address
706 disk: disk size
707 is_public:
708 #TODO to concrete
709 Returns the flavor identifier"""
710
711 # generate a new uuid put to internal dict and return it.
712 self.logger.debug("Creating new flavor - flavor_data: {}".format(flavor_data))
713 new_flavor=flavor_data
714 ram = flavor_data.get(FLAVOR_RAM_KEY, 1024)
715 cpu = flavor_data.get(FLAVOR_VCPUS_KEY, 1)
716 disk = flavor_data.get(FLAVOR_DISK_KEY, 1)
717
718 extended_flv = flavor_data.get("extended")
719 if extended_flv:
720 numas=extended_flv.get("numas")
721 if numas:
722 for numa in numas:
723 #overwrite ram and vcpus
724 ram = numa['memory']*1024
725 if 'paired-threads' in numa:
726 cpu = numa['paired-threads']*2
727 elif 'cores' in numa:
728 cpu = numa['cores']
729 elif 'threads' in numa:
730 cpu = numa['threads']
731
732 new_flavor[FLAVOR_RAM_KEY] = ram
733 new_flavor[FLAVOR_VCPUS_KEY] = cpu
734 new_flavor[FLAVOR_DISK_KEY] = disk
735 # generate a new uuid put to internal dict and return it.
736 flavor_id = uuid.uuid4()
737 flavorlist[str(flavor_id)] = new_flavor
738 self.logger.debug("Created flavor - {} : {}".format(flavor_id, new_flavor))
739
740 return str(flavor_id)
741
742 def delete_flavor(self, flavor_id):
743 """Deletes a tenant flavor from VIM identify by its id
744
745 Returns the used id or raise an exception
746 """
747 if flavor_id not in flavorlist:
748 raise vimconn.vimconnNotFoundException("Flavor not found.")
749
750 flavorlist.pop(flavor_id, None)
751 return flavor_id
752
753 def new_image(self, image_dict):
754 """
755 Adds a tenant image to VIM
756 Returns:
757 200, image-id if the image is created
758 <0, message if there is an error
759 """
760
761 return self.get_image_id_from_path(image_dict['location'])
762
763 def delete_image(self, image_id):
764 """
765
766 :param image_id:
767 :return:
768 """
769
770 raise vimconn.vimconnNotImplemented("Should have implemented this")
771
772 def catalog_exists(self, catalog_name, catalogs):
773 """
774
775 :param catalog_name:
776 :param catalogs:
777 :return:
778 """
779 for catalog in catalogs:
780 if catalog.name == catalog_name:
781 return True
782 return False
783
784 def create_vimcatalog(self, vca=None, catalog_name=None):
785 """ Create new catalog entry in vCloud director.
786
787 Args
788 vca: vCloud director.
789 catalog_name catalog that client wish to create. Note no validation done for a name.
790 Client must make sure that provide valid string representation.
791
792 Return (bool) True if catalog created.
793
794 """
795 try:
796 task = vca.create_catalog(catalog_name, catalog_name)
797 result = vca.block_until_completed(task)
798 if not result:
799 return False
800 catalogs = vca.get_catalogs()
801 except:
802 return False
803 return self.catalog_exists(catalog_name, catalogs)
804
805 # noinspection PyIncorrectDocstring
806 def upload_ovf(self, vca=None, catalog_name=None, image_name=None, media_file_name=None,
807 description='', progress=False, chunk_bytes=128 * 1024):
808 """
809 Uploads a OVF file to a vCloud catalog
810
811 :param chunk_bytes:
812 :param progress:
813 :param description:
814 :param image_name:
815 :param vca:
816 :param catalog_name: (str): The name of the catalog to upload the media.
817 :param media_file_name: (str): The name of the local media file to upload.
818 :return: (bool) True if the media file was successfully uploaded, false otherwise.
819 """
820 os.path.isfile(media_file_name)
821 statinfo = os.stat(media_file_name)
822
823 # find a catalog entry where we upload OVF.
824 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
825 # status change.
826 # if VCD can parse OVF we upload VMDK file
827 for catalog in vca.get_catalogs():
828 if catalog_name != catalog.name:
829 continue
830 link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
831 link.get_rel() == 'add', catalog.get_Link())
832 assert len(link) == 1
833 data = """
834 <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>
835 """ % (escape(catalog_name), escape(description))
836 headers = vca.vcloud_session.get_vcloud_headers()
837 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
838 response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
839 if response.status_code == requests.codes.created:
840 catalogItem = XmlElementTree.fromstring(response.content)
841 entity = [child for child in catalogItem if
842 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
843 href = entity.get('href')
844 template = href
845 response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
846 verify=vca.verify, logger=self.logger)
847
848 if response.status_code == requests.codes.ok:
849 media = mediaType.parseString(response.content, True)
850 link = filter(lambda link: link.get_rel() == 'upload:default',
851 media.get_Files().get_File()[0].get_Link())[0]
852 headers = vca.vcloud_session.get_vcloud_headers()
853 headers['Content-Type'] = 'Content-Type text/xml'
854 response = Http.put(link.get_href(),
855 data=open(media_file_name, 'rb'),
856 headers=headers,
857 verify=vca.verify, logger=self.logger)
858 if response.status_code != requests.codes.ok:
859 self.logger.debug(
860 "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
861 media_file_name))
862 return False
863
864 # TODO fix this with aync block
865 time.sleep(5)
866
867 self.logger.debug("vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
868
869 # uploading VMDK file
870 # check status of OVF upload and upload remaining files.
871 response = Http.get(template,
872 headers=vca.vcloud_session.get_vcloud_headers(),
873 verify=vca.verify,
874 logger=self.logger)
875
876 if response.status_code == requests.codes.ok:
877 media = mediaType.parseString(response.content, True)
878 number_of_files = len(media.get_Files().get_File())
879 for index in xrange(0, number_of_files):
880 links_list = filter(lambda link: link.get_rel() == 'upload:default',
881 media.get_Files().get_File()[index].get_Link())
882 for link in links_list:
883 # we skip ovf since it already uploaded.
884 if 'ovf' in link.get_href():
885 continue
886 # The OVF file and VMDK must be in a same directory
887 head, tail = os.path.split(media_file_name)
888 file_vmdk = head + '/' + link.get_href().split("/")[-1]
889 if not os.path.isfile(file_vmdk):
890 return False
891 statinfo = os.stat(file_vmdk)
892 if statinfo.st_size == 0:
893 return False
894 hrefvmdk = link.get_href()
895
896 if progress:
897 print("Uploading file: {}".format(file_vmdk))
898 if progress:
899 widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
900 FileTransferSpeed()]
901 progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
902
903 bytes_transferred = 0
904 f = open(file_vmdk, 'rb')
905 while bytes_transferred < statinfo.st_size:
906 my_bytes = f.read(chunk_bytes)
907 if len(my_bytes) <= chunk_bytes:
908 headers = vca.vcloud_session.get_vcloud_headers()
909 headers['Content-Range'] = 'bytes %s-%s/%s' % (
910 bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
911 headers['Content-Length'] = str(len(my_bytes))
912 response = Http.put(hrefvmdk,
913 headers=headers,
914 data=my_bytes,
915 verify=vca.verify,
916 logger=None)
917
918 if response.status_code == requests.codes.ok:
919 bytes_transferred += len(my_bytes)
920 if progress:
921 progress_bar.update(bytes_transferred)
922 else:
923 self.logger.debug(
924 'file upload failed with error: [%s] %s' % (response.status_code,
925 response.content))
926
927 f.close()
928 return False
929 f.close()
930 if progress:
931 progress_bar.finish()
932 time.sleep(10)
933 return True
934 else:
935 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
936 format(catalog_name, media_file_name))
937 return False
938
939 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
940 return False
941
942 def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False):
943 """Upload media file"""
944 # TODO add named parameters for readability
945
946 return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0],
947 media_file_name=medial_file_name, description='medial_file_name', progress=progress)
948
949 def validate_uuid4(self, uuid_string=None):
950 """ Method validate correct format of UUID.
951
952 Return: true if string represent valid uuid
953 """
954 try:
955 val = uuid.UUID(uuid_string, version=4)
956 except ValueError:
957 return False
958 return True
959
960 def get_catalogid(self, catalog_name=None, catalogs=None):
961 """ Method check catalog and return catalog ID in UUID format.
962
963 Args
964 catalog_name: catalog name as string
965 catalogs: list of catalogs.
966
967 Return: catalogs uuid
968 """
969
970 for catalog in catalogs:
971 if catalog.name == catalog_name:
972 catalog_id = catalog.get_id().split(":")
973 return catalog_id[3]
974 return None
975
976 def get_catalogbyid(self, catalog_uuid=None, catalogs=None):
977 """ Method check catalog and return catalog name lookup done by catalog UUID.
978
979 Args
980 catalog_name: catalog name as string
981 catalogs: list of catalogs.
982
983 Return: catalogs name or None
984 """
985
986 if not self.validate_uuid4(uuid_string=catalog_uuid):
987 return None
988
989 for catalog in catalogs:
990 catalog_id = catalog.get_id().split(":")[3]
991 if catalog_id == catalog_uuid:
992 return catalog.name
993 return None
994
995 def get_image_id_from_path(self, path=None, progress=False):
996 """ Method upload OVF image to vCloud director.
997
998 Each OVF image represented as single catalog entry in vcloud director.
999 The method check for existing catalog entry. The check done by file name without file extension.
1000
1001 if given catalog name already present method will respond with existing catalog uuid otherwise
1002 it will create new catalog entry and upload OVF file to newly created catalog.
1003
1004 If method can't create catalog entry or upload a file it will throw exception.
1005
1006 Method accept boolean flag progress that will output progress bar. It useful method
1007 for standalone upload use case. In case to test large file upload.
1008
1009 Args
1010 path: - valid path to OVF file.
1011 progress - boolean progress bar show progress bar.
1012
1013 Return: if image uploaded correct method will provide image catalog UUID.
1014 """
1015 vca = self.connect()
1016 if not vca:
1017 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1018
1019 if not path:
1020 raise vimconn.vimconnException("Image path can't be None.")
1021
1022 if not os.path.isfile(path):
1023 raise vimconn.vimconnException("Can't read file. File not found.")
1024
1025 if not os.access(path, os.R_OK):
1026 raise vimconn.vimconnException("Can't read file. Check file permission to read.")
1027
1028 self.logger.debug("get_image_id_from_path() client requesting {} ".format(path))
1029
1030 dirpath, filename = os.path.split(path)
1031 flname, file_extension = os.path.splitext(path)
1032 if file_extension != '.ovf':
1033 self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
1034 raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.")
1035
1036 catalog_name = os.path.splitext(filename)[0]
1037 catalog_md5_name = hashlib.md5(path).hexdigest()
1038 self.logger.debug("File name {} Catalog Name {} file path {} "
1039 "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name))
1040
1041 catalogs = vca.get_catalogs()
1042 if len(catalogs) == 0:
1043 self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
1044 result = self.create_vimcatalog(vca, catalog_md5_name)
1045 if not result:
1046 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1047 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
1048 media_name=filename, medial_file_name=path, progress=progress)
1049 if not result:
1050 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name))
1051 return self.get_catalogid(catalog_name, vca.get_catalogs())
1052 else:
1053 for catalog in catalogs:
1054 # search for existing catalog if we find same name we return ID
1055 # TODO optimize this
1056 if catalog.name == catalog_md5_name:
1057 self.logger.debug("Found existing catalog entry for {} "
1058 "catalog id {}".format(catalog_name,
1059 self.get_catalogid(catalog_md5_name, catalogs)))
1060 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
1061
1062 # if we didn't find existing catalog we create a new one and upload image.
1063 self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name))
1064 result = self.create_vimcatalog(vca, catalog_md5_name)
1065 if not result:
1066 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1067
1068 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
1069 media_name=filename, medial_file_name=path, progress=progress)
1070 if not result:
1071 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name))
1072
1073 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
1074
1075 def get_vappid(self, vdc=None, vapp_name=None):
1076 """ Method takes vdc object and vApp name and returns vapp uuid or None
1077
1078 Args:
1079 vdc: The VDC object.
1080 vapp_name: is application vappp name identifier
1081
1082 Returns:
1083 The return vApp name otherwise None
1084 """
1085 if vdc is None or vapp_name is None:
1086 return None
1087 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1088 try:
1089 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1090 vdc.ResourceEntities.ResourceEntity)
1091 if len(refs) == 1:
1092 return refs[0].href.split("vapp")[1][1:]
1093 except Exception as e:
1094 self.logger.exception(e)
1095 return False
1096 return None
1097
1098 def check_vapp(self, vdc=None, vapp_uuid=None):
1099 """ Method Method returns True or False if vapp deployed in vCloud director
1100
1101 Args:
1102 vca: Connector to VCA
1103 vdc: The VDC object.
1104 vappid: vappid is application identifier
1105
1106 Returns:
1107 The return True if vApp deployed
1108 :param vdc:
1109 :param vapp_uuid:
1110 """
1111 try:
1112 refs = filter(lambda ref:
1113 ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1114 vdc.ResourceEntities.ResourceEntity)
1115 for ref in refs:
1116 vappid = ref.href.split("vapp")[1][1:]
1117 # find vapp with respected vapp uuid
1118 if vappid == vapp_uuid:
1119 return True
1120 except Exception as e:
1121 self.logger.exception(e)
1122 return False
1123 return False
1124
1125 def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None):
1126 """Method returns vApp name from vCD and lookup done by vapp_id.
1127
1128 Args:
1129 vca: Connector to VCA
1130 vdc: The VDC object.
1131 vapp_uuid: vappid is application identifier
1132
1133 Returns:
1134 The return vApp name otherwise None
1135 """
1136
1137 try:
1138 refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1139 vdc.ResourceEntities.ResourceEntity)
1140 for ref in refs:
1141 # we care only about UUID the rest doesn't matter
1142 vappid = ref.href.split("vapp")[1][1:]
1143 if vappid == vapp_uuid:
1144 response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
1145 logger=self.logger)
1146 tree = XmlElementTree.fromstring(response.content)
1147 return tree.attrib['name']
1148 except Exception as e:
1149 self.logger.exception(e)
1150 return None
1151 return None
1152
1153 def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list={},
1154 cloud_config=None, disk_list=None):
1155 """Adds a VM instance to VIM
1156 Params:
1157 start: indicates if VM must start or boot in pause mode. Ignored
1158 image_id,flavor_id: image and flavor uuid
1159 net_list: list of interfaces, each one is a dictionary with:
1160 name:
1161 net_id: network uuid to connect
1162 vpci: virtual vcpi to assign
1163 model: interface model, virtio, e2000, ...
1164 mac_address:
1165 use: 'data', 'bridge', 'mgmt'
1166 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1167 vim_id: filled/added by this function
1168 cloud_config: can be a text script to be passed directly to cloud-init,
1169 or an object to inject users and ssh keys with format:
1170 key-pairs: [] list of keys to install to the default user
1171 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1172 #TODO ip, security groups
1173 Returns >=0, the instance identifier
1174 <0, error_text
1175 """
1176
1177 self.logger.info("Creating new instance for entry {}".format(name))
1178 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1179 format(description, start, image_id, flavor_id, net_list, cloud_config))
1180 vca = self.connect()
1181 if not vca:
1182 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1183
1184 #new vm name = vmname + tenant_id + uuid
1185 new_vm_name = [name, '-', str(uuid.uuid4())]
1186 vmname_andid = ''.join(new_vm_name)
1187
1188 # if vm already deployed we return existing uuid
1189 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1190 # if vapp_uuid is not None:
1191 # return vapp_uuid
1192
1193 # we check for presence of VDC, Catalog entry and Flavor.
1194 vdc = vca.get_vdc(self.tenant_name)
1195 if vdc is None:
1196 raise vimconn.vimconnNotFoundException(
1197 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
1198 catalogs = vca.get_catalogs()
1199 if catalogs is None:
1200 raise vimconn.vimconnNotFoundException(
1201 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
1202
1203 catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1204 if catalog_hash_name:
1205 self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
1206 else:
1207 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1208 "(Failed retrieve catalog information {})".format(name, image_id))
1209
1210
1211 # Set vCPU and Memory based on flavor.
1212 #
1213 vm_cpus = None
1214 vm_memory = None
1215 vm_disk = None
1216 pci_devices_info = []
1217 if flavor_id is not None:
1218 if flavor_id not in flavorlist:
1219 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1220 "Failed retrieve flavor information "
1221 "flavor id {}".format(name, flavor_id))
1222 else:
1223 try:
1224 flavor = flavorlist[flavor_id]
1225 vm_cpus = flavor[FLAVOR_VCPUS_KEY]
1226 vm_memory = flavor[FLAVOR_RAM_KEY]
1227 vm_disk = flavor[FLAVOR_DISK_KEY]
1228 extended = flavor.get("extended", None)
1229 if extended:
1230 numas=extended.get("numas", None)
1231 if numas:
1232 for numa in numas:
1233 for interface in numa.get("interfaces",() ):
1234 if interface["dedicated"].strip()=="yes":
1235 pci_devices_info.append(interface)
1236 except KeyError:
1237 raise vimconn.vimconnException("Corrupted flavor. {}".format(flavor_id))
1238
1239 # image upload creates template name as catalog name space Template.
1240 templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1241 power_on = 'false'
1242 if start:
1243 power_on = 'true'
1244
1245 # client must provide at least one entry in net_list if not we report error
1246 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1247 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1248 primary_net = None
1249 primary_netname = None
1250 network_mode = 'bridged'
1251 if net_list is not None and len(net_list) > 0:
1252 for net in net_list:
1253 if 'use' in net and net['use'] == 'mgmt':
1254 primary_net = net
1255 if primary_net is None:
1256 primary_net = net_list[0]
1257
1258 try:
1259 primary_net_id = primary_net['net_id']
1260 network_dict = self.get_vcd_network(network_uuid=primary_net_id)
1261 if 'name' in network_dict:
1262 primary_netname = network_dict['name']
1263
1264 except KeyError:
1265 raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
1266 else:
1267 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
1268
1269 # use: 'data', 'bridge', 'mgmt'
1270 # create vApp. Set vcpu and ram based on flavor id.
1271 vapptask = vca.create_vapp(self.tenant_name, vmname_andid, templateName,
1272 self.get_catalogbyid(image_id, catalogs),
1273 network_name=None, # None while creating vapp
1274 network_mode=network_mode,
1275 vm_name=vmname_andid,
1276 vm_cpus=vm_cpus, # can be None if flavor is None
1277 vm_memory=vm_memory) # can be None if flavor is None
1278
1279 if vapptask is None or vapptask is False:
1280 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid))
1281 if type(vapptask) is VappTask:
1282 vca.block_until_completed(vapptask)
1283
1284 # we should have now vapp in undeployed state.
1285 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1286 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1287 if vapp is None:
1288 raise vimconn.vimconnUnexpectedResponse(
1289 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
1290 vmname_andid))
1291
1292 #Add PCI passthrough configrations
1293 PCI_devices_status = False
1294 vm_obj = None
1295 si = None
1296 if len(pci_devices_info) > 0:
1297 self.logger.info("Need to add PCI devices {} into VM {}".format(pci_devices_info,
1298 vmname_andid ))
1299 PCI_devices_status, vm_obj, vcenter_conect = self.add_pci_devices(vapp_uuid,
1300 pci_devices_info,
1301 vmname_andid)
1302 if PCI_devices_status:
1303 self.logger.info("Added PCI devives {} to VM {}".format(
1304 pci_devices_info,
1305 vmname_andid)
1306 )
1307 else:
1308 self.logger.info("Fail to add PCI devives {} to VM {}".format(
1309 pci_devices_info,
1310 vmname_andid)
1311 )
1312 # add vm disk
1313 if vm_disk:
1314 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1315 result = self.modify_vm_disk(vapp_uuid, vm_disk)
1316 if result :
1317 self.logger.debug("Modified Disk size of VM {} ".format(vmname_andid))
1318
1319 # add NICs & connect to networks in netlist
1320 try:
1321 self.logger.info("Request to connect VM to a network: {}".format(net_list))
1322 nicIndex = 0
1323 primary_nic_index = 0
1324 for net in net_list:
1325 # openmano uses network id in UUID format.
1326 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1327 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1328 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1329
1330 if 'net_id' not in net:
1331 continue
1332
1333 interface_net_id = net['net_id']
1334 interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
1335 interface_network_mode = net['use']
1336
1337 if interface_network_mode == 'mgmt':
1338 primary_nic_index = nicIndex
1339
1340 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1341 - DHCP (The IP address is obtained from a DHCP service.)
1342 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1343 - NONE (No IP addressing mode specified.)"""
1344
1345 if primary_netname is not None:
1346 nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name))
1347 if len(nets) == 1:
1348 self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name))
1349 task = vapp.connect_to_network(nets[0].name, nets[0].href)
1350 if type(task) is GenericTask:
1351 vca.block_until_completed(task)
1352 # connect network to VM - with all DHCP by default
1353 self.logger.info("new_vminstance(): Connecting VM to a network {}".format(nets[0].name))
1354 task = vapp.connect_vms(nets[0].name,
1355 connection_index=nicIndex,
1356 connections_primary_index=primary_nic_index,
1357 ip_allocation_mode='DHCP')
1358 if type(task) is GenericTask:
1359 vca.block_until_completed(task)
1360 nicIndex += 1
1361 except KeyError:
1362 # it might be a case if specific mandatory entry in dict is empty
1363 self.logger.debug("Key error {}".format(KeyError.message))
1364 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1365
1366 # deploy and power on vm
1367 self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name))
1368 deploytask = vapp.deploy(powerOn=False)
1369 if type(deploytask) is GenericTask:
1370 vca.block_until_completed(deploytask)
1371
1372 # If VM has PCI devices reserve memory for VM
1373 if PCI_devices_status and vm_obj and vcenter_conect:
1374 memReserve = vm_obj.config.hardware.memoryMB
1375 spec = vim.vm.ConfigSpec()
1376 spec.memoryAllocation = vim.ResourceAllocationInfo(reservation=memReserve)
1377 task = vm_obj.ReconfigVM_Task(spec=spec)
1378 if task:
1379 result = self.wait_for_vcenter_task(task, vcenter_conect)
1380 self.logger.info("Reserved memmoery {} MB for "\
1381 "VM VM status: {}".format(str(memReserve),result))
1382 else:
1383 self.logger.info("Fail to reserved memmoery {} to VM {}".format(
1384 str(memReserve),str(vm_obj)))
1385
1386 self.logger.debug("new_vminstance(): power on vApp {} ".format(name))
1387 poweron_task = vapp.poweron()
1388 if type(poweron_task) is GenericTask:
1389 vca.block_until_completed(poweron_task)
1390
1391 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1392 wait_time = 0
1393 vapp_uuid = None
1394 while wait_time <= MAX_WAIT_TIME:
1395 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1396 if vapp and vapp.me.deployed:
1397 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1398 break
1399 else:
1400 self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
1401 time.sleep(INTERVAL_TIME)
1402
1403 wait_time +=INTERVAL_TIME
1404
1405 if vapp_uuid is not None:
1406 return vapp_uuid
1407 else:
1408 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1409
1410 ##
1411 ##
1412 ## based on current discussion
1413 ##
1414 ##
1415 ## server:
1416 # created: '2016-09-08T11:51:58'
1417 # description: simple-instance.linux1.1
1418 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1419 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1420 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1421 # status: ACTIVE
1422 # error_msg:
1423 # interfaces: …
1424 #
1425 def get_vminstance(self, vim_vm_uuid=None):
1426 """Returns the VM instance information from VIM"""
1427
1428 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
1429 vca = self.connect()
1430 if not vca:
1431 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1432
1433 vdc = vca.get_vdc(self.tenant_name)
1434 if vdc is None:
1435 raise vimconn.vimconnConnectionException(
1436 "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1437
1438 vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
1439 if not vm_info_dict:
1440 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1441 raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1442
1443 status_key = vm_info_dict['status']
1444 error = ''
1445 try:
1446 vm_dict = {'created': vm_info_dict['created'],
1447 'description': vm_info_dict['name'],
1448 'status': vcdStatusCode2manoFormat[int(status_key)],
1449 'hostId': vm_info_dict['vmuuid'],
1450 'error_msg': error,
1451 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1452
1453 if 'interfaces' in vm_info_dict:
1454 vm_dict['interfaces'] = vm_info_dict['interfaces']
1455 else:
1456 vm_dict['interfaces'] = []
1457 except KeyError:
1458 vm_dict = {'created': '',
1459 'description': '',
1460 'status': vcdStatusCode2manoFormat[int(-1)],
1461 'hostId': vm_info_dict['vmuuid'],
1462 'error_msg': "Inconsistency state",
1463 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1464
1465 return vm_dict
1466
1467 def delete_vminstance(self, vm__vim_uuid):
1468 """Method poweroff and remove VM instance from vcloud director network.
1469
1470 Args:
1471 vm__vim_uuid: VM UUID
1472
1473 Returns:
1474 Returns the instance identifier
1475 """
1476
1477 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
1478 vca = self.connect()
1479 if not vca:
1480 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1481
1482 vdc = vca.get_vdc(self.tenant_name)
1483 if vdc is None:
1484 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1485 self.tenant_name))
1486 raise vimconn.vimconnException(
1487 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1488
1489 try:
1490 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1491 if vapp_name is None:
1492 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1493 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1494 else:
1495 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1496
1497 # Delete vApp and wait for status change if task executed and vApp is None.
1498 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1499
1500 if vapp:
1501 if vapp.me.deployed:
1502 self.logger.info("Powering off vApp {}".format(vapp_name))
1503 #Power off vApp
1504 powered_off = False
1505 wait_time = 0
1506 while wait_time <= MAX_WAIT_TIME:
1507 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1508 if not vapp:
1509 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1510 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1511
1512 power_off_task = vapp.poweroff()
1513 if type(power_off_task) is GenericTask:
1514 result = vca.block_until_completed(power_off_task)
1515 if result:
1516 powered_off = True
1517 break
1518 else:
1519 self.logger.info("Wait for vApp {} to power off".format(vapp_name))
1520 time.sleep(INTERVAL_TIME)
1521
1522 wait_time +=INTERVAL_TIME
1523 if not powered_off:
1524 self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
1525 else:
1526 self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
1527
1528 #Undeploy vApp
1529 self.logger.info("Undeploy vApp {}".format(vapp_name))
1530 wait_time = 0
1531 undeployed = False
1532 while wait_time <= MAX_WAIT_TIME:
1533 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1534 if not vapp:
1535 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1536 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1537 undeploy_task = vapp.undeploy(action='powerOff')
1538
1539 if type(undeploy_task) is GenericTask:
1540 result = vca.block_until_completed(undeploy_task)
1541 if result:
1542 undeployed = True
1543 break
1544 else:
1545 self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
1546 time.sleep(INTERVAL_TIME)
1547
1548 wait_time +=INTERVAL_TIME
1549
1550 if not undeployed:
1551 self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
1552
1553 # delete vapp
1554 self.logger.info("Start deletion of vApp {} ".format(vapp_name))
1555 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1556
1557 if vapp is not None:
1558 wait_time = 0
1559 result = False
1560
1561 while wait_time <= MAX_WAIT_TIME:
1562 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1563 if not vapp:
1564 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1565 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1566
1567 delete_task = vapp.delete()
1568
1569 if type(delete_task) is GenericTask:
1570 vca.block_until_completed(delete_task)
1571 result = vca.block_until_completed(delete_task)
1572 if result:
1573 break
1574 else:
1575 self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
1576 time.sleep(INTERVAL_TIME)
1577
1578 wait_time +=INTERVAL_TIME
1579
1580 if not result:
1581 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
1582
1583 except:
1584 self.logger.debug(traceback.format_exc())
1585 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1586
1587 if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
1588 self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
1589 return vm__vim_uuid
1590 else:
1591 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1592
1593 def refresh_vms_status(self, vm_list):
1594 """Get the status of the virtual machines and their interfaces/ports
1595 Params: the list of VM identifiers
1596 Returns a dictionary with:
1597 vm_id: #VIM id of this Virtual Machine
1598 status: #Mandatory. Text with one of:
1599 # DELETED (not found at vim)
1600 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1601 # OTHER (Vim reported other status not understood)
1602 # ERROR (VIM indicates an ERROR status)
1603 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1604 # CREATING (on building process), ERROR
1605 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1606 #
1607 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1608 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1609 interfaces:
1610 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1611 mac_address: #Text format XX:XX:XX:XX:XX:XX
1612 vim_net_id: #network id where this interface is connected
1613 vim_interface_id: #interface/port VIM id
1614 ip_address: #null, or text with IPv4, IPv6 address
1615 """
1616
1617 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
1618 vca = self.connect()
1619 if not vca:
1620 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1621
1622 vdc = vca.get_vdc(self.tenant_name)
1623 if vdc is None:
1624 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1625
1626 vms_dict = {}
1627 for vmuuid in vm_list:
1628 vmname = self.get_namebyvappid(vca, vdc, vmuuid)
1629 if vmname is not None:
1630
1631 the_vapp = vca.get_vapp(vdc, vmname)
1632 vm_info = the_vapp.get_vms_details()
1633 vm_status = vm_info[0]['status']
1634 vm_pci_details = self.get_vm_pci_details(vmuuid)
1635 vm_info[0].update(vm_pci_details)
1636
1637 vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1638 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1639 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
1640
1641 # get networks
1642 try:
1643 vm_app_networks = the_vapp.get_vms_network_info()
1644 for vapp_network in vm_app_networks:
1645 for vm_network in vapp_network:
1646 if vm_network['name'] == vmname:
1647 interface = {"mac_address": vm_network['mac'],
1648 "vim_net_id": self.get_network_id_by_name(vm_network['network_name']),
1649 "vim_interface_id": self.get_network_id_by_name(vm_network['network_name']),
1650 'ip_address': vm_network['ip']}
1651 # interface['vim_info'] = yaml.safe_dump(vm_network)
1652 vm_dict["interfaces"].append(interface)
1653 # add a vm to vm dict
1654 vms_dict.setdefault(vmuuid, vm_dict)
1655 except KeyError:
1656 self.logger.debug("Error in respond {}".format(KeyError.message))
1657 self.logger.debug(traceback.format_exc())
1658
1659 return vms_dict
1660
1661 def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
1662 """Send and action over a VM instance from VIM
1663 Returns the vm_id if the action was successfully sent to the VIM"""
1664
1665 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
1666 if vm__vim_uuid is None or action_dict is None:
1667 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
1668
1669 vca = self.connect()
1670 if not vca:
1671 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1672
1673 vdc = vca.get_vdc(self.tenant_name)
1674 if vdc is None:
1675 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
1676
1677 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1678 if vapp_name is None:
1679 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1680 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1681 else:
1682 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1683
1684 try:
1685 the_vapp = vca.get_vapp(vdc, vapp_name)
1686 # TODO fix all status
1687 if "start" in action_dict:
1688 if action_dict["start"] == "rebuild":
1689 the_vapp.deploy(powerOn=True)
1690 else:
1691 vm_info = the_vapp.get_vms_details()
1692 vm_status = vm_info[0]['status']
1693 if vm_status == "Suspended":
1694 the_vapp.poweron()
1695 elif vm_status.status == "Powered off":
1696 the_vapp.poweron()
1697 elif "pause" in action_dict:
1698 pass
1699 ## server.pause()
1700 elif "resume" in action_dict:
1701 pass
1702 ## server.resume()
1703 elif "shutoff" in action_dict or "shutdown" in action_dict:
1704 the_vapp.shutdown()
1705 elif "forceOff" in action_dict:
1706 the_vapp.reset()
1707 elif "terminate" in action_dict:
1708 the_vapp.delete()
1709 # elif "createImage" in action_dict:
1710 # server.create_image()
1711 else:
1712 pass
1713 except:
1714 pass
1715
1716 def get_vminstance_console(self, vm_id, console_type="vnc"):
1717 """
1718 Get a console for the virtual machine
1719 Params:
1720 vm_id: uuid of the VM
1721 console_type, can be:
1722 "novnc" (by default), "xvpvnc" for VNC types,
1723 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1724 Returns dict with the console parameters:
1725 protocol: ssh, ftp, http, https, ...
1726 server: usually ip address
1727 port: the http, ssh, ... port
1728 suffix: extra text, e.g. the http path and query string
1729 """
1730 raise vimconn.vimconnNotImplemented("Should have implemented this")
1731
1732 # NOT USED METHODS in current version
1733
1734 def host_vim2gui(self, host, server_dict):
1735 """Transform host dictionary from VIM format to GUI format,
1736 and append to the server_dict
1737 """
1738 raise vimconn.vimconnNotImplemented("Should have implemented this")
1739
1740 def get_hosts_info(self):
1741 """Get the information of deployed hosts
1742 Returns the hosts content"""
1743 raise vimconn.vimconnNotImplemented("Should have implemented this")
1744
1745 def get_hosts(self, vim_tenant):
1746 """Get the hosts and deployed instances
1747 Returns the hosts content"""
1748 raise vimconn.vimconnNotImplemented("Should have implemented this")
1749
1750 def get_processor_rankings(self):
1751 """Get the processor rankings in the VIM database"""
1752 raise vimconn.vimconnNotImplemented("Should have implemented this")
1753
1754 def new_host(self, host_data):
1755 """Adds a new host to VIM"""
1756 '''Returns status code of the VIM response'''
1757 raise vimconn.vimconnNotImplemented("Should have implemented this")
1758
1759 def new_external_port(self, port_data):
1760 """Adds a external port to VIM"""
1761 '''Returns the port identifier'''
1762 raise vimconn.vimconnNotImplemented("Should have implemented this")
1763
1764 def new_external_network(self, net_name, net_type):
1765 """Adds a external network to VIM (shared)"""
1766 '''Returns the network identifier'''
1767 raise vimconn.vimconnNotImplemented("Should have implemented this")
1768
1769 def connect_port_network(self, port_id, network_id, admin=False):
1770 """Connects a external port to a network"""
1771 '''Returns status code of the VIM response'''
1772 raise vimconn.vimconnNotImplemented("Should have implemented this")
1773
1774 def new_vminstancefromJSON(self, vm_data):
1775 """Adds a VM instance to VIM"""
1776 '''Returns the instance identifier'''
1777 raise vimconn.vimconnNotImplemented("Should have implemented this")
1778
1779 def get_network_name_by_id(self, network_uuid=None):
1780 """Method gets vcloud director network named based on supplied uuid.
1781
1782 Args:
1783 network_uuid: network_id
1784
1785 Returns:
1786 The return network name.
1787 """
1788
1789 vca = self.connect()
1790 if not vca:
1791 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1792
1793 if not network_uuid:
1794 return None
1795
1796 try:
1797 org_dict = self.get_org(self.org_uuid)
1798 if 'networks' in org_dict:
1799 org_network_dict = org_dict['networks']
1800 for net_uuid in org_network_dict:
1801 if net_uuid == network_uuid:
1802 return org_network_dict[net_uuid]
1803 except:
1804 self.logger.debug("Exception in get_network_name_by_id")
1805 self.logger.debug(traceback.format_exc())
1806
1807 return None
1808
1809 def get_network_id_by_name(self, network_name=None):
1810 """Method gets vcloud director network uuid based on supplied name.
1811
1812 Args:
1813 network_name: network_name
1814 Returns:
1815 The return network uuid.
1816 network_uuid: network_id
1817 """
1818
1819 vca = self.connect()
1820 if not vca:
1821 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1822
1823 if not network_name:
1824 self.logger.debug("get_network_id_by_name() : Network name is empty")
1825 return None
1826
1827 try:
1828 org_dict = self.get_org(self.org_uuid)
1829 if org_dict and 'networks' in org_dict:
1830 org_network_dict = org_dict['networks']
1831 for net_uuid,net_name in org_network_dict.iteritems():
1832 if net_name == network_name:
1833 return net_uuid
1834
1835 except KeyError as exp:
1836 self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
1837
1838 return None
1839
1840 def list_org_action(self):
1841 """
1842 Method leverages vCloud director and query for available organization for particular user
1843
1844 Args:
1845 vca - is active VCA connection.
1846 vdc_name - is a vdc name that will be used to query vms action
1847
1848 Returns:
1849 The return XML respond
1850 """
1851
1852 vca = self.connect()
1853 if not vca:
1854 raise vimconn.vimconnConnectionException("self.connect() is failed")
1855
1856 url_list = [vca.host, '/api/org']
1857 vm_list_rest_call = ''.join(url_list)
1858
1859 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1860 response = Http.get(url=vm_list_rest_call,
1861 headers=vca.vcloud_session.get_vcloud_headers(),
1862 verify=vca.verify,
1863 logger=vca.logger)
1864 if response.status_code == requests.codes.ok:
1865 return response.content
1866
1867 return None
1868
1869 def get_org_action(self, org_uuid=None):
1870 """
1871 Method leverages vCloud director and retrieve available object fdr organization.
1872
1873 Args:
1874 vca - is active VCA connection.
1875 vdc_name - is a vdc name that will be used to query vms action
1876
1877 Returns:
1878 The return XML respond
1879 """
1880
1881 vca = self.connect()
1882 if not vca:
1883 raise vimconn.vimconnConnectionException("self.connect() is failed")
1884
1885 if org_uuid is None:
1886 return None
1887
1888 url_list = [vca.host, '/api/org/', org_uuid]
1889 vm_list_rest_call = ''.join(url_list)
1890
1891 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1892 response = Http.get(url=vm_list_rest_call,
1893 headers=vca.vcloud_session.get_vcloud_headers(),
1894 verify=vca.verify,
1895 logger=vca.logger)
1896 if response.status_code == requests.codes.ok:
1897 return response.content
1898
1899 return None
1900
1901 def get_org(self, org_uuid=None):
1902 """
1903 Method retrieves available organization in vCloud Director
1904
1905 Args:
1906 org_uuid - is a organization uuid.
1907
1908 Returns:
1909 The return dictionary with following key
1910 "network" - for network list under the org
1911 "catalogs" - for network list under the org
1912 "vdcs" - for vdc list under org
1913 """
1914
1915 org_dict = {}
1916 vca = self.connect()
1917 if not vca:
1918 raise vimconn.vimconnConnectionException("self.connect() is failed")
1919
1920 if org_uuid is None:
1921 return org_dict
1922
1923 content = self.get_org_action(org_uuid=org_uuid)
1924 try:
1925 vdc_list = {}
1926 network_list = {}
1927 catalog_list = {}
1928 vm_list_xmlroot = XmlElementTree.fromstring(content)
1929 for child in vm_list_xmlroot:
1930 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1931 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1932 org_dict['vdcs'] = vdc_list
1933 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1934 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1935 org_dict['networks'] = network_list
1936 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1937 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1938 org_dict['catalogs'] = catalog_list
1939 except:
1940 pass
1941
1942 return org_dict
1943
1944 def get_org_list(self):
1945 """
1946 Method retrieves available organization in vCloud Director
1947
1948 Args:
1949 vca - is active VCA connection.
1950
1951 Returns:
1952 The return dictionary and key for each entry VDC UUID
1953 """
1954
1955 org_dict = {}
1956 vca = self.connect()
1957 if not vca:
1958 raise vimconn.vimconnConnectionException("self.connect() is failed")
1959
1960 content = self.list_org_action()
1961 try:
1962 vm_list_xmlroot = XmlElementTree.fromstring(content)
1963 for vm_xml in vm_list_xmlroot:
1964 if vm_xml.tag.split("}")[1] == 'Org':
1965 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
1966 org_dict[org_uuid[0]] = vm_xml.attrib['name']
1967 except:
1968 pass
1969
1970 return org_dict
1971
1972 def vms_view_action(self, vdc_name=None):
1973 """ Method leverages vCloud director vms query call
1974
1975 Args:
1976 vca - is active VCA connection.
1977 vdc_name - is a vdc name that will be used to query vms action
1978
1979 Returns:
1980 The return XML respond
1981 """
1982 vca = self.connect()
1983 if vdc_name is None:
1984 return None
1985
1986 url_list = [vca.host, '/api/vms/query']
1987 vm_list_rest_call = ''.join(url_list)
1988
1989 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1990 refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
1991 vca.vcloud_session.organization.Link)
1992 if len(refs) == 1:
1993 response = Http.get(url=vm_list_rest_call,
1994 headers=vca.vcloud_session.get_vcloud_headers(),
1995 verify=vca.verify,
1996 logger=vca.logger)
1997 if response.status_code == requests.codes.ok:
1998 return response.content
1999
2000 return None
2001
2002 def get_vapp_list(self, vdc_name=None):
2003 """
2004 Method retrieves vApp list deployed vCloud director and returns a dictionary
2005 contains a list of all vapp deployed for queried VDC.
2006 The key for a dictionary is vApp UUID
2007
2008
2009 Args:
2010 vca - is active VCA connection.
2011 vdc_name - is a vdc name that will be used to query vms action
2012
2013 Returns:
2014 The return dictionary and key for each entry vapp UUID
2015 """
2016
2017 vapp_dict = {}
2018 if vdc_name is None:
2019 return vapp_dict
2020
2021 content = self.vms_view_action(vdc_name=vdc_name)
2022 try:
2023 vm_list_xmlroot = XmlElementTree.fromstring(content)
2024 for vm_xml in vm_list_xmlroot:
2025 if vm_xml.tag.split("}")[1] == 'VMRecord':
2026 if vm_xml.attrib['isVAppTemplate'] == 'true':
2027 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
2028 if 'vappTemplate-' in rawuuid[0]:
2029 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2030 # vm and use raw UUID as key
2031 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
2032 except:
2033 pass
2034
2035 return vapp_dict
2036
2037 def get_vm_list(self, vdc_name=None):
2038 """
2039 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2040 contains a list of all VM's deployed for queried VDC.
2041 The key for a dictionary is VM UUID
2042
2043
2044 Args:
2045 vca - is active VCA connection.
2046 vdc_name - is a vdc name that will be used to query vms action
2047
2048 Returns:
2049 The return dictionary and key for each entry vapp UUID
2050 """
2051 vm_dict = {}
2052
2053 if vdc_name is None:
2054 return vm_dict
2055
2056 content = self.vms_view_action(vdc_name=vdc_name)
2057 try:
2058 vm_list_xmlroot = XmlElementTree.fromstring(content)
2059 for vm_xml in vm_list_xmlroot:
2060 if vm_xml.tag.split("}")[1] == 'VMRecord':
2061 if vm_xml.attrib['isVAppTemplate'] == 'false':
2062 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2063 if 'vm-' in rawuuid[0]:
2064 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2065 # vm and use raw UUID as key
2066 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2067 except:
2068 pass
2069
2070 return vm_dict
2071
2072 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
2073 """
2074 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2075 contains a list of all VM's deployed for queried VDC.
2076 The key for a dictionary is VM UUID
2077
2078
2079 Args:
2080 vca - is active VCA connection.
2081 vdc_name - is a vdc name that will be used to query vms action
2082
2083 Returns:
2084 The return dictionary and key for each entry vapp UUID
2085 """
2086 vm_dict = {}
2087 vca = self.connect()
2088 if not vca:
2089 raise vimconn.vimconnConnectionException("self.connect() is failed")
2090
2091 if vdc_name is None:
2092 return vm_dict
2093
2094 content = self.vms_view_action(vdc_name=vdc_name)
2095 try:
2096 vm_list_xmlroot = XmlElementTree.fromstring(content)
2097 for vm_xml in vm_list_xmlroot:
2098 if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
2099 # lookup done by UUID
2100 if isuuid:
2101 if vapp_name in vm_xml.attrib['container']:
2102 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2103 if 'vm-' in rawuuid[0]:
2104 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2105 break
2106 # lookup done by Name
2107 else:
2108 if vapp_name in vm_xml.attrib['name']:
2109 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2110 if 'vm-' in rawuuid[0]:
2111 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2112 break
2113 except:
2114 pass
2115
2116 return vm_dict
2117
2118 def get_network_action(self, network_uuid=None):
2119 """
2120 Method leverages vCloud director and query network based on network uuid
2121
2122 Args:
2123 vca - is active VCA connection.
2124 network_uuid - is a network uuid
2125
2126 Returns:
2127 The return XML respond
2128 """
2129
2130 vca = self.connect()
2131 if not vca:
2132 raise vimconn.vimconnConnectionException("self.connect() is failed")
2133
2134 if network_uuid is None:
2135 return None
2136
2137 url_list = [vca.host, '/api/network/', network_uuid]
2138 vm_list_rest_call = ''.join(url_list)
2139
2140 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2141 response = Http.get(url=vm_list_rest_call,
2142 headers=vca.vcloud_session.get_vcloud_headers(),
2143 verify=vca.verify,
2144 logger=vca.logger)
2145 if response.status_code == requests.codes.ok:
2146 return response.content
2147
2148 return None
2149
2150 def get_vcd_network(self, network_uuid=None):
2151 """
2152 Method retrieves available network from vCloud Director
2153
2154 Args:
2155 network_uuid - is VCD network UUID
2156
2157 Each element serialized as key : value pair
2158
2159 Following keys available for access. network_configuration['Gateway'}
2160 <Configuration>
2161 <IpScopes>
2162 <IpScope>
2163 <IsInherited>true</IsInherited>
2164 <Gateway>172.16.252.100</Gateway>
2165 <Netmask>255.255.255.0</Netmask>
2166 <Dns1>172.16.254.201</Dns1>
2167 <Dns2>172.16.254.202</Dns2>
2168 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2169 <IsEnabled>true</IsEnabled>
2170 <IpRanges>
2171 <IpRange>
2172 <StartAddress>172.16.252.1</StartAddress>
2173 <EndAddress>172.16.252.99</EndAddress>
2174 </IpRange>
2175 </IpRanges>
2176 </IpScope>
2177 </IpScopes>
2178 <FenceMode>bridged</FenceMode>
2179
2180 Returns:
2181 The return dictionary and key for each entry vapp UUID
2182 """
2183
2184 network_configuration = {}
2185 if network_uuid is None:
2186 return network_uuid
2187
2188 content = self.get_network_action(network_uuid=network_uuid)
2189 try:
2190 vm_list_xmlroot = XmlElementTree.fromstring(content)
2191
2192 network_configuration['status'] = vm_list_xmlroot.get("status")
2193 network_configuration['name'] = vm_list_xmlroot.get("name")
2194 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
2195
2196 for child in vm_list_xmlroot:
2197 if child.tag.split("}")[1] == 'IsShared':
2198 network_configuration['isShared'] = child.text.strip()
2199 if child.tag.split("}")[1] == 'Configuration':
2200 for configuration in child.iter():
2201 tagKey = configuration.tag.split("}")[1].strip()
2202 if tagKey != "":
2203 network_configuration[tagKey] = configuration.text.strip()
2204 return network_configuration
2205 except:
2206 pass
2207
2208 return network_configuration
2209
2210 def delete_network_action(self, network_uuid=None):
2211 """
2212 Method delete given network from vCloud director
2213
2214 Args:
2215 network_uuid - is a network uuid that client wish to delete
2216
2217 Returns:
2218 The return None or XML respond or false
2219 """
2220
2221 vca = self.connect_as_admin()
2222 if not vca:
2223 raise vimconn.vimconnConnectionException("self.connect() is failed")
2224 if network_uuid is None:
2225 return False
2226
2227 url_list = [vca.host, '/api/admin/network/', network_uuid]
2228 vm_list_rest_call = ''.join(url_list)
2229
2230 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2231 response = Http.delete(url=vm_list_rest_call,
2232 headers=vca.vcloud_session.get_vcloud_headers(),
2233 verify=vca.verify,
2234 logger=vca.logger)
2235
2236 if response.status_code == 202:
2237 return True
2238
2239 return False
2240
2241 def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2242 ip_profile=None, isshared='true'):
2243 """
2244 Method create network in vCloud director
2245
2246 Args:
2247 network_name - is network name to be created.
2248 net_type - can be 'bridge','data','ptp','mgmt'.
2249 ip_profile is a dict containing the IP parameters of the network
2250 isshared - is a boolean
2251 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2252 It optional attribute. by default if no parent network indicate the first available will be used.
2253
2254 Returns:
2255 The return network uuid or return None
2256 """
2257
2258 new_network_name = [network_name, '-', str(uuid.uuid4())]
2259 content = self.create_network_rest(network_name=''.join(new_network_name),
2260 ip_profile=ip_profile,
2261 net_type=net_type,
2262 parent_network_uuid=parent_network_uuid,
2263 isshared=isshared)
2264 if content is None:
2265 self.logger.debug("Failed create network {}.".format(network_name))
2266 return None
2267
2268 try:
2269 vm_list_xmlroot = XmlElementTree.fromstring(content)
2270 vcd_uuid = vm_list_xmlroot.get('id').split(":")
2271 if len(vcd_uuid) == 4:
2272 self.logger.info("Create new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
2273 return vcd_uuid[3]
2274 except:
2275 self.logger.debug("Failed create network {}".format(network_name))
2276 return None
2277
2278 def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2279 ip_profile=None, isshared='true'):
2280 """
2281 Method create network in vCloud director
2282
2283 Args:
2284 network_name - is network name to be created.
2285 net_type - can be 'bridge','data','ptp','mgmt'.
2286 ip_profile is a dict containing the IP parameters of the network
2287 isshared - is a boolean
2288 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2289 It optional attribute. by default if no parent network indicate the first available will be used.
2290
2291 Returns:
2292 The return network uuid or return None
2293 """
2294
2295 vca = self.connect_as_admin()
2296 if not vca:
2297 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2298 if network_name is None:
2299 return None
2300
2301 url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
2302 vm_list_rest_call = ''.join(url_list)
2303 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2304 response = Http.get(url=vm_list_rest_call,
2305 headers=vca.vcloud_session.get_vcloud_headers(),
2306 verify=vca.verify,
2307 logger=vca.logger)
2308
2309 provider_network = None
2310 available_networks = None
2311 add_vdc_rest_url = None
2312
2313 if response.status_code != requests.codes.ok:
2314 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2315 response.status_code))
2316 return None
2317 else:
2318 try:
2319 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2320 for child in vm_list_xmlroot:
2321 if child.tag.split("}")[1] == 'ProviderVdcReference':
2322 provider_network = child.attrib.get('href')
2323 # application/vnd.vmware.admin.providervdc+xml
2324 if child.tag.split("}")[1] == 'Link':
2325 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2326 and child.attrib.get('rel') == 'add':
2327 add_vdc_rest_url = child.attrib.get('href')
2328 except:
2329 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2330 self.logger.debug("Respond body {}".format(response.content))
2331 return None
2332
2333 # find pvdc provided available network
2334 response = Http.get(url=provider_network,
2335 headers=vca.vcloud_session.get_vcloud_headers(),
2336 verify=vca.verify,
2337 logger=vca.logger)
2338 if response.status_code != requests.codes.ok:
2339 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2340 response.status_code))
2341 return None
2342
2343 # available_networks.split("/")[-1]
2344
2345 if parent_network_uuid is None:
2346 try:
2347 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2348 for child in vm_list_xmlroot.iter():
2349 if child.tag.split("}")[1] == 'AvailableNetworks':
2350 for networks in child.iter():
2351 # application/vnd.vmware.admin.network+xml
2352 if networks.attrib.get('href') is not None:
2353 available_networks = networks.attrib.get('href')
2354 break
2355 except:
2356 return None
2357
2358 #Configure IP profile of the network
2359 ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
2360
2361 gateway_address=ip_profile['gateway_address']
2362 dhcp_count=int(ip_profile['dhcp_count'])
2363 subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
2364
2365 if ip_profile['dhcp_enabled']==True:
2366 dhcp_enabled='true'
2367 else:
2368 dhcp_enabled='false'
2369 dhcp_start_address=ip_profile['dhcp_start_address']
2370
2371 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2372 end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
2373 end_ip_int += dhcp_count - 1
2374 dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
2375
2376 ip_version=ip_profile['ip_version']
2377 dns_address=ip_profile['dns_address']
2378
2379 # either use client provided UUID or search for a first available
2380 # if both are not defined we return none
2381 if parent_network_uuid is not None:
2382 url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
2383 add_vdc_rest_url = ''.join(url_list)
2384
2385 if net_type=='ptp':
2386 fence_mode="isolated"
2387 isshared='false'
2388 is_inherited='false'
2389 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2390 <Description>Openmano created</Description>
2391 <Configuration>
2392 <IpScopes>
2393 <IpScope>
2394 <IsInherited>{1:s}</IsInherited>
2395 <Gateway>{2:s}</Gateway>
2396 <Netmask>{3:s}</Netmask>
2397 <Dns1>{4:s}</Dns1>
2398 <IsEnabled>{5:s}</IsEnabled>
2399 <IpRanges>
2400 <IpRange>
2401 <StartAddress>{6:s}</StartAddress>
2402 <EndAddress>{7:s}</EndAddress>
2403 </IpRange>
2404 </IpRanges>
2405 </IpScope>
2406 </IpScopes>
2407 <FenceMode>{8:s}</FenceMode>
2408 </Configuration>
2409 <IsShared>{9:s}</IsShared>
2410 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2411 subnet_address, dns_address, dhcp_enabled,
2412 dhcp_start_address, dhcp_end_address, fence_mode, isshared)
2413
2414 else:
2415 fence_mode="bridged"
2416 is_inherited='false'
2417 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2418 <Description>Openmano created</Description>
2419 <Configuration>
2420 <IpScopes>
2421 <IpScope>
2422 <IsInherited>{1:s}</IsInherited>
2423 <Gateway>{2:s}</Gateway>
2424 <Netmask>{3:s}</Netmask>
2425 <Dns1>{4:s}</Dns1>
2426 <IsEnabled>{5:s}</IsEnabled>
2427 <IpRanges>
2428 <IpRange>
2429 <StartAddress>{6:s}</StartAddress>
2430 <EndAddress>{7:s}</EndAddress>
2431 </IpRange>
2432 </IpRanges>
2433 </IpScope>
2434 </IpScopes>
2435 <ParentNetwork href="{8:s}"/>
2436 <FenceMode>{9:s}</FenceMode>
2437 </Configuration>
2438 <IsShared>{10:s}</IsShared>
2439 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2440 subnet_address, dns_address, dhcp_enabled,
2441 dhcp_start_address, dhcp_end_address, available_networks,
2442 fence_mode, isshared)
2443
2444 headers = vca.vcloud_session.get_vcloud_headers()
2445 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2446 try:
2447 response = Http.post(url=add_vdc_rest_url,
2448 headers=headers,
2449 data=data,
2450 verify=vca.verify,
2451 logger=vca.logger)
2452
2453 if response.status_code != 201:
2454 self.logger.debug("Create Network POST REST API call failed. Return status code {}"
2455 .format(response.status_code))
2456 else:
2457 network = networkType.parseString(response.content, True)
2458 create_nw_task = network.get_Tasks().get_Task()[0]
2459
2460 # if we all ok we respond with content after network creation completes
2461 # otherwise by default return None
2462 if create_nw_task is not None:
2463 self.logger.debug("Create Network REST : Waiting for Nw creation complete")
2464 status = vca.block_until_completed(create_nw_task)
2465 if status:
2466 return response.content
2467 else:
2468 self.logger.debug("create_network_rest task failed. Network Create response : {}"
2469 .format(response.content))
2470 except Exception as exp:
2471 self.logger.debug("create_network_rest : Exception : {} ".format(exp))
2472
2473 return None
2474
2475 def convert_cidr_to_netmask(self, cidr_ip=None):
2476 """
2477 Method sets convert CIDR netmask address to normal IP format
2478 Args:
2479 cidr_ip : CIDR IP address
2480 Returns:
2481 netmask : Converted netmask
2482 """
2483 if cidr_ip is not None:
2484 if '/' in cidr_ip:
2485 network, net_bits = cidr_ip.split('/')
2486 netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
2487 else:
2488 netmask = cidr_ip
2489 return netmask
2490 return None
2491
2492 def get_provider_rest(self, vca=None):
2493 """
2494 Method gets provider vdc view from vcloud director
2495
2496 Args:
2497 network_name - is network name to be created.
2498 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2499 It optional attribute. by default if no parent network indicate the first available will be used.
2500
2501 Returns:
2502 The return xml content of respond or None
2503 """
2504
2505 url_list = [vca.host, '/api/admin']
2506 response = Http.get(url=''.join(url_list),
2507 headers=vca.vcloud_session.get_vcloud_headers(),
2508 verify=vca.verify,
2509 logger=vca.logger)
2510
2511 if response.status_code == requests.codes.ok:
2512 return response.content
2513 return None
2514
2515 def create_vdc(self, vdc_name=None):
2516
2517 vdc_dict = {}
2518
2519 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
2520 if xml_content is not None:
2521 try:
2522 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
2523 for child in task_resp_xmlroot:
2524 if child.tag.split("}")[1] == 'Owner':
2525 vdc_id = child.attrib.get('href').split("/")[-1]
2526 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
2527 return vdc_dict
2528 except:
2529 self.logger.debug("Respond body {}".format(xml_content))
2530
2531 return None
2532
2533 def create_vdc_from_tmpl_rest(self, vdc_name=None):
2534 """
2535 Method create vdc in vCloud director based on VDC template.
2536 it uses pre-defined template that must be named openmano
2537
2538 Args:
2539 vdc_name - name of a new vdc.
2540
2541 Returns:
2542 The return xml content of respond or None
2543 """
2544
2545 self.logger.info("Creating new vdc {}".format(vdc_name))
2546 vca = self.connect()
2547 if not vca:
2548 raise vimconn.vimconnConnectionException("self.connect() is failed")
2549 if vdc_name is None:
2550 return None
2551
2552 url_list = [vca.host, '/api/vdcTemplates']
2553 vm_list_rest_call = ''.join(url_list)
2554 response = Http.get(url=vm_list_rest_call,
2555 headers=vca.vcloud_session.get_vcloud_headers(),
2556 verify=vca.verify,
2557 logger=vca.logger)
2558
2559 # container url to a template
2560 vdc_template_ref = None
2561 try:
2562 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2563 for child in vm_list_xmlroot:
2564 # application/vnd.vmware.admin.providervdc+xml
2565 # we need find a template from witch we instantiate VDC
2566 if child.tag.split("}")[1] == 'VdcTemplate':
2567 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
2568 'name') == 'openmano':
2569 vdc_template_ref = child.attrib.get('href')
2570 except:
2571 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2572 self.logger.debug("Respond body {}".format(response.content))
2573 return None
2574
2575 # if we didn't found required pre defined template we return None
2576 if vdc_template_ref is None:
2577 return None
2578
2579 try:
2580 # instantiate vdc
2581 url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
2582 vm_list_rest_call = ''.join(url_list)
2583 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2584 <Source href="{1:s}"></Source>
2585 <Description>opnemano</Description>
2586 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
2587 headers = vca.vcloud_session.get_vcloud_headers()
2588 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2589 response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
2590 logger=vca.logger)
2591 # if we all ok we respond with content otherwise by default None
2592 if response.status_code >= 200 and response.status_code < 300:
2593 return response.content
2594 return None
2595 except:
2596 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2597 self.logger.debug("Respond body {}".format(response.content))
2598
2599 return None
2600
2601 def create_vdc_rest(self, vdc_name=None):
2602 """
2603 Method create network in vCloud director
2604
2605 Args:
2606 network_name - is network name to be created.
2607 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2608 It optional attribute. by default if no parent network indicate the first available will be used.
2609
2610 Returns:
2611 The return network uuid or return None
2612 """
2613
2614 self.logger.info("Creating new vdc {}".format(vdc_name))
2615
2616 vca = self.connect_as_admin()
2617 if not vca:
2618 raise vimconn.vimconnConnectionException("self.connect() is failed")
2619 if vdc_name is None:
2620 return None
2621
2622 url_list = [vca.host, '/api/admin/org/', self.org_uuid]
2623 vm_list_rest_call = ''.join(url_list)
2624 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2625 response = Http.get(url=vm_list_rest_call,
2626 headers=vca.vcloud_session.get_vcloud_headers(),
2627 verify=vca.verify,
2628 logger=vca.logger)
2629
2630 provider_vdc_ref = None
2631 add_vdc_rest_url = None
2632 available_networks = None
2633
2634 if response.status_code != requests.codes.ok:
2635 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2636 response.status_code))
2637 return None
2638 else:
2639 try:
2640 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2641 for child in vm_list_xmlroot:
2642 # application/vnd.vmware.admin.providervdc+xml
2643 if child.tag.split("}")[1] == 'Link':
2644 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2645 and child.attrib.get('rel') == 'add':
2646 add_vdc_rest_url = child.attrib.get('href')
2647 except:
2648 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2649 self.logger.debug("Respond body {}".format(response.content))
2650 return None
2651
2652 response = self.get_provider_rest(vca=vca)
2653 try:
2654 vm_list_xmlroot = XmlElementTree.fromstring(response)
2655 for child in vm_list_xmlroot:
2656 if child.tag.split("}")[1] == 'ProviderVdcReferences':
2657 for sub_child in child:
2658 provider_vdc_ref = sub_child.attrib.get('href')
2659 except:
2660 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2661 self.logger.debug("Respond body {}".format(response))
2662 return None
2663
2664 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
2665 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2666 <AllocationModel>ReservationPool</AllocationModel>
2667 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2668 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2669 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2670 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2671 <ProviderVdcReference
2672 name="Main Provider"
2673 href="{2:s}" />
2674 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
2675 escape(vdc_name),
2676 provider_vdc_ref)
2677
2678 headers = vca.vcloud_session.get_vcloud_headers()
2679 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2680 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
2681 logger=vca.logger)
2682
2683 # if we all ok we respond with content otherwise by default None
2684 if response.status_code == 201:
2685 return response.content
2686 return None
2687
2688 def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
2689 """
2690 Method retrieve vapp detail from vCloud director
2691
2692 Args:
2693 vapp_uuid - is vapp identifier.
2694
2695 Returns:
2696 The return network uuid or return None
2697 """
2698
2699 parsed_respond = {}
2700 vca = None
2701
2702 if need_admin_access:
2703 vca = self.connect_as_admin()
2704 else:
2705 vca = self.connect()
2706
2707 if not vca:
2708 raise vimconn.vimconnConnectionException("self.connect() is failed")
2709 if vapp_uuid is None:
2710 return None
2711
2712 url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
2713 get_vapp_restcall = ''.join(url_list)
2714
2715 if vca.vcloud_session and vca.vcloud_session.organization:
2716 response = Http.get(url=get_vapp_restcall,
2717 headers=vca.vcloud_session.get_vcloud_headers(),
2718 verify=vca.verify,
2719 logger=vca.logger)
2720
2721 if response.status_code != requests.codes.ok:
2722 self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
2723 response.status_code))
2724 return parsed_respond
2725
2726 try:
2727 xmlroot_respond = XmlElementTree.fromstring(response.content)
2728 parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
2729
2730 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2731 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2732 'vmw': 'http://www.vmware.com/schema/ovf',
2733 'vm': 'http://www.vmware.com/vcloud/v1.5',
2734 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2735 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2736 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2737 }
2738
2739 created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
2740 if created_section is not None:
2741 parsed_respond['created'] = created_section.text
2742
2743 network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
2744 if network_section is not None and 'networkName' in network_section.attrib:
2745 parsed_respond['networkname'] = network_section.attrib['networkName']
2746
2747 ipscopes_section = \
2748 xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2749 namespaces)
2750 if ipscopes_section is not None:
2751 for ipscope in ipscopes_section:
2752 for scope in ipscope:
2753 tag_key = scope.tag.split("}")[1]
2754 if tag_key == 'IpRanges':
2755 ip_ranges = scope.getchildren()
2756 for ipblock in ip_ranges:
2757 for block in ipblock:
2758 parsed_respond[block.tag.split("}")[1]] = block.text
2759 else:
2760 parsed_respond[tag_key] = scope.text
2761
2762 # parse children section for other attrib
2763 children_section = xmlroot_respond.find('vm:Children/', namespaces)
2764 if children_section is not None:
2765 parsed_respond['name'] = children_section.attrib['name']
2766 parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
2767 if "nestedHypervisorEnabled" in children_section.attrib else None
2768 parsed_respond['deployed'] = children_section.attrib['deployed']
2769 parsed_respond['status'] = children_section.attrib['status']
2770 parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
2771 network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
2772 nic_list = []
2773 for adapters in network_adapter:
2774 adapter_key = adapters.tag.split("}")[1]
2775 if adapter_key == 'PrimaryNetworkConnectionIndex':
2776 parsed_respond['primarynetwork'] = adapters.text
2777 if adapter_key == 'NetworkConnection':
2778 vnic = {}
2779 if 'network' in adapters.attrib:
2780 vnic['network'] = adapters.attrib['network']
2781 for adapter in adapters:
2782 setting_key = adapter.tag.split("}")[1]
2783 vnic[setting_key] = adapter.text
2784 nic_list.append(vnic)
2785
2786 for link in children_section:
2787 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
2788 if link.attrib['rel'] == 'screen:acquireTicket':
2789 parsed_respond['acquireTicket'] = link.attrib
2790 if link.attrib['rel'] == 'screen:acquireMksTicket':
2791 parsed_respond['acquireMksTicket'] = link.attrib
2792
2793 parsed_respond['interfaces'] = nic_list
2794 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
2795 if vCloud_extension_section is not None:
2796 vm_vcenter_info = {}
2797 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
2798 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
2799 if vmext is not None:
2800 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
2801 vm_vcenter_info["vim_server_href"] = vmext.find('vmext:VimServerRef', namespaces).attrib['href']
2802 parsed_respond["vm_vcenter_info"]= vm_vcenter_info
2803
2804 virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
2805 vm_virtual_hardware_info = {}
2806 if virtual_hardware_section is not None:
2807 for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
2808 if item.find("rasd:Description",namespaces).text == "Hard disk":
2809 disk_size = item.find("rasd:HostResource" ,namespaces
2810 ).attrib["{"+namespaces['vm']+"}capacity"]
2811
2812 vm_virtual_hardware_info["disk_size"]= disk_size
2813 break
2814
2815 for link in virtual_hardware_section:
2816 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
2817 if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
2818 vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
2819 break
2820
2821 parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
2822 except Exception as exp :
2823 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
2824 return parsed_respond
2825
2826 def acuire_console(self, vm_uuid=None):
2827
2828 vca = self.connect()
2829 if not vca:
2830 raise vimconn.vimconnConnectionException("self.connect() is failed")
2831 if vm_uuid is None:
2832 return None
2833
2834 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2835 vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
2836 console_dict = vm_dict['acquireTicket']
2837 console_rest_call = console_dict['href']
2838
2839 response = Http.post(url=console_rest_call,
2840 headers=vca.vcloud_session.get_vcloud_headers(),
2841 verify=vca.verify,
2842 logger=vca.logger)
2843
2844 if response.status_code == requests.codes.ok:
2845 return response.content
2846
2847 return None
2848
2849 def modify_vm_disk(self, vapp_uuid, flavor_disk):
2850 """
2851 Method retrieve vm disk details
2852
2853 Args:
2854 vapp_uuid - is vapp identifier.
2855 flavor_disk - disk size as specified in VNFD (flavor)
2856
2857 Returns:
2858 The return network uuid or return None
2859 """
2860 status = None
2861 try:
2862 #Flavor disk is in GB convert it into MB
2863 flavor_disk = int(flavor_disk) * 1024
2864 vm_details = self.get_vapp_details_rest(vapp_uuid)
2865 if vm_details:
2866 vm_name = vm_details["name"]
2867 self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
2868
2869 if vm_details and "vm_virtual_hardware" in vm_details:
2870 vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
2871 disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
2872
2873 self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
2874
2875 if flavor_disk > vm_disk:
2876 status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
2877 self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
2878 vm_disk, flavor_disk ))
2879 else:
2880 status = True
2881 self.logger.info("No need to modify disk of VM {}".format(vm_name))
2882
2883 return status
2884 except Exception as exp:
2885 self.logger.info("Error occurred while modifing disk size {}".format(exp))
2886
2887
2888 def modify_vm_disk_rest(self, disk_href , disk_size):
2889 """
2890 Method retrieve modify vm disk size
2891
2892 Args:
2893 disk_href - vCD API URL to GET and PUT disk data
2894 disk_size - disk size as specified in VNFD (flavor)
2895
2896 Returns:
2897 The return network uuid or return None
2898 """
2899 vca = self.connect()
2900 if not vca:
2901 raise vimconn.vimconnConnectionException("self.connect() is failed")
2902 if disk_href is None or disk_size is None:
2903 return None
2904
2905 if vca.vcloud_session and vca.vcloud_session.organization:
2906 response = Http.get(url=disk_href,
2907 headers=vca.vcloud_session.get_vcloud_headers(),
2908 verify=vca.verify,
2909 logger=vca.logger)
2910
2911 if response.status_code != requests.codes.ok:
2912 self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
2913 response.status_code))
2914 return None
2915 try:
2916 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
2917 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
2918 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
2919
2920 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
2921 if item.find("rasd:Description",namespaces).text == "Hard disk":
2922 disk_item = item.find("rasd:HostResource" ,namespaces )
2923 if disk_item is not None:
2924 disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
2925 break
2926
2927 data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
2928 xml_declaration=True)
2929
2930 #Send PUT request to modify disk size
2931 headers = vca.vcloud_session.get_vcloud_headers()
2932 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
2933
2934 response = Http.put(url=disk_href,
2935 data=data,
2936 headers=headers,
2937 verify=vca.verify, logger=self.logger)
2938
2939 if response.status_code != 202:
2940 self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
2941 response.status_code))
2942 else:
2943 modify_disk_task = taskType.parseString(response.content, True)
2944 if type(modify_disk_task) is GenericTask:
2945 status = vca.block_until_completed(modify_disk_task)
2946 return status
2947
2948 return None
2949
2950 except Exception as exp :
2951 self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
2952 return None
2953
2954 def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
2955 """
2956 Method to attach pci devices to VM
2957
2958 Args:
2959 vapp_uuid - uuid of vApp/VM
2960 pci_devices - pci devices infromation as specified in VNFD (flavor)
2961
2962 Returns:
2963 The status of add pci device task , vm object and
2964 vcenter_conect object
2965 """
2966 vm_obj = None
2967 vcenter_conect = None
2968 self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
2969 #Assuming password of vCenter user is same as password of vCloud user
2970 vm_moref_id , vm_vcenter_host , vm_vcenter_username, vm_vcenter_port = self.get_vcenter_info_rest(vapp_uuid)
2971 self.logger.info("vm_moref_id, {} vm_vcenter_host {} vm_vcenter_username{} "\
2972 "vm_vcenter_port{}".format(
2973 vm_moref_id, vm_vcenter_host,
2974 vm_vcenter_username, vm_vcenter_port))
2975 if vm_moref_id and vm_vcenter_host and vm_vcenter_username:
2976 context = None
2977 if hasattr(ssl, '_create_unverified_context'):
2978 context = ssl._create_unverified_context()
2979 try:
2980 no_of_pci_devices = len(pci_devices)
2981 if no_of_pci_devices > 0:
2982 vcenter_conect = SmartConnect(host=vm_vcenter_host, user=vm_vcenter_username,
2983 pwd=self.passwd, port=int(vm_vcenter_port) ,
2984 sslContext=context)
2985 atexit.register(Disconnect, vcenter_conect)
2986 content = vcenter_conect.RetrieveContent()
2987
2988 #Get VM and its host
2989 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
2990 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
2991 if host_obj and vm_obj:
2992 #get PCI devies from host on which vapp is currently installed
2993 avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
2994
2995 if avilable_pci_devices is None:
2996 #find other hosts with active pci devices
2997 new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
2998 content,
2999 no_of_pci_devices
3000 )
3001
3002 if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3003 #Migrate vm to the host where PCI devices are availble
3004 self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
3005 task = self.relocate_vm(new_host_obj, vm_obj)
3006 if task is not None:
3007 result = self.wait_for_vcenter_task(task, vcenter_conect)
3008 self.logger.info("Migrate VM status: {}".format(result))
3009 host_obj = new_host_obj
3010 else:
3011 self.logger.info("Fail to migrate VM : {}".format(result))
3012 raise vimconn.vimconnNotFoundException(
3013 "Fail to migrate VM : {} to host {}".format(
3014 vmname_andid,
3015 new_host_obj)
3016 )
3017
3018 if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3019 #Add PCI devices one by one
3020 for pci_device in avilable_pci_devices:
3021 task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
3022 if task:
3023 status= self.wait_for_vcenter_task(task, vcenter_conect)
3024 if status:
3025 self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3026 else:
3027 self.logger.info("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3028 return True, vm_obj, vcenter_conect
3029 else:
3030 self.logger.error("Currently there is no host with"\
3031 " {} number of avaialble PCI devices required for VM {}".format(
3032 no_of_pci_devices,
3033 vmname_andid)
3034 )
3035 raise vimconn.vimconnNotFoundException(
3036 "Currently there is no host with {} "\
3037 "number of avaialble PCI devices required for VM {}".format(
3038 no_of_pci_devices,
3039 vmname_andid))
3040 else:
3041 self.logger.debug("No infromation about PCI devices {} ",pci_devices)
3042
3043 except vmodl.MethodFault as error:
3044 self.logger.error("Error occurred while adding PCI devices {} ",error)
3045 return None, vm_obj, vcenter_conect
3046
3047 def get_vm_obj(self, content, mob_id):
3048 """
3049 Method to get the vsphere VM object associated with a given morf ID
3050 Args:
3051 vapp_uuid - uuid of vApp/VM
3052 content - vCenter content object
3053 mob_id - mob_id of VM
3054
3055 Returns:
3056 VM and host object
3057 """
3058 vm_obj = None
3059 host_obj = None
3060 try :
3061 container = content.viewManager.CreateContainerView(content.rootFolder,
3062 [vim.VirtualMachine], True
3063 )
3064 for vm in container.view:
3065 mobID = vm._GetMoId()
3066 if mobID == mob_id:
3067 vm_obj = vm
3068 host_obj = vm_obj.runtime.host
3069 break
3070 except Exception as exp:
3071 self.logger.error("Error occurred while finding VM object : {}".format(exp))
3072 return host_obj, vm_obj
3073
3074 def get_pci_devices(self, host, need_devices):
3075 """
3076 Method to get the details of pci devices on given host
3077 Args:
3078 host - vSphere host object
3079 need_devices - number of pci devices needed on host
3080
3081 Returns:
3082 array of pci devices
3083 """
3084 all_devices = []
3085 all_device_ids = []
3086 used_devices_ids = []
3087
3088 try:
3089 if host:
3090 pciPassthruInfo = host.config.pciPassthruInfo
3091 pciDevies = host.hardware.pciDevice
3092
3093 for pci_status in pciPassthruInfo:
3094 if pci_status.passthruActive:
3095 for device in pciDevies:
3096 if device.id == pci_status.id:
3097 all_device_ids.append(device.id)
3098 all_devices.append(device)
3099
3100 #check if devices are in use
3101 avalible_devices = all_devices
3102 for vm in host.vm:
3103 if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
3104 vm_devices = vm.config.hardware.device
3105 for device in vm_devices:
3106 if type(device) is vim.vm.device.VirtualPCIPassthrough:
3107 if device.backing.id in all_device_ids:
3108 for use_device in avalible_devices:
3109 if use_device.id == device.backing.id:
3110 avalible_devices.remove(use_device)
3111 used_devices_ids.append(device.backing.id)
3112 self.logger.debug("Device {} from devices {}"\
3113 "is in use".format(device.backing.id,
3114 device)
3115 )
3116 if len(avalible_devices) < need_devices:
3117 self.logger.debug("Host {} don't have {} number of active devices".format(host,
3118 need_devices))
3119 self.logger.debug("found only {} devives {}".format(len(avalible_devices),
3120 avalible_devices))
3121 return None
3122 else:
3123 required_devices = avalible_devices[:need_devices]
3124 self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
3125 len(avalible_devices),
3126 host,
3127 need_devices))
3128 self.logger.info("Retruning {} devices as {}".format(need_devices,
3129 required_devices ))
3130 return required_devices
3131
3132 except Exception as exp:
3133 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
3134
3135 return None
3136
3137 def get_host_and_PCIdevices(self, content, need_devices):
3138 """
3139 Method to get the details of pci devices infromation on all hosts
3140
3141 Args:
3142 content - vSphere host object
3143 need_devices - number of pci devices needed on host
3144
3145 Returns:
3146 array of pci devices and host object
3147 """
3148 host_obj = None
3149 pci_device_objs = None
3150 try:
3151 if content:
3152 container = content.viewManager.CreateContainerView(content.rootFolder,
3153 [vim.HostSystem], True)
3154 for host in container.view:
3155 devices = self.get_pci_devices(host, need_devices)
3156 if devices:
3157 host_obj = host
3158 pci_device_objs = devices
3159 break
3160 except Exception as exp:
3161 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
3162
3163 return host_obj,pci_device_objs
3164
3165 def relocate_vm(self, dest_host, vm) :
3166 """
3167 Method to get the relocate VM to new host
3168
3169 Args:
3170 dest_host - vSphere host object
3171 vm - vSphere VM object
3172
3173 Returns:
3174 task object
3175 """
3176 task = None
3177 try:
3178 relocate_spec = vim.vm.RelocateSpec(host=dest_host)
3179 task = vm.Relocate(relocate_spec)
3180 self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
3181 except Exception as exp:
3182 self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
3183 dest_host, vm, exp))
3184 return task
3185
3186 def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
3187 """
3188 Waits and provides updates on a vSphere task
3189 """
3190 while task.info.state == vim.TaskInfo.State.running:
3191 time.sleep(2)
3192
3193 if task.info.state == vim.TaskInfo.State.success:
3194 if task.info.result is not None and not hideResult:
3195 self.logger.info('{} completed successfully, result: {}'.format(
3196 actionName,
3197 task.info.result))
3198 else:
3199 self.logger.info('Task {} completed successfully.'.format(actionName))
3200 else:
3201 self.logger.error('{} did not complete successfully: {} '.format(
3202 actionName,
3203 task.info.error)
3204 )
3205
3206 return task.info.result
3207
3208 def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
3209 """
3210 Method to add pci device in given VM
3211
3212 Args:
3213 host_object - vSphere host object
3214 vm_object - vSphere VM object
3215 host_pci_dev - host_pci_dev must be one of the devices from the
3216 host_object.hardware.pciDevice list
3217 which is configured as a PCI passthrough device
3218
3219 Returns:
3220 task object
3221 """
3222 task = None
3223 if vm_object and host_object and host_pci_dev:
3224 try :
3225 #Add PCI device to VM
3226 pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
3227 systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
3228
3229 if host_pci_dev.id not in systemid_by_pciid:
3230 self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
3231 return None
3232
3233 deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
3234 backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
3235 id=host_pci_dev.id,
3236 systemId=systemid_by_pciid[host_pci_dev.id],
3237 vendorId=host_pci_dev.vendorId,
3238 deviceName=host_pci_dev.deviceName)
3239
3240 hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
3241
3242 new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
3243 new_device_config.operation = "add"
3244 vmConfigSpec = vim.vm.ConfigSpec()
3245 vmConfigSpec.deviceChange = [new_device_config]
3246
3247 task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
3248 self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
3249 host_pci_dev, vm_object, host_object)
3250 )
3251 except Exception as exp:
3252 self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3253 host_pci_dev,
3254 vm_object,
3255 exp))
3256 return task
3257
3258 def get_vcenter_info_rest(self , vapp_uuid):
3259 """
3260 https://192.169.241.105/api/admin/extension/vimServer/cc82baf9-9f80-4468-bfe9-ce42b3f9dde5
3261 Method to get details of vCenter
3262
3263 Args:
3264 vapp_uuid - uuid of vApp or VM
3265
3266 Returns:
3267 Moref Id of VM and deails of vCenter
3268 """
3269 vm_moref_id = None
3270 vm_vcenter = None
3271 vm_vcenter_username = None
3272 vm_vcenter_port = None
3273
3274 vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
3275 if vm_details and "vm_vcenter_info" in vm_details:
3276 vm_moref_id = vm_details["vm_vcenter_info"]["vm_moref_id"]
3277 vim_server_href = vm_details["vm_vcenter_info"]["vim_server_href"]
3278
3279 if vim_server_href:
3280 vca = self.connect_as_admin()
3281 if not vca:
3282 raise vimconn.vimconnConnectionException("self.connect() is failed")
3283 if vim_server_href is None:
3284 self.logger.error("No url to get vcenter details")
3285
3286 if vca.vcloud_session and vca.vcloud_session.organization:
3287 response = Http.get(url=vim_server_href,
3288 headers=vca.vcloud_session.get_vcloud_headers(),
3289 verify=vca.verify,
3290 logger=vca.logger)
3291
3292 if response.status_code != requests.codes.ok:
3293 self.logger.debug("GET REST API call {} failed. Return status code {}".format(vim_server_href,
3294 response.status_code))
3295 try:
3296 namespaces={"vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3297 "vcloud":"http://www.vmware.com/vcloud/v1.5"
3298 }
3299 xmlroot_respond = XmlElementTree.fromstring(response.content)
3300 vm_vcenter_username = xmlroot_respond.find('vmext:Username', namespaces).text
3301 vcenter_url = xmlroot_respond.find('vmext:Url', namespaces).text
3302 vm_vcenter_port = vcenter_url.split(":")[2]
3303 vm_vcenter = vcenter_url.split(":")[1].split("//")[1]
3304
3305 except Exception as exp :
3306 self.logger.info("Error occurred calling rest api for vcenter information {}".format(exp))
3307
3308 return vm_moref_id , vm_vcenter , vm_vcenter_username, vm_vcenter_port
3309
3310
3311 def get_vm_pci_details(self, vmuuid):
3312 """
3313 Method to get VM PCI device details from vCenter
3314
3315 Args:
3316 vm_obj - vSphere VM object
3317
3318 Returns:
3319 dict of PCI devives attached to VM
3320
3321 """
3322 vm_pci_devices_info = {}
3323 try:
3324 vm_moref_id , vm_vcenter_host , vm_vcenter_username, vm_vcenter_port = self.get_vcenter_info_rest(vmuuid)
3325 if vm_moref_id and vm_vcenter_host and vm_vcenter_username:
3326 context = None
3327 if hasattr(ssl, '_create_unverified_context'):
3328 context = ssl._create_unverified_context()
3329 vcenter_conect = SmartConnect(host=vm_vcenter_host, user=vm_vcenter_username,
3330 pwd=self.passwd, port=int(vm_vcenter_port),
3331 sslContext=context)
3332 atexit.register(Disconnect, vcenter_conect)
3333 content = vcenter_conect.RetrieveContent()
3334
3335 #Get VM and its host
3336 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
3337 for device in vm_obj.config.hardware.device:
3338 if type(device) == vim.vm.device.VirtualPCIPassthrough:
3339 device_details={'devide_id':device.backing.id,
3340 'pciSlotNumber':device.slotInfo.pciSlotNumber
3341 }
3342 vm_pci_devices_info[device.deviceInfo.label] = device_details
3343 except Exception as exp:
3344 self.logger.info("Error occurred while getting PCI devices infromationn"\
3345 " for VM {} : {}".format(vm_obj,exp))
3346 return vm_pci_devices_info
3347
3348