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