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