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