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