Merge "Fix minor bug in openmano client with subcommand vim-image-list" into v1.1
[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_image_list(self, filter_dict={}):
1076 '''Obtain tenant images from VIM
1077 Filter_dict can be:
1078 name: image name
1079 id: image uuid
1080 checksum: image checksum
1081 location: image path
1082 Returns the image list of dictionaries:
1083 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1084 List can be empty
1085 '''
1086 vca = self.connect()
1087 if not vca:
1088 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1089 try:
1090 image_list = []
1091 catalogs = vca.get_catalogs()
1092 if len(catalogs) == 0:
1093 return image_list
1094 else:
1095 for catalog in catalogs:
1096 catalog_uuid = catalog.get_id().split(":")[3]
1097 name = catalog.name
1098 filtered_dict = {}
1099 if filter_dict.get("name") and filter_dict["name"] != name:
1100 continue
1101 if filter_dict.get("id") and filter_dict["id"] != catalog_uuid:
1102 continue
1103 filtered_dict ["name"] = name
1104 filtered_dict ["id"] = catalog_uuid
1105 image_list.append(filtered_dict)
1106
1107 self.logger.debug("List of already created catalog items: {}".format(image_list))
1108 return image_list
1109 except Exception as exp:
1110 raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp))
1111
1112 def get_vappid(self, vdc=None, vapp_name=None):
1113 """ Method takes vdc object and vApp name and returns vapp uuid or None
1114
1115 Args:
1116 vdc: The VDC object.
1117 vapp_name: is application vappp name identifier
1118
1119 Returns:
1120 The return vApp name otherwise None
1121 """
1122 if vdc is None or vapp_name is None:
1123 return None
1124 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1125 try:
1126 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1127 vdc.ResourceEntities.ResourceEntity)
1128 if len(refs) == 1:
1129 return refs[0].href.split("vapp")[1][1:]
1130 except Exception as e:
1131 self.logger.exception(e)
1132 return False
1133 return None
1134
1135 def check_vapp(self, vdc=None, vapp_uuid=None):
1136 """ Method Method returns True or False if vapp deployed in vCloud director
1137
1138 Args:
1139 vca: Connector to VCA
1140 vdc: The VDC object.
1141 vappid: vappid is application identifier
1142
1143 Returns:
1144 The return True if vApp deployed
1145 :param vdc:
1146 :param vapp_uuid:
1147 """
1148 try:
1149 refs = filter(lambda ref:
1150 ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1151 vdc.ResourceEntities.ResourceEntity)
1152 for ref in refs:
1153 vappid = ref.href.split("vapp")[1][1:]
1154 # find vapp with respected vapp uuid
1155 if vappid == vapp_uuid:
1156 return True
1157 except Exception as e:
1158 self.logger.exception(e)
1159 return False
1160 return False
1161
1162 def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None):
1163 """Method returns vApp name from vCD and lookup done by vapp_id.
1164
1165 Args:
1166 vca: Connector to VCA
1167 vdc: The VDC object.
1168 vapp_uuid: vappid is application identifier
1169
1170 Returns:
1171 The return vApp name otherwise None
1172 """
1173
1174 try:
1175 refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1176 vdc.ResourceEntities.ResourceEntity)
1177 for ref in refs:
1178 # we care only about UUID the rest doesn't matter
1179 vappid = ref.href.split("vapp")[1][1:]
1180 if vappid == vapp_uuid:
1181 response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
1182 logger=self.logger)
1183 tree = XmlElementTree.fromstring(response.content)
1184 return tree.attrib['name']
1185 except Exception as e:
1186 self.logger.exception(e)
1187 return None
1188 return None
1189
1190 def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list={},
1191 cloud_config=None, disk_list=None):
1192 """Adds a VM instance to VIM
1193 Params:
1194 start: indicates if VM must start or boot in pause mode. Ignored
1195 image_id,flavor_id: image and flavor uuid
1196 net_list: list of interfaces, each one is a dictionary with:
1197 name:
1198 net_id: network uuid to connect
1199 vpci: virtual vcpi to assign
1200 model: interface model, virtio, e2000, ...
1201 mac_address:
1202 use: 'data', 'bridge', 'mgmt'
1203 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1204 vim_id: filled/added by this function
1205 cloud_config: can be a text script to be passed directly to cloud-init,
1206 or an object to inject users and ssh keys with format:
1207 key-pairs: [] list of keys to install to the default user
1208 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1209 #TODO ip, security groups
1210 Returns >=0, the instance identifier
1211 <0, error_text
1212 """
1213
1214 self.logger.info("Creating new instance for entry {}".format(name))
1215 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1216 format(description, start, image_id, flavor_id, net_list, cloud_config))
1217 vca = self.connect()
1218 if not vca:
1219 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1220
1221 #new vm name = vmname + tenant_id + uuid
1222 new_vm_name = [name, '-', str(uuid.uuid4())]
1223 vmname_andid = ''.join(new_vm_name)
1224
1225 # if vm already deployed we return existing uuid
1226 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1227 # if vapp_uuid is not None:
1228 # return vapp_uuid
1229
1230 # we check for presence of VDC, Catalog entry and Flavor.
1231 vdc = vca.get_vdc(self.tenant_name)
1232 if vdc is None:
1233 raise vimconn.vimconnNotFoundException(
1234 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
1235 catalogs = vca.get_catalogs()
1236 if catalogs is None:
1237 raise vimconn.vimconnNotFoundException(
1238 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
1239
1240 catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1241 if catalog_hash_name:
1242 self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
1243 else:
1244 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1245 "(Failed retrieve catalog information {})".format(name, image_id))
1246
1247
1248 # Set vCPU and Memory based on flavor.
1249 #
1250 vm_cpus = None
1251 vm_memory = None
1252 vm_disk = None
1253 pci_devices_info = []
1254 if flavor_id is not None:
1255 if flavor_id not in flavorlist:
1256 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1257 "Failed retrieve flavor information "
1258 "flavor id {}".format(name, flavor_id))
1259 else:
1260 try:
1261 flavor = flavorlist[flavor_id]
1262 vm_cpus = flavor[FLAVOR_VCPUS_KEY]
1263 vm_memory = flavor[FLAVOR_RAM_KEY]
1264 vm_disk = flavor[FLAVOR_DISK_KEY]
1265 extended = flavor.get("extended", None)
1266 if extended:
1267 numas=extended.get("numas", None)
1268 if numas:
1269 for numa in numas:
1270 for interface in numa.get("interfaces",() ):
1271 if interface["dedicated"].strip()=="yes":
1272 pci_devices_info.append(interface)
1273 except KeyError:
1274 raise vimconn.vimconnException("Corrupted flavor. {}".format(flavor_id))
1275
1276 # image upload creates template name as catalog name space Template.
1277 templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1278 power_on = 'false'
1279 if start:
1280 power_on = 'true'
1281
1282 # client must provide at least one entry in net_list if not we report error
1283 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1284 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1285 primary_net = None
1286 primary_netname = None
1287 network_mode = 'bridged'
1288 if net_list is not None and len(net_list) > 0:
1289 for net in net_list:
1290 if 'use' in net and net['use'] == 'mgmt':
1291 primary_net = net
1292 if primary_net is None:
1293 primary_net = net_list[0]
1294
1295 try:
1296 primary_net_id = primary_net['net_id']
1297 network_dict = self.get_vcd_network(network_uuid=primary_net_id)
1298 if 'name' in network_dict:
1299 primary_netname = network_dict['name']
1300
1301 except KeyError:
1302 raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
1303 else:
1304 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
1305
1306 # use: 'data', 'bridge', 'mgmt'
1307 # create vApp. Set vcpu and ram based on flavor id.
1308 vapptask = vca.create_vapp(self.tenant_name, vmname_andid, templateName,
1309 self.get_catalogbyid(image_id, catalogs),
1310 network_name=None, # None while creating vapp
1311 network_mode=network_mode,
1312 vm_name=vmname_andid,
1313 vm_cpus=vm_cpus, # can be None if flavor is None
1314 vm_memory=vm_memory) # can be None if flavor is None
1315
1316 if vapptask is None or vapptask is False:
1317 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid))
1318 if type(vapptask) is VappTask:
1319 vca.block_until_completed(vapptask)
1320
1321 # we should have now vapp in undeployed state.
1322 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1323 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1324 if vapp is None:
1325 raise vimconn.vimconnUnexpectedResponse(
1326 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
1327 vmname_andid))
1328
1329 #Add PCI passthrough configrations
1330 PCI_devices_status = False
1331 vm_obj = None
1332 si = None
1333 if len(pci_devices_info) > 0:
1334 self.logger.info("Need to add PCI devices {} into VM {}".format(pci_devices_info,
1335 vmname_andid ))
1336 PCI_devices_status, vm_obj, vcenter_conect = self.add_pci_devices(vapp_uuid,
1337 pci_devices_info,
1338 vmname_andid)
1339 if PCI_devices_status:
1340 self.logger.info("Added PCI devives {} to VM {}".format(
1341 pci_devices_info,
1342 vmname_andid)
1343 )
1344 else:
1345 self.logger.info("Fail to add PCI devives {} to VM {}".format(
1346 pci_devices_info,
1347 vmname_andid)
1348 )
1349 # add vm disk
1350 if vm_disk:
1351 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1352 result = self.modify_vm_disk(vapp_uuid, vm_disk)
1353 if result :
1354 self.logger.debug("Modified Disk size of VM {} ".format(vmname_andid))
1355
1356 # add NICs & connect to networks in netlist
1357 try:
1358 self.logger.info("Request to connect VM to a network: {}".format(net_list))
1359 nicIndex = 0
1360 primary_nic_index = 0
1361 for net in net_list:
1362 # openmano uses network id in UUID format.
1363 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1364 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1365 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1366
1367 if 'net_id' not in net:
1368 continue
1369
1370 interface_net_id = net['net_id']
1371 interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
1372 interface_network_mode = net['use']
1373
1374 if interface_network_mode == 'mgmt':
1375 primary_nic_index = nicIndex
1376
1377 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1378 - DHCP (The IP address is obtained from a DHCP service.)
1379 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1380 - NONE (No IP addressing mode specified.)"""
1381
1382 if primary_netname is not None:
1383 nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name))
1384 if len(nets) == 1:
1385 self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name))
1386 task = vapp.connect_to_network(nets[0].name, nets[0].href)
1387 if type(task) is GenericTask:
1388 vca.block_until_completed(task)
1389 # connect network to VM - with all DHCP by default
1390 self.logger.info("new_vminstance(): Connecting VM to a network {}".format(nets[0].name))
1391 task = vapp.connect_vms(nets[0].name,
1392 connection_index=nicIndex,
1393 connections_primary_index=primary_nic_index,
1394 ip_allocation_mode='DHCP')
1395 if type(task) is GenericTask:
1396 vca.block_until_completed(task)
1397 nicIndex += 1
1398 except KeyError:
1399 # it might be a case if specific mandatory entry in dict is empty
1400 self.logger.debug("Key error {}".format(KeyError.message))
1401 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1402
1403 # deploy and power on vm
1404 self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name))
1405 deploytask = vapp.deploy(powerOn=False)
1406 if type(deploytask) is GenericTask:
1407 vca.block_until_completed(deploytask)
1408
1409 # If VM has PCI devices reserve memory for VM
1410 if PCI_devices_status and vm_obj and vcenter_conect:
1411 memReserve = vm_obj.config.hardware.memoryMB
1412 spec = vim.vm.ConfigSpec()
1413 spec.memoryAllocation = vim.ResourceAllocationInfo(reservation=memReserve)
1414 task = vm_obj.ReconfigVM_Task(spec=spec)
1415 if task:
1416 result = self.wait_for_vcenter_task(task, vcenter_conect)
1417 self.logger.info("Reserved memmoery {} MB for "\
1418 "VM VM status: {}".format(str(memReserve),result))
1419 else:
1420 self.logger.info("Fail to reserved memmoery {} to VM {}".format(
1421 str(memReserve),str(vm_obj)))
1422
1423 self.logger.debug("new_vminstance(): power on vApp {} ".format(name))
1424 poweron_task = vapp.poweron()
1425 if type(poweron_task) is GenericTask:
1426 vca.block_until_completed(poweron_task)
1427
1428 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1429 wait_time = 0
1430 vapp_uuid = None
1431 while wait_time <= MAX_WAIT_TIME:
1432 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1433 if vapp and vapp.me.deployed:
1434 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1435 break
1436 else:
1437 self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
1438 time.sleep(INTERVAL_TIME)
1439
1440 wait_time +=INTERVAL_TIME
1441
1442 if vapp_uuid is not None:
1443 return vapp_uuid
1444 else:
1445 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1446
1447 ##
1448 ##
1449 ## based on current discussion
1450 ##
1451 ##
1452 ## server:
1453 # created: '2016-09-08T11:51:58'
1454 # description: simple-instance.linux1.1
1455 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1456 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1457 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1458 # status: ACTIVE
1459 # error_msg:
1460 # interfaces: …
1461 #
1462 def get_vminstance(self, vim_vm_uuid=None):
1463 """Returns the VM instance information from VIM"""
1464
1465 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
1466 vca = self.connect()
1467 if not vca:
1468 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1469
1470 vdc = vca.get_vdc(self.tenant_name)
1471 if vdc is None:
1472 raise vimconn.vimconnConnectionException(
1473 "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1474
1475 vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
1476 if not vm_info_dict:
1477 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1478 raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1479
1480 status_key = vm_info_dict['status']
1481 error = ''
1482 try:
1483 vm_dict = {'created': vm_info_dict['created'],
1484 'description': vm_info_dict['name'],
1485 'status': vcdStatusCode2manoFormat[int(status_key)],
1486 'hostId': vm_info_dict['vmuuid'],
1487 'error_msg': error,
1488 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1489
1490 if 'interfaces' in vm_info_dict:
1491 vm_dict['interfaces'] = vm_info_dict['interfaces']
1492 else:
1493 vm_dict['interfaces'] = []
1494 except KeyError:
1495 vm_dict = {'created': '',
1496 'description': '',
1497 'status': vcdStatusCode2manoFormat[int(-1)],
1498 'hostId': vm_info_dict['vmuuid'],
1499 'error_msg': "Inconsistency state",
1500 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1501
1502 return vm_dict
1503
1504 def delete_vminstance(self, vm__vim_uuid):
1505 """Method poweroff and remove VM instance from vcloud director network.
1506
1507 Args:
1508 vm__vim_uuid: VM UUID
1509
1510 Returns:
1511 Returns the instance identifier
1512 """
1513
1514 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
1515 vca = self.connect()
1516 if not vca:
1517 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1518
1519 vdc = vca.get_vdc(self.tenant_name)
1520 if vdc is None:
1521 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1522 self.tenant_name))
1523 raise vimconn.vimconnException(
1524 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1525
1526 try:
1527 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1528 if vapp_name is None:
1529 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1530 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1531 else:
1532 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1533
1534 # Delete vApp and wait for status change if task executed and vApp is None.
1535 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1536
1537 if vapp:
1538 if vapp.me.deployed:
1539 self.logger.info("Powering off vApp {}".format(vapp_name))
1540 #Power off vApp
1541 powered_off = False
1542 wait_time = 0
1543 while wait_time <= MAX_WAIT_TIME:
1544 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1545 if not vapp:
1546 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1547 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1548
1549 power_off_task = vapp.poweroff()
1550 if type(power_off_task) is GenericTask:
1551 result = vca.block_until_completed(power_off_task)
1552 if result:
1553 powered_off = True
1554 break
1555 else:
1556 self.logger.info("Wait for vApp {} to power off".format(vapp_name))
1557 time.sleep(INTERVAL_TIME)
1558
1559 wait_time +=INTERVAL_TIME
1560 if not powered_off:
1561 self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
1562 else:
1563 self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
1564
1565 #Undeploy vApp
1566 self.logger.info("Undeploy vApp {}".format(vapp_name))
1567 wait_time = 0
1568 undeployed = False
1569 while wait_time <= MAX_WAIT_TIME:
1570 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1571 if not vapp:
1572 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1573 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1574 undeploy_task = vapp.undeploy(action='powerOff')
1575
1576 if type(undeploy_task) is GenericTask:
1577 result = vca.block_until_completed(undeploy_task)
1578 if result:
1579 undeployed = True
1580 break
1581 else:
1582 self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
1583 time.sleep(INTERVAL_TIME)
1584
1585 wait_time +=INTERVAL_TIME
1586
1587 if not undeployed:
1588 self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
1589
1590 # delete vapp
1591 self.logger.info("Start deletion of vApp {} ".format(vapp_name))
1592 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1593
1594 if vapp is not None:
1595 wait_time = 0
1596 result = False
1597
1598 while wait_time <= MAX_WAIT_TIME:
1599 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1600 if not vapp:
1601 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1602 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1603
1604 delete_task = vapp.delete()
1605
1606 if type(delete_task) is GenericTask:
1607 vca.block_until_completed(delete_task)
1608 result = vca.block_until_completed(delete_task)
1609 if result:
1610 break
1611 else:
1612 self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
1613 time.sleep(INTERVAL_TIME)
1614
1615 wait_time +=INTERVAL_TIME
1616
1617 if not result:
1618 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
1619
1620 except:
1621 self.logger.debug(traceback.format_exc())
1622 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1623
1624 if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
1625 self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
1626 return vm__vim_uuid
1627 else:
1628 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1629
1630 def refresh_vms_status(self, vm_list):
1631 """Get the status of the virtual machines and their interfaces/ports
1632 Params: the list of VM identifiers
1633 Returns a dictionary with:
1634 vm_id: #VIM id of this Virtual Machine
1635 status: #Mandatory. Text with one of:
1636 # DELETED (not found at vim)
1637 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1638 # OTHER (Vim reported other status not understood)
1639 # ERROR (VIM indicates an ERROR status)
1640 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1641 # CREATING (on building process), ERROR
1642 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1643 #
1644 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1645 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1646 interfaces:
1647 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1648 mac_address: #Text format XX:XX:XX:XX:XX:XX
1649 vim_net_id: #network id where this interface is connected
1650 vim_interface_id: #interface/port VIM id
1651 ip_address: #null, or text with IPv4, IPv6 address
1652 """
1653
1654 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
1655 vca = self.connect()
1656 if not vca:
1657 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1658
1659 vdc = vca.get_vdc(self.tenant_name)
1660 if vdc is None:
1661 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1662
1663 vms_dict = {}
1664 for vmuuid in vm_list:
1665 vmname = self.get_namebyvappid(vca, vdc, vmuuid)
1666 if vmname is not None:
1667
1668 the_vapp = vca.get_vapp(vdc, vmname)
1669 vm_info = the_vapp.get_vms_details()
1670 vm_status = vm_info[0]['status']
1671 vm_pci_details = self.get_vm_pci_details(vmuuid)
1672 vm_info[0].update(vm_pci_details)
1673
1674 vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1675 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1676 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
1677
1678 # get networks
1679 try:
1680 vm_app_networks = the_vapp.get_vms_network_info()
1681 for vapp_network in vm_app_networks:
1682 for vm_network in vapp_network:
1683 if vm_network['name'] == vmname:
1684 interface = {"mac_address": vm_network['mac'],
1685 "vim_net_id": self.get_network_id_by_name(vm_network['network_name']),
1686 "vim_interface_id": self.get_network_id_by_name(vm_network['network_name']),
1687 'ip_address': vm_network['ip']}
1688 # interface['vim_info'] = yaml.safe_dump(vm_network)
1689 vm_dict["interfaces"].append(interface)
1690 # add a vm to vm dict
1691 vms_dict.setdefault(vmuuid, vm_dict)
1692 except KeyError:
1693 self.logger.debug("Error in respond {}".format(KeyError.message))
1694 self.logger.debug(traceback.format_exc())
1695
1696 return vms_dict
1697
1698 def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
1699 """Send and action over a VM instance from VIM
1700 Returns the vm_id if the action was successfully sent to the VIM"""
1701
1702 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
1703 if vm__vim_uuid is None or action_dict is None:
1704 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
1705
1706 vca = self.connect()
1707 if not vca:
1708 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1709
1710 vdc = vca.get_vdc(self.tenant_name)
1711 if vdc is None:
1712 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
1713
1714 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1715 if vapp_name is None:
1716 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1717 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1718 else:
1719 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1720
1721 try:
1722 the_vapp = vca.get_vapp(vdc, vapp_name)
1723 # TODO fix all status
1724 if "start" in action_dict:
1725 if action_dict["start"] == "rebuild":
1726 the_vapp.deploy(powerOn=True)
1727 else:
1728 vm_info = the_vapp.get_vms_details()
1729 vm_status = vm_info[0]['status']
1730 if vm_status == "Suspended":
1731 the_vapp.poweron()
1732 elif vm_status.status == "Powered off":
1733 the_vapp.poweron()
1734 elif "pause" in action_dict:
1735 pass
1736 ## server.pause()
1737 elif "resume" in action_dict:
1738 pass
1739 ## server.resume()
1740 elif "shutoff" in action_dict or "shutdown" in action_dict:
1741 the_vapp.shutdown()
1742 elif "forceOff" in action_dict:
1743 the_vapp.reset()
1744 elif "terminate" in action_dict:
1745 the_vapp.delete()
1746 # elif "createImage" in action_dict:
1747 # server.create_image()
1748 else:
1749 pass
1750 except:
1751 pass
1752
1753 def get_vminstance_console(self, vm_id, console_type="vnc"):
1754 """
1755 Get a console for the virtual machine
1756 Params:
1757 vm_id: uuid of the VM
1758 console_type, can be:
1759 "novnc" (by default), "xvpvnc" for VNC types,
1760 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1761 Returns dict with the console parameters:
1762 protocol: ssh, ftp, http, https, ...
1763 server: usually ip address
1764 port: the http, ssh, ... port
1765 suffix: extra text, e.g. the http path and query string
1766 """
1767 raise vimconn.vimconnNotImplemented("Should have implemented this")
1768
1769 # NOT USED METHODS in current version
1770
1771 def host_vim2gui(self, host, server_dict):
1772 """Transform host dictionary from VIM format to GUI format,
1773 and append to the server_dict
1774 """
1775 raise vimconn.vimconnNotImplemented("Should have implemented this")
1776
1777 def get_hosts_info(self):
1778 """Get the information of deployed hosts
1779 Returns the hosts content"""
1780 raise vimconn.vimconnNotImplemented("Should have implemented this")
1781
1782 def get_hosts(self, vim_tenant):
1783 """Get the hosts and deployed instances
1784 Returns the hosts content"""
1785 raise vimconn.vimconnNotImplemented("Should have implemented this")
1786
1787 def get_processor_rankings(self):
1788 """Get the processor rankings in the VIM database"""
1789 raise vimconn.vimconnNotImplemented("Should have implemented this")
1790
1791 def new_host(self, host_data):
1792 """Adds a new host to VIM"""
1793 '''Returns status code of the VIM response'''
1794 raise vimconn.vimconnNotImplemented("Should have implemented this")
1795
1796 def new_external_port(self, port_data):
1797 """Adds a external port to VIM"""
1798 '''Returns the port identifier'''
1799 raise vimconn.vimconnNotImplemented("Should have implemented this")
1800
1801 def new_external_network(self, net_name, net_type):
1802 """Adds a external network to VIM (shared)"""
1803 '''Returns the network identifier'''
1804 raise vimconn.vimconnNotImplemented("Should have implemented this")
1805
1806 def connect_port_network(self, port_id, network_id, admin=False):
1807 """Connects a external port to a network"""
1808 '''Returns status code of the VIM response'''
1809 raise vimconn.vimconnNotImplemented("Should have implemented this")
1810
1811 def new_vminstancefromJSON(self, vm_data):
1812 """Adds a VM instance to VIM"""
1813 '''Returns the instance identifier'''
1814 raise vimconn.vimconnNotImplemented("Should have implemented this")
1815
1816 def get_network_name_by_id(self, network_uuid=None):
1817 """Method gets vcloud director network named based on supplied uuid.
1818
1819 Args:
1820 network_uuid: network_id
1821
1822 Returns:
1823 The return network name.
1824 """
1825
1826 vca = self.connect()
1827 if not vca:
1828 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1829
1830 if not network_uuid:
1831 return None
1832
1833 try:
1834 org_dict = self.get_org(self.org_uuid)
1835 if 'networks' in org_dict:
1836 org_network_dict = org_dict['networks']
1837 for net_uuid in org_network_dict:
1838 if net_uuid == network_uuid:
1839 return org_network_dict[net_uuid]
1840 except:
1841 self.logger.debug("Exception in get_network_name_by_id")
1842 self.logger.debug(traceback.format_exc())
1843
1844 return None
1845
1846 def get_network_id_by_name(self, network_name=None):
1847 """Method gets vcloud director network uuid based on supplied name.
1848
1849 Args:
1850 network_name: network_name
1851 Returns:
1852 The return network uuid.
1853 network_uuid: network_id
1854 """
1855
1856 vca = self.connect()
1857 if not vca:
1858 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1859
1860 if not network_name:
1861 self.logger.debug("get_network_id_by_name() : Network name is empty")
1862 return None
1863
1864 try:
1865 org_dict = self.get_org(self.org_uuid)
1866 if org_dict and 'networks' in org_dict:
1867 org_network_dict = org_dict['networks']
1868 for net_uuid,net_name in org_network_dict.iteritems():
1869 if net_name == network_name:
1870 return net_uuid
1871
1872 except KeyError as exp:
1873 self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
1874
1875 return None
1876
1877 def list_org_action(self):
1878 """
1879 Method leverages vCloud director and query for available organization for particular user
1880
1881 Args:
1882 vca - is active VCA connection.
1883 vdc_name - is a vdc name that will be used to query vms action
1884
1885 Returns:
1886 The return XML respond
1887 """
1888
1889 vca = self.connect()
1890 if not vca:
1891 raise vimconn.vimconnConnectionException("self.connect() is failed")
1892
1893 url_list = [vca.host, '/api/org']
1894 vm_list_rest_call = ''.join(url_list)
1895
1896 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1897 response = Http.get(url=vm_list_rest_call,
1898 headers=vca.vcloud_session.get_vcloud_headers(),
1899 verify=vca.verify,
1900 logger=vca.logger)
1901 if response.status_code == requests.codes.ok:
1902 return response.content
1903
1904 return None
1905
1906 def get_org_action(self, org_uuid=None):
1907 """
1908 Method leverages vCloud director and retrieve available object fdr organization.
1909
1910 Args:
1911 vca - is active VCA connection.
1912 vdc_name - is a vdc name that will be used to query vms action
1913
1914 Returns:
1915 The return XML respond
1916 """
1917
1918 vca = self.connect()
1919 if not vca:
1920 raise vimconn.vimconnConnectionException("self.connect() is failed")
1921
1922 if org_uuid is None:
1923 return None
1924
1925 url_list = [vca.host, '/api/org/', org_uuid]
1926 vm_list_rest_call = ''.join(url_list)
1927
1928 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1929 response = Http.get(url=vm_list_rest_call,
1930 headers=vca.vcloud_session.get_vcloud_headers(),
1931 verify=vca.verify,
1932 logger=vca.logger)
1933 if response.status_code == requests.codes.ok:
1934 return response.content
1935
1936 return None
1937
1938 def get_org(self, org_uuid=None):
1939 """
1940 Method retrieves available organization in vCloud Director
1941
1942 Args:
1943 org_uuid - is a organization uuid.
1944
1945 Returns:
1946 The return dictionary with following key
1947 "network" - for network list under the org
1948 "catalogs" - for network list under the org
1949 "vdcs" - for vdc list under org
1950 """
1951
1952 org_dict = {}
1953 vca = self.connect()
1954 if not vca:
1955 raise vimconn.vimconnConnectionException("self.connect() is failed")
1956
1957 if org_uuid is None:
1958 return org_dict
1959
1960 content = self.get_org_action(org_uuid=org_uuid)
1961 try:
1962 vdc_list = {}
1963 network_list = {}
1964 catalog_list = {}
1965 vm_list_xmlroot = XmlElementTree.fromstring(content)
1966 for child in vm_list_xmlroot:
1967 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1968 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1969 org_dict['vdcs'] = vdc_list
1970 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1971 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1972 org_dict['networks'] = network_list
1973 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1974 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1975 org_dict['catalogs'] = catalog_list
1976 except:
1977 pass
1978
1979 return org_dict
1980
1981 def get_org_list(self):
1982 """
1983 Method retrieves available organization in vCloud Director
1984
1985 Args:
1986 vca - is active VCA connection.
1987
1988 Returns:
1989 The return dictionary and key for each entry VDC UUID
1990 """
1991
1992 org_dict = {}
1993 vca = self.connect()
1994 if not vca:
1995 raise vimconn.vimconnConnectionException("self.connect() is failed")
1996
1997 content = self.list_org_action()
1998 try:
1999 vm_list_xmlroot = XmlElementTree.fromstring(content)
2000 for vm_xml in vm_list_xmlroot:
2001 if vm_xml.tag.split("}")[1] == 'Org':
2002 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
2003 org_dict[org_uuid[0]] = vm_xml.attrib['name']
2004 except:
2005 pass
2006
2007 return org_dict
2008
2009 def vms_view_action(self, vdc_name=None):
2010 """ Method leverages vCloud director vms query call
2011
2012 Args:
2013 vca - is active VCA connection.
2014 vdc_name - is a vdc name that will be used to query vms action
2015
2016 Returns:
2017 The return XML respond
2018 """
2019 vca = self.connect()
2020 if vdc_name is None:
2021 return None
2022
2023 url_list = [vca.host, '/api/vms/query']
2024 vm_list_rest_call = ''.join(url_list)
2025
2026 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2027 refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
2028 vca.vcloud_session.organization.Link)
2029 if len(refs) == 1:
2030 response = Http.get(url=vm_list_rest_call,
2031 headers=vca.vcloud_session.get_vcloud_headers(),
2032 verify=vca.verify,
2033 logger=vca.logger)
2034 if response.status_code == requests.codes.ok:
2035 return response.content
2036
2037 return None
2038
2039 def get_vapp_list(self, vdc_name=None):
2040 """
2041 Method retrieves vApp list deployed vCloud director and returns a dictionary
2042 contains a list of all vapp deployed for queried VDC.
2043 The key for a dictionary is vApp UUID
2044
2045
2046 Args:
2047 vca - is active VCA connection.
2048 vdc_name - is a vdc name that will be used to query vms action
2049
2050 Returns:
2051 The return dictionary and key for each entry vapp UUID
2052 """
2053
2054 vapp_dict = {}
2055 if vdc_name is None:
2056 return vapp_dict
2057
2058 content = self.vms_view_action(vdc_name=vdc_name)
2059 try:
2060 vm_list_xmlroot = XmlElementTree.fromstring(content)
2061 for vm_xml in vm_list_xmlroot:
2062 if vm_xml.tag.split("}")[1] == 'VMRecord':
2063 if vm_xml.attrib['isVAppTemplate'] == 'true':
2064 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
2065 if 'vappTemplate-' in rawuuid[0]:
2066 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2067 # vm and use raw UUID as key
2068 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
2069 except:
2070 pass
2071
2072 return vapp_dict
2073
2074 def get_vm_list(self, vdc_name=None):
2075 """
2076 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2077 contains a list of all VM's deployed for queried VDC.
2078 The key for a dictionary is VM UUID
2079
2080
2081 Args:
2082 vca - is active VCA connection.
2083 vdc_name - is a vdc name that will be used to query vms action
2084
2085 Returns:
2086 The return dictionary and key for each entry vapp UUID
2087 """
2088 vm_dict = {}
2089
2090 if vdc_name is None:
2091 return vm_dict
2092
2093 content = self.vms_view_action(vdc_name=vdc_name)
2094 try:
2095 vm_list_xmlroot = XmlElementTree.fromstring(content)
2096 for vm_xml in vm_list_xmlroot:
2097 if vm_xml.tag.split("}")[1] == 'VMRecord':
2098 if vm_xml.attrib['isVAppTemplate'] == 'false':
2099 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2100 if 'vm-' in rawuuid[0]:
2101 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2102 # vm and use raw UUID as key
2103 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2104 except:
2105 pass
2106
2107 return vm_dict
2108
2109 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
2110 """
2111 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2112 contains a list of all VM's deployed for queried VDC.
2113 The key for a dictionary is VM UUID
2114
2115
2116 Args:
2117 vca - is active VCA connection.
2118 vdc_name - is a vdc name that will be used to query vms action
2119
2120 Returns:
2121 The return dictionary and key for each entry vapp UUID
2122 """
2123 vm_dict = {}
2124 vca = self.connect()
2125 if not vca:
2126 raise vimconn.vimconnConnectionException("self.connect() is failed")
2127
2128 if vdc_name is None:
2129 return vm_dict
2130
2131 content = self.vms_view_action(vdc_name=vdc_name)
2132 try:
2133 vm_list_xmlroot = XmlElementTree.fromstring(content)
2134 for vm_xml in vm_list_xmlroot:
2135 if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
2136 # lookup done by UUID
2137 if isuuid:
2138 if vapp_name in vm_xml.attrib['container']:
2139 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2140 if 'vm-' in rawuuid[0]:
2141 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2142 break
2143 # lookup done by Name
2144 else:
2145 if vapp_name in vm_xml.attrib['name']:
2146 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2147 if 'vm-' in rawuuid[0]:
2148 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2149 break
2150 except:
2151 pass
2152
2153 return vm_dict
2154
2155 def get_network_action(self, network_uuid=None):
2156 """
2157 Method leverages vCloud director and query network based on network uuid
2158
2159 Args:
2160 vca - is active VCA connection.
2161 network_uuid - is a network uuid
2162
2163 Returns:
2164 The return XML respond
2165 """
2166
2167 vca = self.connect()
2168 if not vca:
2169 raise vimconn.vimconnConnectionException("self.connect() is failed")
2170
2171 if network_uuid is None:
2172 return None
2173
2174 url_list = [vca.host, '/api/network/', network_uuid]
2175 vm_list_rest_call = ''.join(url_list)
2176
2177 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2178 response = Http.get(url=vm_list_rest_call,
2179 headers=vca.vcloud_session.get_vcloud_headers(),
2180 verify=vca.verify,
2181 logger=vca.logger)
2182 if response.status_code == requests.codes.ok:
2183 return response.content
2184
2185 return None
2186
2187 def get_vcd_network(self, network_uuid=None):
2188 """
2189 Method retrieves available network from vCloud Director
2190
2191 Args:
2192 network_uuid - is VCD network UUID
2193
2194 Each element serialized as key : value pair
2195
2196 Following keys available for access. network_configuration['Gateway'}
2197 <Configuration>
2198 <IpScopes>
2199 <IpScope>
2200 <IsInherited>true</IsInherited>
2201 <Gateway>172.16.252.100</Gateway>
2202 <Netmask>255.255.255.0</Netmask>
2203 <Dns1>172.16.254.201</Dns1>
2204 <Dns2>172.16.254.202</Dns2>
2205 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2206 <IsEnabled>true</IsEnabled>
2207 <IpRanges>
2208 <IpRange>
2209 <StartAddress>172.16.252.1</StartAddress>
2210 <EndAddress>172.16.252.99</EndAddress>
2211 </IpRange>
2212 </IpRanges>
2213 </IpScope>
2214 </IpScopes>
2215 <FenceMode>bridged</FenceMode>
2216
2217 Returns:
2218 The return dictionary and key for each entry vapp UUID
2219 """
2220
2221 network_configuration = {}
2222 if network_uuid is None:
2223 return network_uuid
2224
2225 content = self.get_network_action(network_uuid=network_uuid)
2226 try:
2227 vm_list_xmlroot = XmlElementTree.fromstring(content)
2228
2229 network_configuration['status'] = vm_list_xmlroot.get("status")
2230 network_configuration['name'] = vm_list_xmlroot.get("name")
2231 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
2232
2233 for child in vm_list_xmlroot:
2234 if child.tag.split("}")[1] == 'IsShared':
2235 network_configuration['isShared'] = child.text.strip()
2236 if child.tag.split("}")[1] == 'Configuration':
2237 for configuration in child.iter():
2238 tagKey = configuration.tag.split("}")[1].strip()
2239 if tagKey != "":
2240 network_configuration[tagKey] = configuration.text.strip()
2241 return network_configuration
2242 except:
2243 pass
2244
2245 return network_configuration
2246
2247 def delete_network_action(self, network_uuid=None):
2248 """
2249 Method delete given network from vCloud director
2250
2251 Args:
2252 network_uuid - is a network uuid that client wish to delete
2253
2254 Returns:
2255 The return None or XML respond or false
2256 """
2257
2258 vca = self.connect_as_admin()
2259 if not vca:
2260 raise vimconn.vimconnConnectionException("self.connect() is failed")
2261 if network_uuid is None:
2262 return False
2263
2264 url_list = [vca.host, '/api/admin/network/', network_uuid]
2265 vm_list_rest_call = ''.join(url_list)
2266
2267 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2268 response = Http.delete(url=vm_list_rest_call,
2269 headers=vca.vcloud_session.get_vcloud_headers(),
2270 verify=vca.verify,
2271 logger=vca.logger)
2272
2273 if response.status_code == 202:
2274 return True
2275
2276 return False
2277
2278 def create_network(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 new_network_name = [network_name, '-', str(uuid.uuid4())]
2296 content = self.create_network_rest(network_name=''.join(new_network_name),
2297 ip_profile=ip_profile,
2298 net_type=net_type,
2299 parent_network_uuid=parent_network_uuid,
2300 isshared=isshared)
2301 if content is None:
2302 self.logger.debug("Failed create network {}.".format(network_name))
2303 return None
2304
2305 try:
2306 vm_list_xmlroot = XmlElementTree.fromstring(content)
2307 vcd_uuid = vm_list_xmlroot.get('id').split(":")
2308 if len(vcd_uuid) == 4:
2309 self.logger.info("Create new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
2310 return vcd_uuid[3]
2311 except:
2312 self.logger.debug("Failed create network {}".format(network_name))
2313 return None
2314
2315 def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2316 ip_profile=None, isshared='true'):
2317 """
2318 Method create network in vCloud director
2319
2320 Args:
2321 network_name - is network name to be created.
2322 net_type - can be 'bridge','data','ptp','mgmt'.
2323 ip_profile is a dict containing the IP parameters of the network
2324 isshared - is a boolean
2325 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2326 It optional attribute. by default if no parent network indicate the first available will be used.
2327
2328 Returns:
2329 The return network uuid or return None
2330 """
2331
2332 vca = self.connect_as_admin()
2333 if not vca:
2334 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2335 if network_name is None:
2336 return None
2337
2338 url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
2339 vm_list_rest_call = ''.join(url_list)
2340 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2341 response = Http.get(url=vm_list_rest_call,
2342 headers=vca.vcloud_session.get_vcloud_headers(),
2343 verify=vca.verify,
2344 logger=vca.logger)
2345
2346 provider_network = None
2347 available_networks = None
2348 add_vdc_rest_url = None
2349
2350 if response.status_code != requests.codes.ok:
2351 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2352 response.status_code))
2353 return None
2354 else:
2355 try:
2356 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2357 for child in vm_list_xmlroot:
2358 if child.tag.split("}")[1] == 'ProviderVdcReference':
2359 provider_network = child.attrib.get('href')
2360 # application/vnd.vmware.admin.providervdc+xml
2361 if child.tag.split("}")[1] == 'Link':
2362 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2363 and child.attrib.get('rel') == 'add':
2364 add_vdc_rest_url = child.attrib.get('href')
2365 except:
2366 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2367 self.logger.debug("Respond body {}".format(response.content))
2368 return None
2369
2370 # find pvdc provided available network
2371 response = Http.get(url=provider_network,
2372 headers=vca.vcloud_session.get_vcloud_headers(),
2373 verify=vca.verify,
2374 logger=vca.logger)
2375 if response.status_code != requests.codes.ok:
2376 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2377 response.status_code))
2378 return None
2379
2380 # available_networks.split("/")[-1]
2381
2382 if parent_network_uuid is None:
2383 try:
2384 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2385 for child in vm_list_xmlroot.iter():
2386 if child.tag.split("}")[1] == 'AvailableNetworks':
2387 for networks in child.iter():
2388 # application/vnd.vmware.admin.network+xml
2389 if networks.attrib.get('href') is not None:
2390 available_networks = networks.attrib.get('href')
2391 break
2392 except:
2393 return None
2394
2395 #Configure IP profile of the network
2396 ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
2397
2398 gateway_address=ip_profile['gateway_address']
2399 dhcp_count=int(ip_profile['dhcp_count'])
2400 subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
2401
2402 if ip_profile['dhcp_enabled']==True:
2403 dhcp_enabled='true'
2404 else:
2405 dhcp_enabled='false'
2406 dhcp_start_address=ip_profile['dhcp_start_address']
2407
2408 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2409 end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
2410 end_ip_int += dhcp_count - 1
2411 dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
2412
2413 ip_version=ip_profile['ip_version']
2414 dns_address=ip_profile['dns_address']
2415
2416 # either use client provided UUID or search for a first available
2417 # if both are not defined we return none
2418 if parent_network_uuid is not None:
2419 url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
2420 add_vdc_rest_url = ''.join(url_list)
2421
2422 if net_type=='ptp':
2423 fence_mode="isolated"
2424 isshared='false'
2425 is_inherited='false'
2426 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2427 <Description>Openmano created</Description>
2428 <Configuration>
2429 <IpScopes>
2430 <IpScope>
2431 <IsInherited>{1:s}</IsInherited>
2432 <Gateway>{2:s}</Gateway>
2433 <Netmask>{3:s}</Netmask>
2434 <Dns1>{4:s}</Dns1>
2435 <IsEnabled>{5:s}</IsEnabled>
2436 <IpRanges>
2437 <IpRange>
2438 <StartAddress>{6:s}</StartAddress>
2439 <EndAddress>{7:s}</EndAddress>
2440 </IpRange>
2441 </IpRanges>
2442 </IpScope>
2443 </IpScopes>
2444 <FenceMode>{8:s}</FenceMode>
2445 </Configuration>
2446 <IsShared>{9:s}</IsShared>
2447 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2448 subnet_address, dns_address, dhcp_enabled,
2449 dhcp_start_address, dhcp_end_address, fence_mode, isshared)
2450
2451 else:
2452 fence_mode="bridged"
2453 is_inherited='false'
2454 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2455 <Description>Openmano created</Description>
2456 <Configuration>
2457 <IpScopes>
2458 <IpScope>
2459 <IsInherited>{1:s}</IsInherited>
2460 <Gateway>{2:s}</Gateway>
2461 <Netmask>{3:s}</Netmask>
2462 <Dns1>{4:s}</Dns1>
2463 <IsEnabled>{5:s}</IsEnabled>
2464 <IpRanges>
2465 <IpRange>
2466 <StartAddress>{6:s}</StartAddress>
2467 <EndAddress>{7:s}</EndAddress>
2468 </IpRange>
2469 </IpRanges>
2470 </IpScope>
2471 </IpScopes>
2472 <ParentNetwork href="{8:s}"/>
2473 <FenceMode>{9:s}</FenceMode>
2474 </Configuration>
2475 <IsShared>{10:s}</IsShared>
2476 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2477 subnet_address, dns_address, dhcp_enabled,
2478 dhcp_start_address, dhcp_end_address, available_networks,
2479 fence_mode, isshared)
2480
2481 headers = vca.vcloud_session.get_vcloud_headers()
2482 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2483 try:
2484 response = Http.post(url=add_vdc_rest_url,
2485 headers=headers,
2486 data=data,
2487 verify=vca.verify,
2488 logger=vca.logger)
2489
2490 if response.status_code != 201:
2491 self.logger.debug("Create Network POST REST API call failed. Return status code {}"
2492 .format(response.status_code))
2493 else:
2494 network = networkType.parseString(response.content, True)
2495 create_nw_task = network.get_Tasks().get_Task()[0]
2496
2497 # if we all ok we respond with content after network creation completes
2498 # otherwise by default return None
2499 if create_nw_task is not None:
2500 self.logger.debug("Create Network REST : Waiting for Nw creation complete")
2501 status = vca.block_until_completed(create_nw_task)
2502 if status:
2503 return response.content
2504 else:
2505 self.logger.debug("create_network_rest task failed. Network Create response : {}"
2506 .format(response.content))
2507 except Exception as exp:
2508 self.logger.debug("create_network_rest : Exception : {} ".format(exp))
2509
2510 return None
2511
2512 def convert_cidr_to_netmask(self, cidr_ip=None):
2513 """
2514 Method sets convert CIDR netmask address to normal IP format
2515 Args:
2516 cidr_ip : CIDR IP address
2517 Returns:
2518 netmask : Converted netmask
2519 """
2520 if cidr_ip is not None:
2521 if '/' in cidr_ip:
2522 network, net_bits = cidr_ip.split('/')
2523 netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
2524 else:
2525 netmask = cidr_ip
2526 return netmask
2527 return None
2528
2529 def get_provider_rest(self, vca=None):
2530 """
2531 Method gets provider vdc view from vcloud director
2532
2533 Args:
2534 network_name - is network name to be created.
2535 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2536 It optional attribute. by default if no parent network indicate the first available will be used.
2537
2538 Returns:
2539 The return xml content of respond or None
2540 """
2541
2542 url_list = [vca.host, '/api/admin']
2543 response = Http.get(url=''.join(url_list),
2544 headers=vca.vcloud_session.get_vcloud_headers(),
2545 verify=vca.verify,
2546 logger=vca.logger)
2547
2548 if response.status_code == requests.codes.ok:
2549 return response.content
2550 return None
2551
2552 def create_vdc(self, vdc_name=None):
2553
2554 vdc_dict = {}
2555
2556 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
2557 if xml_content is not None:
2558 try:
2559 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
2560 for child in task_resp_xmlroot:
2561 if child.tag.split("}")[1] == 'Owner':
2562 vdc_id = child.attrib.get('href').split("/")[-1]
2563 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
2564 return vdc_dict
2565 except:
2566 self.logger.debug("Respond body {}".format(xml_content))
2567
2568 return None
2569
2570 def create_vdc_from_tmpl_rest(self, vdc_name=None):
2571 """
2572 Method create vdc in vCloud director based on VDC template.
2573 it uses pre-defined template that must be named openmano
2574
2575 Args:
2576 vdc_name - name of a new vdc.
2577
2578 Returns:
2579 The return xml content of respond or None
2580 """
2581
2582 self.logger.info("Creating new vdc {}".format(vdc_name))
2583 vca = self.connect()
2584 if not vca:
2585 raise vimconn.vimconnConnectionException("self.connect() is failed")
2586 if vdc_name is None:
2587 return None
2588
2589 url_list = [vca.host, '/api/vdcTemplates']
2590 vm_list_rest_call = ''.join(url_list)
2591 response = Http.get(url=vm_list_rest_call,
2592 headers=vca.vcloud_session.get_vcloud_headers(),
2593 verify=vca.verify,
2594 logger=vca.logger)
2595
2596 # container url to a template
2597 vdc_template_ref = None
2598 try:
2599 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2600 for child in vm_list_xmlroot:
2601 # application/vnd.vmware.admin.providervdc+xml
2602 # we need find a template from witch we instantiate VDC
2603 if child.tag.split("}")[1] == 'VdcTemplate':
2604 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
2605 'name') == 'openmano':
2606 vdc_template_ref = child.attrib.get('href')
2607 except:
2608 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2609 self.logger.debug("Respond body {}".format(response.content))
2610 return None
2611
2612 # if we didn't found required pre defined template we return None
2613 if vdc_template_ref is None:
2614 return None
2615
2616 try:
2617 # instantiate vdc
2618 url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
2619 vm_list_rest_call = ''.join(url_list)
2620 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2621 <Source href="{1:s}"></Source>
2622 <Description>opnemano</Description>
2623 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
2624 headers = vca.vcloud_session.get_vcloud_headers()
2625 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2626 response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
2627 logger=vca.logger)
2628 # if we all ok we respond with content otherwise by default None
2629 if response.status_code >= 200 and response.status_code < 300:
2630 return response.content
2631 return None
2632 except:
2633 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2634 self.logger.debug("Respond body {}".format(response.content))
2635
2636 return None
2637
2638 def create_vdc_rest(self, vdc_name=None):
2639 """
2640 Method create network in vCloud director
2641
2642 Args:
2643 network_name - is network name to be created.
2644 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2645 It optional attribute. by default if no parent network indicate the first available will be used.
2646
2647 Returns:
2648 The return network uuid or return None
2649 """
2650
2651 self.logger.info("Creating new vdc {}".format(vdc_name))
2652
2653 vca = self.connect_as_admin()
2654 if not vca:
2655 raise vimconn.vimconnConnectionException("self.connect() is failed")
2656 if vdc_name is None:
2657 return None
2658
2659 url_list = [vca.host, '/api/admin/org/', self.org_uuid]
2660 vm_list_rest_call = ''.join(url_list)
2661 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2662 response = Http.get(url=vm_list_rest_call,
2663 headers=vca.vcloud_session.get_vcloud_headers(),
2664 verify=vca.verify,
2665 logger=vca.logger)
2666
2667 provider_vdc_ref = None
2668 add_vdc_rest_url = None
2669 available_networks = None
2670
2671 if response.status_code != requests.codes.ok:
2672 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2673 response.status_code))
2674 return None
2675 else:
2676 try:
2677 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2678 for child in vm_list_xmlroot:
2679 # application/vnd.vmware.admin.providervdc+xml
2680 if child.tag.split("}")[1] == 'Link':
2681 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2682 and child.attrib.get('rel') == 'add':
2683 add_vdc_rest_url = child.attrib.get('href')
2684 except:
2685 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2686 self.logger.debug("Respond body {}".format(response.content))
2687 return None
2688
2689 response = self.get_provider_rest(vca=vca)
2690 try:
2691 vm_list_xmlroot = XmlElementTree.fromstring(response)
2692 for child in vm_list_xmlroot:
2693 if child.tag.split("}")[1] == 'ProviderVdcReferences':
2694 for sub_child in child:
2695 provider_vdc_ref = sub_child.attrib.get('href')
2696 except:
2697 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2698 self.logger.debug("Respond body {}".format(response))
2699 return None
2700
2701 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
2702 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2703 <AllocationModel>ReservationPool</AllocationModel>
2704 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2705 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2706 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2707 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2708 <ProviderVdcReference
2709 name="Main Provider"
2710 href="{2:s}" />
2711 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
2712 escape(vdc_name),
2713 provider_vdc_ref)
2714
2715 headers = vca.vcloud_session.get_vcloud_headers()
2716 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2717 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
2718 logger=vca.logger)
2719
2720 # if we all ok we respond with content otherwise by default None
2721 if response.status_code == 201:
2722 return response.content
2723 return None
2724
2725 def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
2726 """
2727 Method retrieve vapp detail from vCloud director
2728
2729 Args:
2730 vapp_uuid - is vapp identifier.
2731
2732 Returns:
2733 The return network uuid or return None
2734 """
2735
2736 parsed_respond = {}
2737 vca = None
2738
2739 if need_admin_access:
2740 vca = self.connect_as_admin()
2741 else:
2742 vca = self.connect()
2743
2744 if not vca:
2745 raise vimconn.vimconnConnectionException("self.connect() is failed")
2746 if vapp_uuid is None:
2747 return None
2748
2749 url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
2750 get_vapp_restcall = ''.join(url_list)
2751
2752 if vca.vcloud_session and vca.vcloud_session.organization:
2753 response = Http.get(url=get_vapp_restcall,
2754 headers=vca.vcloud_session.get_vcloud_headers(),
2755 verify=vca.verify,
2756 logger=vca.logger)
2757
2758 if response.status_code != requests.codes.ok:
2759 self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
2760 response.status_code))
2761 return parsed_respond
2762
2763 try:
2764 xmlroot_respond = XmlElementTree.fromstring(response.content)
2765 parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
2766
2767 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2768 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2769 'vmw': 'http://www.vmware.com/schema/ovf',
2770 'vm': 'http://www.vmware.com/vcloud/v1.5',
2771 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2772 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2773 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2774 }
2775
2776 created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
2777 if created_section is not None:
2778 parsed_respond['created'] = created_section.text
2779
2780 network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
2781 if network_section is not None and 'networkName' in network_section.attrib:
2782 parsed_respond['networkname'] = network_section.attrib['networkName']
2783
2784 ipscopes_section = \
2785 xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2786 namespaces)
2787 if ipscopes_section is not None:
2788 for ipscope in ipscopes_section:
2789 for scope in ipscope:
2790 tag_key = scope.tag.split("}")[1]
2791 if tag_key == 'IpRanges':
2792 ip_ranges = scope.getchildren()
2793 for ipblock in ip_ranges:
2794 for block in ipblock:
2795 parsed_respond[block.tag.split("}")[1]] = block.text
2796 else:
2797 parsed_respond[tag_key] = scope.text
2798
2799 # parse children section for other attrib
2800 children_section = xmlroot_respond.find('vm:Children/', namespaces)
2801 if children_section is not None:
2802 parsed_respond['name'] = children_section.attrib['name']
2803 parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
2804 if "nestedHypervisorEnabled" in children_section.attrib else None
2805 parsed_respond['deployed'] = children_section.attrib['deployed']
2806 parsed_respond['status'] = children_section.attrib['status']
2807 parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
2808 network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
2809 nic_list = []
2810 for adapters in network_adapter:
2811 adapter_key = adapters.tag.split("}")[1]
2812 if adapter_key == 'PrimaryNetworkConnectionIndex':
2813 parsed_respond['primarynetwork'] = adapters.text
2814 if adapter_key == 'NetworkConnection':
2815 vnic = {}
2816 if 'network' in adapters.attrib:
2817 vnic['network'] = adapters.attrib['network']
2818 for adapter in adapters:
2819 setting_key = adapter.tag.split("}")[1]
2820 vnic[setting_key] = adapter.text
2821 nic_list.append(vnic)
2822
2823 for link in children_section:
2824 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
2825 if link.attrib['rel'] == 'screen:acquireTicket':
2826 parsed_respond['acquireTicket'] = link.attrib
2827 if link.attrib['rel'] == 'screen:acquireMksTicket':
2828 parsed_respond['acquireMksTicket'] = link.attrib
2829
2830 parsed_respond['interfaces'] = nic_list
2831 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
2832 if vCloud_extension_section is not None:
2833 vm_vcenter_info = {}
2834 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
2835 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
2836 if vmext is not None:
2837 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
2838 vm_vcenter_info["vim_server_href"] = vmext.find('vmext:VimServerRef', namespaces).attrib['href']
2839 parsed_respond["vm_vcenter_info"]= vm_vcenter_info
2840
2841 virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
2842 vm_virtual_hardware_info = {}
2843 if virtual_hardware_section is not None:
2844 for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
2845 if item.find("rasd:Description",namespaces).text == "Hard disk":
2846 disk_size = item.find("rasd:HostResource" ,namespaces
2847 ).attrib["{"+namespaces['vm']+"}capacity"]
2848
2849 vm_virtual_hardware_info["disk_size"]= disk_size
2850 break
2851
2852 for link in virtual_hardware_section:
2853 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
2854 if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
2855 vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
2856 break
2857
2858 parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
2859 except Exception as exp :
2860 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
2861 return parsed_respond
2862
2863 def acuire_console(self, vm_uuid=None):
2864
2865 vca = self.connect()
2866 if not vca:
2867 raise vimconn.vimconnConnectionException("self.connect() is failed")
2868 if vm_uuid is None:
2869 return None
2870
2871 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2872 vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
2873 console_dict = vm_dict['acquireTicket']
2874 console_rest_call = console_dict['href']
2875
2876 response = Http.post(url=console_rest_call,
2877 headers=vca.vcloud_session.get_vcloud_headers(),
2878 verify=vca.verify,
2879 logger=vca.logger)
2880
2881 if response.status_code == requests.codes.ok:
2882 return response.content
2883
2884 return None
2885
2886 def modify_vm_disk(self, vapp_uuid, flavor_disk):
2887 """
2888 Method retrieve vm disk details
2889
2890 Args:
2891 vapp_uuid - is vapp identifier.
2892 flavor_disk - disk size as specified in VNFD (flavor)
2893
2894 Returns:
2895 The return network uuid or return None
2896 """
2897 status = None
2898 try:
2899 #Flavor disk is in GB convert it into MB
2900 flavor_disk = int(flavor_disk) * 1024
2901 vm_details = self.get_vapp_details_rest(vapp_uuid)
2902 if vm_details:
2903 vm_name = vm_details["name"]
2904 self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
2905
2906 if vm_details and "vm_virtual_hardware" in vm_details:
2907 vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
2908 disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
2909
2910 self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
2911
2912 if flavor_disk > vm_disk:
2913 status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
2914 self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
2915 vm_disk, flavor_disk ))
2916 else:
2917 status = True
2918 self.logger.info("No need to modify disk of VM {}".format(vm_name))
2919
2920 return status
2921 except Exception as exp:
2922 self.logger.info("Error occurred while modifing disk size {}".format(exp))
2923
2924
2925 def modify_vm_disk_rest(self, disk_href , disk_size):
2926 """
2927 Method retrieve modify vm disk size
2928
2929 Args:
2930 disk_href - vCD API URL to GET and PUT disk data
2931 disk_size - disk size as specified in VNFD (flavor)
2932
2933 Returns:
2934 The return network uuid or return None
2935 """
2936 vca = self.connect()
2937 if not vca:
2938 raise vimconn.vimconnConnectionException("self.connect() is failed")
2939 if disk_href is None or disk_size is None:
2940 return None
2941
2942 if vca.vcloud_session and vca.vcloud_session.organization:
2943 response = Http.get(url=disk_href,
2944 headers=vca.vcloud_session.get_vcloud_headers(),
2945 verify=vca.verify,
2946 logger=vca.logger)
2947
2948 if response.status_code != requests.codes.ok:
2949 self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
2950 response.status_code))
2951 return None
2952 try:
2953 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
2954 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
2955 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
2956
2957 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
2958 if item.find("rasd:Description",namespaces).text == "Hard disk":
2959 disk_item = item.find("rasd:HostResource" ,namespaces )
2960 if disk_item is not None:
2961 disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
2962 break
2963
2964 data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
2965 xml_declaration=True)
2966
2967 #Send PUT request to modify disk size
2968 headers = vca.vcloud_session.get_vcloud_headers()
2969 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
2970
2971 response = Http.put(url=disk_href,
2972 data=data,
2973 headers=headers,
2974 verify=vca.verify, logger=self.logger)
2975
2976 if response.status_code != 202:
2977 self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
2978 response.status_code))
2979 else:
2980 modify_disk_task = taskType.parseString(response.content, True)
2981 if type(modify_disk_task) is GenericTask:
2982 status = vca.block_until_completed(modify_disk_task)
2983 return status
2984
2985 return None
2986
2987 except Exception as exp :
2988 self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
2989 return None
2990
2991 def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
2992 """
2993 Method to attach pci devices to VM
2994
2995 Args:
2996 vapp_uuid - uuid of vApp/VM
2997 pci_devices - pci devices infromation as specified in VNFD (flavor)
2998
2999 Returns:
3000 The status of add pci device task , vm object and
3001 vcenter_conect object
3002 """
3003 vm_obj = None
3004 vcenter_conect = None
3005 self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
3006 #Assuming password of vCenter user is same as password of vCloud user
3007 vm_moref_id , vm_vcenter_host , vm_vcenter_username, vm_vcenter_port = self.get_vcenter_info_rest(vapp_uuid)
3008 self.logger.info("vm_moref_id, {} vm_vcenter_host {} vm_vcenter_username{} "\
3009 "vm_vcenter_port{}".format(
3010 vm_moref_id, vm_vcenter_host,
3011 vm_vcenter_username, vm_vcenter_port))
3012 if vm_moref_id and vm_vcenter_host and vm_vcenter_username:
3013 context = None
3014 if hasattr(ssl, '_create_unverified_context'):
3015 context = ssl._create_unverified_context()
3016 try:
3017 no_of_pci_devices = len(pci_devices)
3018 if no_of_pci_devices > 0:
3019 vcenter_conect = SmartConnect(host=vm_vcenter_host, user=vm_vcenter_username,
3020 pwd=self.passwd, port=int(vm_vcenter_port) ,
3021 sslContext=context)
3022 atexit.register(Disconnect, vcenter_conect)
3023 content = vcenter_conect.RetrieveContent()
3024
3025 #Get VM and its host
3026 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
3027 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
3028 if host_obj and vm_obj:
3029 #get PCI devies from host on which vapp is currently installed
3030 avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
3031
3032 if avilable_pci_devices is None:
3033 #find other hosts with active pci devices
3034 new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
3035 content,
3036 no_of_pci_devices
3037 )
3038
3039 if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3040 #Migrate vm to the host where PCI devices are availble
3041 self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
3042 task = self.relocate_vm(new_host_obj, vm_obj)
3043 if task is not None:
3044 result = self.wait_for_vcenter_task(task, vcenter_conect)
3045 self.logger.info("Migrate VM status: {}".format(result))
3046 host_obj = new_host_obj
3047 else:
3048 self.logger.info("Fail to migrate VM : {}".format(result))
3049 raise vimconn.vimconnNotFoundException(
3050 "Fail to migrate VM : {} to host {}".format(
3051 vmname_andid,
3052 new_host_obj)
3053 )
3054
3055 if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3056 #Add PCI devices one by one
3057 for pci_device in avilable_pci_devices:
3058 task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
3059 if task:
3060 status= self.wait_for_vcenter_task(task, vcenter_conect)
3061 if status:
3062 self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3063 else:
3064 self.logger.info("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3065 return True, vm_obj, vcenter_conect
3066 else:
3067 self.logger.error("Currently there is no host with"\
3068 " {} number of avaialble PCI devices required for VM {}".format(
3069 no_of_pci_devices,
3070 vmname_andid)
3071 )
3072 raise vimconn.vimconnNotFoundException(
3073 "Currently there is no host with {} "\
3074 "number of avaialble PCI devices required for VM {}".format(
3075 no_of_pci_devices,
3076 vmname_andid))
3077 else:
3078 self.logger.debug("No infromation about PCI devices {} ",pci_devices)
3079
3080 except vmodl.MethodFault as error:
3081 self.logger.error("Error occurred while adding PCI devices {} ",error)
3082 return None, vm_obj, vcenter_conect
3083
3084 def get_vm_obj(self, content, mob_id):
3085 """
3086 Method to get the vsphere VM object associated with a given morf ID
3087 Args:
3088 vapp_uuid - uuid of vApp/VM
3089 content - vCenter content object
3090 mob_id - mob_id of VM
3091
3092 Returns:
3093 VM and host object
3094 """
3095 vm_obj = None
3096 host_obj = None
3097 try :
3098 container = content.viewManager.CreateContainerView(content.rootFolder,
3099 [vim.VirtualMachine], True
3100 )
3101 for vm in container.view:
3102 mobID = vm._GetMoId()
3103 if mobID == mob_id:
3104 vm_obj = vm
3105 host_obj = vm_obj.runtime.host
3106 break
3107 except Exception as exp:
3108 self.logger.error("Error occurred while finding VM object : {}".format(exp))
3109 return host_obj, vm_obj
3110
3111 def get_pci_devices(self, host, need_devices):
3112 """
3113 Method to get the details of pci devices on given host
3114 Args:
3115 host - vSphere host object
3116 need_devices - number of pci devices needed on host
3117
3118 Returns:
3119 array of pci devices
3120 """
3121 all_devices = []
3122 all_device_ids = []
3123 used_devices_ids = []
3124
3125 try:
3126 if host:
3127 pciPassthruInfo = host.config.pciPassthruInfo
3128 pciDevies = host.hardware.pciDevice
3129
3130 for pci_status in pciPassthruInfo:
3131 if pci_status.passthruActive:
3132 for device in pciDevies:
3133 if device.id == pci_status.id:
3134 all_device_ids.append(device.id)
3135 all_devices.append(device)
3136
3137 #check if devices are in use
3138 avalible_devices = all_devices
3139 for vm in host.vm:
3140 if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
3141 vm_devices = vm.config.hardware.device
3142 for device in vm_devices:
3143 if type(device) is vim.vm.device.VirtualPCIPassthrough:
3144 if device.backing.id in all_device_ids:
3145 for use_device in avalible_devices:
3146 if use_device.id == device.backing.id:
3147 avalible_devices.remove(use_device)
3148 used_devices_ids.append(device.backing.id)
3149 self.logger.debug("Device {} from devices {}"\
3150 "is in use".format(device.backing.id,
3151 device)
3152 )
3153 if len(avalible_devices) < need_devices:
3154 self.logger.debug("Host {} don't have {} number of active devices".format(host,
3155 need_devices))
3156 self.logger.debug("found only {} devives {}".format(len(avalible_devices),
3157 avalible_devices))
3158 return None
3159 else:
3160 required_devices = avalible_devices[:need_devices]
3161 self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
3162 len(avalible_devices),
3163 host,
3164 need_devices))
3165 self.logger.info("Retruning {} devices as {}".format(need_devices,
3166 required_devices ))
3167 return required_devices
3168
3169 except Exception as exp:
3170 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
3171
3172 return None
3173
3174 def get_host_and_PCIdevices(self, content, need_devices):
3175 """
3176 Method to get the details of pci devices infromation on all hosts
3177
3178 Args:
3179 content - vSphere host object
3180 need_devices - number of pci devices needed on host
3181
3182 Returns:
3183 array of pci devices and host object
3184 """
3185 host_obj = None
3186 pci_device_objs = None
3187 try:
3188 if content:
3189 container = content.viewManager.CreateContainerView(content.rootFolder,
3190 [vim.HostSystem], True)
3191 for host in container.view:
3192 devices = self.get_pci_devices(host, need_devices)
3193 if devices:
3194 host_obj = host
3195 pci_device_objs = devices
3196 break
3197 except Exception as exp:
3198 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
3199
3200 return host_obj,pci_device_objs
3201
3202 def relocate_vm(self, dest_host, vm) :
3203 """
3204 Method to get the relocate VM to new host
3205
3206 Args:
3207 dest_host - vSphere host object
3208 vm - vSphere VM object
3209
3210 Returns:
3211 task object
3212 """
3213 task = None
3214 try:
3215 relocate_spec = vim.vm.RelocateSpec(host=dest_host)
3216 task = vm.Relocate(relocate_spec)
3217 self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
3218 except Exception as exp:
3219 self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
3220 dest_host, vm, exp))
3221 return task
3222
3223 def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
3224 """
3225 Waits and provides updates on a vSphere task
3226 """
3227 while task.info.state == vim.TaskInfo.State.running:
3228 time.sleep(2)
3229
3230 if task.info.state == vim.TaskInfo.State.success:
3231 if task.info.result is not None and not hideResult:
3232 self.logger.info('{} completed successfully, result: {}'.format(
3233 actionName,
3234 task.info.result))
3235 else:
3236 self.logger.info('Task {} completed successfully.'.format(actionName))
3237 else:
3238 self.logger.error('{} did not complete successfully: {} '.format(
3239 actionName,
3240 task.info.error)
3241 )
3242
3243 return task.info.result
3244
3245 def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
3246 """
3247 Method to add pci device in given VM
3248
3249 Args:
3250 host_object - vSphere host object
3251 vm_object - vSphere VM object
3252 host_pci_dev - host_pci_dev must be one of the devices from the
3253 host_object.hardware.pciDevice list
3254 which is configured as a PCI passthrough device
3255
3256 Returns:
3257 task object
3258 """
3259 task = None
3260 if vm_object and host_object and host_pci_dev:
3261 try :
3262 #Add PCI device to VM
3263 pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
3264 systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
3265
3266 if host_pci_dev.id not in systemid_by_pciid:
3267 self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
3268 return None
3269
3270 deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
3271 backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
3272 id=host_pci_dev.id,
3273 systemId=systemid_by_pciid[host_pci_dev.id],
3274 vendorId=host_pci_dev.vendorId,
3275 deviceName=host_pci_dev.deviceName)
3276
3277 hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
3278
3279 new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
3280 new_device_config.operation = "add"
3281 vmConfigSpec = vim.vm.ConfigSpec()
3282 vmConfigSpec.deviceChange = [new_device_config]
3283
3284 task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
3285 self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
3286 host_pci_dev, vm_object, host_object)
3287 )
3288 except Exception as exp:
3289 self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3290 host_pci_dev,
3291 vm_object,
3292 exp))
3293 return task
3294
3295 def get_vcenter_info_rest(self , vapp_uuid):
3296 """
3297 https://192.169.241.105/api/admin/extension/vimServer/cc82baf9-9f80-4468-bfe9-ce42b3f9dde5
3298 Method to get details of vCenter
3299
3300 Args:
3301 vapp_uuid - uuid of vApp or VM
3302
3303 Returns:
3304 Moref Id of VM and deails of vCenter
3305 """
3306 vm_moref_id = None
3307 vm_vcenter = None
3308 vm_vcenter_username = None
3309 vm_vcenter_port = None
3310
3311 vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
3312 if vm_details and "vm_vcenter_info" in vm_details:
3313 vm_moref_id = vm_details["vm_vcenter_info"]["vm_moref_id"]
3314 vim_server_href = vm_details["vm_vcenter_info"]["vim_server_href"]
3315
3316 if vim_server_href:
3317 vca = self.connect_as_admin()
3318 if not vca:
3319 raise vimconn.vimconnConnectionException("self.connect() is failed")
3320 if vim_server_href is None:
3321 self.logger.error("No url to get vcenter details")
3322
3323 if vca.vcloud_session and vca.vcloud_session.organization:
3324 response = Http.get(url=vim_server_href,
3325 headers=vca.vcloud_session.get_vcloud_headers(),
3326 verify=vca.verify,
3327 logger=vca.logger)
3328
3329 if response.status_code != requests.codes.ok:
3330 self.logger.debug("GET REST API call {} failed. Return status code {}".format(vim_server_href,
3331 response.status_code))
3332 try:
3333 namespaces={"vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3334 "vcloud":"http://www.vmware.com/vcloud/v1.5"
3335 }
3336 xmlroot_respond = XmlElementTree.fromstring(response.content)
3337 vm_vcenter_username = xmlroot_respond.find('vmext:Username', namespaces).text
3338 vcenter_url = xmlroot_respond.find('vmext:Url', namespaces).text
3339 vm_vcenter_port = vcenter_url.split(":")[2]
3340 vm_vcenter = vcenter_url.split(":")[1].split("//")[1]
3341
3342 except Exception as exp :
3343 self.logger.info("Error occurred calling rest api for vcenter information {}".format(exp))
3344
3345 return vm_moref_id , vm_vcenter , vm_vcenter_username, vm_vcenter_port
3346
3347
3348 def get_vm_pci_details(self, vmuuid):
3349 """
3350 Method to get VM PCI device details from vCenter
3351
3352 Args:
3353 vm_obj - vSphere VM object
3354
3355 Returns:
3356 dict of PCI devives attached to VM
3357
3358 """
3359 vm_pci_devices_info = {}
3360 try:
3361 vm_moref_id , vm_vcenter_host , vm_vcenter_username, vm_vcenter_port = self.get_vcenter_info_rest(vmuuid)
3362 if vm_moref_id and vm_vcenter_host and vm_vcenter_username:
3363 context = None
3364 if hasattr(ssl, '_create_unverified_context'):
3365 context = ssl._create_unverified_context()
3366 vcenter_conect = SmartConnect(host=vm_vcenter_host, user=vm_vcenter_username,
3367 pwd=self.passwd, port=int(vm_vcenter_port),
3368 sslContext=context)
3369 atexit.register(Disconnect, vcenter_conect)
3370 content = vcenter_conect.RetrieveContent()
3371
3372 #Get VM and its host
3373 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
3374 for device in vm_obj.config.hardware.device:
3375 if type(device) == vim.vm.device.VirtualPCIPassthrough:
3376 device_details={'devide_id':device.backing.id,
3377 'pciSlotNumber':device.slotInfo.pciSlotNumber
3378 }
3379 vm_pci_devices_info[device.deviceInfo.label] = device_details
3380 except Exception as exp:
3381 self.logger.info("Error occurred while getting PCI devices infromationn"\
3382 " for VM {} : {}".format(vm_obj,exp))
3383 return vm_pci_devices_info
3384
3385