cd5a47bc8fad4f09c916050b936fd87cec40da02
[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
36 from xml.etree import ElementTree as XmlElementTree
37
38 import yaml
39 from pyvcloud import Http
40 from pyvcloud.vcloudair import VCA
41 from pyvcloud.schema.vcd.v1_5.schemas.vcloud import sessionType, organizationType, \
42 vAppType, organizationListType, vdcType, catalogType, queryRecordViewType, \
43 networkType, vcloudType, taskType, diskType, vmsType, vdcTemplateListType, mediaType
44 from xml.sax.saxutils import escape
45
46 from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TaskType
47 from pyvcloud.schema.vcd.v1_5.schemas.vcloud.taskType import TaskType as GenericTask
48 from pyvcloud.schema.vcd.v1_5.schemas.vcloud.vAppType import TaskType as VappTask
49 from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TasksInProgressType
50
51 import logging
52 import json
53 import time
54 import uuid
55 import httplib
56 import hashlib
57 import socket
58 import struct
59 import netaddr
60
61 # global variable for vcd connector type
62 STANDALONE = 'standalone'
63
64 # key for flavor dicts
65 FLAVOR_RAM_KEY = 'ram'
66 FLAVOR_VCPUS_KEY = 'vcpus'
67
68 DEFAULT_IP_PROFILE = {'gateway_address':"192.168.1.1",
69 'dhcp_count':50,
70 'subnet_address':"192.168.1.0/24",
71 'dhcp_enabled':True,
72 'dhcp_start_address':"192.168.1.3",
73 'ip_version':"IPv4",
74 'dns_address':"192.168.1.2"
75 }
76 # global variable for wait time
77 INTERVAL_TIME = 5
78 MAX_WAIT_TIME = 1800
79
80 VCAVERSION = '5.9'
81
82 __author__ = "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
83 __date__ = "$23-Dec-2016 11:09:29$"
84 __version__ = '0.1'
85
86 # -1: "Could not be created",
87 # 0: "Unresolved",
88 # 1: "Resolved",
89 # 2: "Deployed",
90 # 3: "Suspended",
91 # 4: "Powered on",
92 # 5: "Waiting for user input",
93 # 6: "Unknown state",
94 # 7: "Unrecognized state",
95 # 8: "Powered off",
96 # 9: "Inconsistent state",
97 # 10: "Children do not all have the same status",
98 # 11: "Upload initiated, OVF descriptor pending",
99 # 12: "Upload initiated, copying contents",
100 # 13: "Upload initiated , disk contents pending",
101 # 14: "Upload has been quarantined",
102 # 15: "Upload quarantine period has expired"
103
104 # mapping vCD status to MANO
105 vcdStatusCode2manoFormat = {4: 'ACTIVE',
106 7: 'PAUSED',
107 3: 'SUSPENDED',
108 8: 'INACTIVE',
109 12: 'BUILD',
110 -1: 'ERROR',
111 14: 'DELETED'}
112
113 #
114 netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
115 'ERROR': 'ERROR', 'DELETED': 'DELETED'
116 }
117
118 # dict used to store flavor in memory
119 flavorlist = {}
120
121
122 class vimconnector(vimconn.vimconnector):
123 def __init__(self, uuid=None, name=None, tenant_id=None, tenant_name=None,
124 url=None, url_admin=None, user=None, passwd=None, log_level=None, config={}):
125 """
126 Constructor create vmware connector to vCloud director.
127
128 By default construct doesn't validate connection state. So client can create object with None arguments.
129 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
130
131 a) It initialize organization UUID
132 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
133
134 Args:
135 uuid - is organization uuid.
136 name - is organization name that must be presented in vCloud director.
137 tenant_id - is VDC uuid it must be presented in vCloud director
138 tenant_name - is VDC name.
139 url - is hostname or ip address of vCloud director
140 url_admin - same as above.
141 user - is user that administrator for organization. Caller must make sure that
142 username has right privileges.
143
144 password - is password for a user.
145
146 VMware connector also requires PVDC administrative privileges and separate account.
147 This variables must be passed via config argument dict contains keys
148
149 dict['admin_username']
150 dict['admin_password']
151
152 Returns:
153 Nothing.
154 """
155
156 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url,
157 url_admin, user, passwd, log_level, config)
158
159 self.logger = logging.getLogger('openmano.vim.vmware')
160 self.logger.setLevel(10)
161
162 self.name = name
163 self.id = uuid
164 self.url = url
165 self.url_admin = url_admin
166 self.tenant_id = tenant_id
167 self.tenant_name = tenant_name
168 self.user = user
169 self.passwd = passwd
170 self.config = config
171 self.admin_password = None
172 self.admin_user = None
173 self.org_name = ""
174
175 if tenant_name is not None:
176 orgnameandtenant = tenant_name.split(":")
177 if len(orgnameandtenant) == 2:
178 self.tenant_name = orgnameandtenant[1]
179 self.org_name = orgnameandtenant[0]
180 else:
181 self.tenant_name = tenant_name
182 elif "orgname" in config:
183 self.org_name = config['orgname']
184
185 if log_level:
186 self.logger.setLevel(getattr(logging, log_level))
187
188 try:
189 self.admin_user = config['admin_username']
190 self.admin_password = config['admin_password']
191 except KeyError:
192 raise vimconn.vimconnException(message="Error admin username or admin password is empty.")
193
194 self.org_uuid = None
195 self.vca = None
196
197 if not url:
198 raise vimconn.vimconnException('url param can not be NoneType')
199
200 if not self.url_admin: # try to use normal url
201 self.url_admin = self.url
202
203 logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.org_name,
204 self.tenant_id, self.tenant_name))
205 logging.debug("vcd url {} vcd username: {} vcd password: {}".format(self.url, self.user, self.passwd))
206 logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password))
207
208 # initialize organization
209 if self.user is not None and self.passwd is not None and self.url:
210 self.init_organization()
211
212 def __getitem__(self, index):
213 if index == 'name':
214 return self.name
215 if index == 'tenant_id':
216 return self.tenant_id
217 if index == 'tenant_name':
218 return self.tenant_name
219 elif index == 'id':
220 return self.id
221 elif index == 'org_name':
222 return self.org_name
223 elif index == 'org_uuid':
224 return self.org_uuid
225 elif index == 'user':
226 return self.user
227 elif index == 'passwd':
228 return self.passwd
229 elif index == 'url':
230 return self.url
231 elif index == 'url_admin':
232 return self.url_admin
233 elif index == "config":
234 return self.config
235 else:
236 raise KeyError("Invalid key '%s'" % str(index))
237
238 def __setitem__(self, index, value):
239 if index == 'name':
240 self.name = value
241 if index == 'tenant_id':
242 self.tenant_id = value
243 if index == 'tenant_name':
244 self.tenant_name = value
245 elif index == 'id':
246 self.id = value
247 elif index == 'org_name':
248 self.org_name = value
249 elif index == 'org_uuid':
250 self.org_uuid = value
251 elif index == 'user':
252 self.user = value
253 elif index == 'passwd':
254 self.passwd = value
255 elif index == 'url':
256 self.url = value
257 elif index == 'url_admin':
258 self.url_admin = value
259 else:
260 raise KeyError("Invalid key '%s'" % str(index))
261
262 def connect_as_admin(self):
263 """ Method connect as pvdc admin user to vCloud director.
264 There are certain action that can be done only by provider vdc admin user.
265 Organization creation / provider network creation etc.
266
267 Returns:
268 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
269 """
270
271 self.logger.debug("Logging in to a vca {} as admin.".format(self.org_name))
272
273 vca_admin = VCA(host=self.url,
274 username=self.admin_user,
275 service_type=STANDALONE,
276 version=VCAVERSION,
277 verify=False,
278 log=False)
279 result = vca_admin.login(password=self.admin_password, org='System')
280 if not result:
281 raise vimconn.vimconnConnectionException(
282 "Can't connect to a vCloud director as: {}".format(self.admin_user))
283 result = vca_admin.login(token=vca_admin.token, org='System', org_url=vca_admin.vcloud_session.org_url)
284 if result is True:
285 self.logger.info(
286 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self.admin_user))
287
288 return vca_admin
289
290 def connect(self):
291 """ Method connect as normal user to vCloud director.
292
293 Returns:
294 The return vca object that letter can be used to connect to vCloud director as admin for VDC
295 """
296
297 try:
298 self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.org_name,
299 self.user,
300 self.org_name))
301 vca = VCA(host=self.url,
302 username=self.user,
303 service_type=STANDALONE,
304 version=VCAVERSION,
305 verify=False,
306 log=False)
307
308 result = vca.login(password=self.passwd, org=self.org_name)
309 if not result:
310 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user))
311 result = vca.login(token=vca.token, org=self.org_name, org_url=vca.vcloud_session.org_url)
312 if result is True:
313 self.logger.info(
314 "Successfully logged to a vcloud direct org: {} as user: {}".format(self.org_name, self.user))
315
316 except:
317 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
318 "{} as user: {}".format(self.org_name, self.user))
319
320 return vca
321
322 def init_organization(self):
323 """ Method initialize organization UUID and VDC parameters.
324
325 At bare minimum client must provide organization name that present in vCloud director and VDC.
326
327 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
328 The Org - UUID will be initialized at the run time if data center present in vCloud director.
329
330 Returns:
331 The return vca object that letter can be used to connect to vcloud direct as admin
332 """
333 try:
334 if self.org_uuid is None:
335 org_dict = self.get_org_list()
336 for org in org_dict:
337 # we set org UUID at the init phase but we can do it only when we have valid credential.
338 if org_dict[org] == self.org_name:
339 self.org_uuid = org
340 self.logger.debug("Setting organization UUID {}".format(self.org_uuid))
341 break
342 else:
343 raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name))
344
345 # if well good we require for org details
346 org_details_dict = self.get_org(org_uuid=self.org_uuid)
347
348 # we have two case if we want to initialize VDC ID or VDC name at run time
349 # tenant_name provided but no tenant id
350 if self.tenant_id is None and self.tenant_name is not None and 'vdcs' in org_details_dict:
351 vdcs_dict = org_details_dict['vdcs']
352 for vdc in vdcs_dict:
353 if vdcs_dict[vdc] == self.tenant_name:
354 self.tenant_id = vdc
355 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
356 self.org_name))
357 break
358 else:
359 raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.")
360 # case two we have tenant_id but we don't have tenant name so we find and set it.
361 if self.tenant_id is not None and self.tenant_name is None and 'vdcs' in org_details_dict:
362 vdcs_dict = org_details_dict['vdcs']
363 for vdc in vdcs_dict:
364 if vdc == self.tenant_id:
365 self.tenant_name = vdcs_dict[vdc]
366 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
367 self.org_name))
368 break
369 else:
370 raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director")
371 self.logger.debug("Setting organization uuid {}".format(self.org_uuid))
372 except:
373 self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name))
374 self.logger.debug(traceback.format_exc())
375 self.org_uuid = None
376
377 def new_tenant(self, tenant_name=None, tenant_description=None):
378 """ Method adds a new tenant to VIM with this name.
379 This action requires access to create VDC action in vCloud director.
380
381 Args:
382 tenant_name is tenant_name to be created.
383 tenant_description not used for this call
384
385 Return:
386 returns the tenant identifier in UUID format.
387 If action is failed method will throw vimconn.vimconnException method
388 """
389 vdc_task = self.create_vdc(vdc_name=tenant_name)
390 if vdc_task is not None:
391 vdc_uuid, value = vdc_task.popitem()
392 self.logger.info("Crated new vdc {} and uuid: {}".format(tenant_name, vdc_uuid))
393 return vdc_uuid
394 else:
395 raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name))
396
397 def delete_tenant(self, tenant_id=None):
398 """Delete a tenant from VIM"""
399 'Returns the tenant identifier'
400 raise vimconn.vimconnNotImplemented("Should have implemented this")
401
402 def get_tenant_list(self, filter_dict={}):
403 """Obtain tenants of VIM
404 filter_dict can contain the following keys:
405 name: filter by tenant name
406 id: filter by tenant uuid/id
407 <other VIM specific>
408 Returns the tenant list of dictionaries:
409 [{'name':'<name>, 'id':'<id>, ...}, ...]
410
411 """
412 org_dict = self.get_org(self.org_uuid)
413 vdcs_dict = org_dict['vdcs']
414
415 vdclist = []
416 try:
417 for k in vdcs_dict:
418 entry = {'name': vdcs_dict[k], 'id': k}
419 # if caller didn't specify dictionary we return all tenants.
420 if filter_dict is not None and filter_dict:
421 filtered_entry = entry.copy()
422 filtered_dict = set(entry.keys()) - set(filter_dict)
423 for unwanted_key in filtered_dict: del entry[unwanted_key]
424 if filter_dict == entry:
425 vdclist.append(filtered_entry)
426 else:
427 vdclist.append(entry)
428 except:
429 self.logger.debug("Error in get_tenant_list()")
430 self.logger.debug(traceback.format_exc())
431 raise vimconn.vimconnException("Incorrect state. {}")
432
433 return vdclist
434
435 def new_network(self, net_name, net_type, ip_profile=None, shared=False):
436 """Adds a tenant network to VIM
437 net_name is the name
438 net_type can be 'bridge','data'.'ptp'.
439 ip_profile is a dict containing the IP parameters of the network
440 shared is a boolean
441 Returns the network identifier"""
442
443 self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
444 .format(net_name, net_type, ip_profile, shared))
445
446 isshared = 'false'
447 if shared:
448 isshared = 'true'
449
450 network_uuid = self.create_network(network_name=net_name, net_type=net_type,
451 ip_profile=ip_profile, isshared=isshared)
452 if network_uuid is not None:
453 return network_uuid
454 else:
455 raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
456
457 def get_vcd_network_list(self):
458 """ Method available organization for a logged in tenant
459
460 Returns:
461 The return vca object that letter can be used to connect to vcloud direct as admin
462 """
463
464 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
465 vca = self.connect()
466 if not vca:
467 raise vimconn.vimconnConnectionException("self.connect() is failed.")
468
469 if not self.tenant_name:
470 raise vimconn.vimconnConnectionException("Tenant name is empty.")
471
472 vdc = vca.get_vdc(self.tenant_name)
473 if vdc is None:
474 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self.tenant_name))
475
476 vdc_uuid = vdc.get_id().split(":")[3]
477 networks = vca.get_networks(vdc.get_name())
478 network_list = []
479 try:
480 for network in networks:
481 filter_dict = {}
482 netid = network.get_id().split(":")
483 if len(netid) != 4:
484 continue
485
486 filter_dict["name"] = network.get_name()
487 filter_dict["id"] = netid[3]
488 filter_dict["shared"] = network.get_IsShared()
489 filter_dict["tenant_id"] = vdc_uuid
490 if network.get_status() == 1:
491 filter_dict["admin_state_up"] = True
492 else:
493 filter_dict["admin_state_up"] = False
494 filter_dict["status"] = "ACTIVE"
495 filter_dict["type"] = "bridge"
496 network_list.append(filter_dict)
497 self.logger.debug("get_vcd_network_list adding entry {}".format(filter_dict))
498 except:
499 self.logger.debug("Error in get_vcd_network_list")
500 self.logger.debug(traceback.format_exc())
501 pass
502
503 self.logger.debug("get_vcd_network_list returning {}".format(network_list))
504 return network_list
505
506 def get_network_list(self, filter_dict={}):
507 """Obtain tenant networks of VIM
508 Filter_dict can be:
509 name: network name OR/AND
510 id: network uuid OR/AND
511 shared: boolean OR/AND
512 tenant_id: tenant OR/AND
513 admin_state_up: boolean
514 status: 'ACTIVE'
515
516 [{key : value , key : value}]
517
518 Returns the network list of dictionaries:
519 [{<the fields at Filter_dict plus some VIM specific>}, ...]
520 List can be empty
521 """
522
523 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
524 vca = self.connect()
525 if not vca:
526 raise vimconn.vimconnConnectionException("self.connect() is failed.")
527
528 if not self.tenant_name:
529 raise vimconn.vimconnConnectionException("Tenant name is empty.")
530
531 vdc = vca.get_vdc(self.tenant_name)
532 if vdc is None:
533 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self.tenant_name))
534
535 vdcid = vdc.get_id().split(":")[3]
536 networks = vca.get_networks(vdc.get_name())
537 network_list = []
538
539 try:
540 for network in networks:
541 filter_entry = {}
542 net_uuid = network.get_id().split(":")
543 if len(net_uuid) != 4:
544 continue
545 else:
546 net_uuid = net_uuid[3]
547 # create dict entry
548 self.logger.debug("Adding {} to a list vcd id {} network {}".format(net_uuid,
549 vdcid,
550 network.get_name()))
551 filter_entry["name"] = network.get_name()
552 filter_entry["id"] = net_uuid
553 filter_entry["shared"] = network.get_IsShared()
554 filter_entry["tenant_id"] = vdcid
555 if network.get_status() == 1:
556 filter_entry["admin_state_up"] = True
557 else:
558 filter_entry["admin_state_up"] = False
559 filter_entry["status"] = "ACTIVE"
560 filter_entry["type"] = "bridge"
561 filtered_entry = filter_entry.copy()
562
563 if filter_dict is not None and filter_dict:
564 # we remove all the key : value we don't care and match only
565 # respected field
566 filtered_dict = set(filter_entry.keys()) - set(filter_dict)
567 for unwanted_key in filtered_dict: del filter_entry[unwanted_key]
568 if filter_dict == filter_entry:
569 network_list.append(filtered_entry)
570 else:
571 network_list.append(filtered_entry)
572 except:
573 self.logger.debug("Error in get_vcd_network_list")
574 self.logger.debug(traceback.format_exc())
575
576 self.logger.debug("Returning {}".format(network_list))
577 return network_list
578
579 def get_network(self, net_id):
580 """Method obtains network details of net_id VIM network
581 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
582
583 vca = self.connect()
584 if not vca:
585 raise vimconn.vimconnConnectionException("self.connect() is failed")
586
587 vdc = vca.get_vdc(self.tenant_name)
588 vdc_id = vdc.get_id().split(":")[3]
589
590 networks = vca.get_networks(vdc.get_name())
591 filter_dict = {}
592
593 try:
594 for network in networks:
595 vdc_network_id = network.get_id().split(":")
596 if len(vdc_network_id) == 4 and vdc_network_id[3] == net_id:
597 filter_dict["name"] = network.get_name()
598 filter_dict["id"] = vdc_network_id[3]
599 filter_dict["shared"] = network.get_IsShared()
600 filter_dict["tenant_id"] = vdc_id
601 if network.get_status() == 1:
602 filter_dict["admin_state_up"] = True
603 else:
604 filter_dict["admin_state_up"] = False
605 filter_dict["status"] = "ACTIVE"
606 filter_dict["type"] = "bridge"
607 self.logger.debug("Returning {}".format(filter_dict))
608 return filter_dict
609 except:
610 self.logger.debug("Error in get_network")
611 self.logger.debug(traceback.format_exc())
612
613 return filter_dict
614
615 def delete_network(self, net_id):
616 """
617 Method Deletes a tenant network from VIM, provide the network id.
618
619 Returns the network identifier or raise an exception
620 """
621
622 vca = self.connect()
623 if not vca:
624 raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self.tenant_name))
625
626 vcd_network = self.get_vcd_network(network_uuid=net_id)
627 if vcd_network is not None and vcd_network:
628 if self.delete_network_action(network_uuid=net_id):
629 return net_id
630 else:
631 raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
632
633 def refresh_nets_status(self, net_list):
634 """Get the status of the networks
635 Params: the list of network identifiers
636 Returns a dictionary with:
637 net_id: #VIM id of this network
638 status: #Mandatory. Text with one of:
639 # DELETED (not found at vim)
640 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
641 # OTHER (Vim reported other status not understood)
642 # ERROR (VIM indicates an ERROR status)
643 # ACTIVE, INACTIVE, DOWN (admin down),
644 # BUILD (on building process)
645 #
646 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
647 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
648
649 """
650
651 vca = self.connect()
652 if not vca:
653 raise vimconn.vimconnConnectionException("self.connect() is failed")
654
655 dict_entry = {}
656 try:
657 for net in net_list:
658 errormsg = ''
659 vcd_network = self.get_vcd_network(network_uuid=net)
660 if vcd_network is not None and vcd_network:
661 if vcd_network['status'] == '1':
662 status = 'ACTIVE'
663 else:
664 status = 'DOWN'
665 else:
666 status = 'DELETED'
667 errormsg = 'Network not found.'
668
669 dict_entry[net] = {'status': status, 'error_msg': errormsg,
670 'vim_info': yaml.safe_dump(vcd_network)}
671 except:
672 self.logger.debug("Error in refresh_nets_status")
673 self.logger.debug(traceback.format_exc())
674
675 return dict_entry
676
677 def get_flavor(self, flavor_id):
678 """Obtain flavor details from the VIM
679 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
680 """
681 if flavor_id not in flavorlist:
682 raise vimconn.vimconnNotFoundException("Flavor not found.")
683 return flavorlist[flavor_id]
684
685 def new_flavor(self, flavor_data):
686 """Adds a tenant flavor to VIM
687 flavor_data contains a dictionary with information, keys:
688 name: flavor name
689 ram: memory (cloud type) in MBytes
690 vpcus: cpus (cloud type)
691 extended: EPA parameters
692 - numas: #items requested in same NUMA
693 memory: number of 1G huge pages memory
694 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
695 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
696 - name: interface name
697 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
698 bandwidth: X Gbps; requested guarantee bandwidth
699 vpci: requested virtual PCI address
700 disk: disk size
701 is_public:
702
703
704
705 #TODO to concrete
706 Returns the flavor identifier"""
707
708 # generate a new uuid put to internal dict and return it.
709 flavor_id = uuid.uuid4()
710 flavorlist[str(flavor_id)] = flavor_data
711
712 return str(flavor_id)
713
714 def delete_flavor(self, flavor_id):
715 """Deletes a tenant flavor from VIM identify by its id
716
717 Returns the used id or raise an exception
718 """
719 if flavor_id not in flavorlist:
720 raise vimconn.vimconnNotFoundException("Flavor not found.")
721
722 flavorlist.pop(flavor_id, None)
723 return flavor_id
724
725 def new_image(self, image_dict):
726 """
727 Adds a tenant image to VIM
728 Returns:
729 200, image-id if the image is created
730 <0, message if there is an error
731 """
732
733 return self.get_image_id_from_path(image_dict['location'])
734
735 def delete_image(self, image_id):
736 """
737
738 :param image_id:
739 :return:
740 """
741
742 raise vimconn.vimconnNotImplemented("Should have implemented this")
743
744 def catalog_exists(self, catalog_name, catalogs):
745 """
746
747 :param catalog_name:
748 :param catalogs:
749 :return:
750 """
751 for catalog in catalogs:
752 if catalog.name == catalog_name:
753 return True
754 return False
755
756 def create_vimcatalog(self, vca=None, catalog_name=None):
757 """ Create new catalog entry in vCloud director.
758
759 Args
760 vca: vCloud director.
761 catalog_name catalog that client wish to create. Note no validation done for a name.
762 Client must make sure that provide valid string representation.
763
764 Return (bool) True if catalog created.
765
766 """
767 try:
768 task = vca.create_catalog(catalog_name, catalog_name)
769 result = vca.block_until_completed(task)
770 if not result:
771 return False
772 catalogs = vca.get_catalogs()
773 except:
774 return False
775 return self.catalog_exists(catalog_name, catalogs)
776
777 # noinspection PyIncorrectDocstring
778 def upload_ovf(self, vca=None, catalog_name=None, image_name=None, media_file_name=None,
779 description='', progress=False, chunk_bytes=128 * 1024):
780 """
781 Uploads a OVF file to a vCloud catalog
782
783 :param chunk_bytes:
784 :param progress:
785 :param description:
786 :param image_name:
787 :param vca:
788 :param catalog_name: (str): The name of the catalog to upload the media.
789 :param media_file_name: (str): The name of the local media file to upload.
790 :return: (bool) True if the media file was successfully uploaded, false otherwise.
791 """
792 os.path.isfile(media_file_name)
793 statinfo = os.stat(media_file_name)
794
795 # find a catalog entry where we upload OVF.
796 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
797 # status change.
798 # if VCD can parse OVF we upload VMDK file
799 for catalog in vca.get_catalogs():
800 if catalog_name != catalog.name:
801 continue
802 link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
803 link.get_rel() == 'add', catalog.get_Link())
804 assert len(link) == 1
805 data = """
806 <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>
807 """ % (escape(catalog_name), escape(description))
808 headers = vca.vcloud_session.get_vcloud_headers()
809 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
810 response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
811 if response.status_code == requests.codes.created:
812 catalogItem = XmlElementTree.fromstring(response.content)
813 entity = [child for child in catalogItem if
814 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
815 href = entity.get('href')
816 template = href
817 response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
818 verify=vca.verify, logger=self.logger)
819
820 if response.status_code == requests.codes.ok:
821 media = mediaType.parseString(response.content, True)
822 link = filter(lambda link: link.get_rel() == 'upload:default',
823 media.get_Files().get_File()[0].get_Link())[0]
824 headers = vca.vcloud_session.get_vcloud_headers()
825 headers['Content-Type'] = 'Content-Type text/xml'
826 response = Http.put(link.get_href(),
827 data=open(media_file_name, 'rb'),
828 headers=headers,
829 verify=vca.verify, logger=self.logger)
830 if response.status_code != requests.codes.ok:
831 self.logger.debug(
832 "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
833 media_file_name))
834 return False
835
836 # TODO fix this with aync block
837 time.sleep(5)
838
839 self.logger.debug("vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
840
841 # uploading VMDK file
842 # check status of OVF upload and upload remaining files.
843 response = Http.get(template,
844 headers=vca.vcloud_session.get_vcloud_headers(),
845 verify=vca.verify,
846 logger=self.logger)
847
848 if response.status_code == requests.codes.ok:
849 media = mediaType.parseString(response.content, True)
850 number_of_files = len(media.get_Files().get_File())
851 for index in xrange(0, number_of_files):
852 links_list = filter(lambda link: link.get_rel() == 'upload:default',
853 media.get_Files().get_File()[index].get_Link())
854 for link in links_list:
855 # we skip ovf since it already uploaded.
856 if 'ovf' in link.get_href():
857 continue
858 # The OVF file and VMDK must be in a same directory
859 head, tail = os.path.split(media_file_name)
860 file_vmdk = head + '/' + link.get_href().split("/")[-1]
861 if not os.path.isfile(file_vmdk):
862 return False
863 statinfo = os.stat(file_vmdk)
864 if statinfo.st_size == 0:
865 return False
866 hrefvmdk = link.get_href()
867
868 if progress:
869 print("Uploading file: {}".format(file_vmdk))
870 if progress:
871 widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
872 FileTransferSpeed()]
873 progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
874
875 bytes_transferred = 0
876 f = open(file_vmdk, 'rb')
877 while bytes_transferred < statinfo.st_size:
878 my_bytes = f.read(chunk_bytes)
879 if len(my_bytes) <= chunk_bytes:
880 headers = vca.vcloud_session.get_vcloud_headers()
881 headers['Content-Range'] = 'bytes %s-%s/%s' % (
882 bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
883 headers['Content-Length'] = str(len(my_bytes))
884 response = Http.put(hrefvmdk,
885 headers=headers,
886 data=my_bytes,
887 verify=vca.verify,
888 logger=None)
889
890 if response.status_code == requests.codes.ok:
891 bytes_transferred += len(my_bytes)
892 if progress:
893 progress_bar.update(bytes_transferred)
894 else:
895 self.logger.debug(
896 'file upload failed with error: [%s] %s' % (response.status_code,
897 response.content))
898
899 f.close()
900 return False
901 f.close()
902 if progress:
903 progress_bar.finish()
904 return True
905 else:
906 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
907 format(catalog_name, media_file_name))
908 return False
909
910 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
911 return False
912
913 def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False):
914 """Upload media file"""
915 # TODO add named parameters for readability
916
917 return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0],
918 media_file_name=medial_file_name, description='medial_file_name', progress=progress)
919
920 def validate_uuid4(self, uuid_string=None):
921 """ Method validate correct format of UUID.
922
923 Return: true if string represent valid uuid
924 """
925 try:
926 val = uuid.UUID(uuid_string, version=4)
927 except ValueError:
928 return False
929 return True
930
931 def get_catalogid(self, catalog_name=None, catalogs=None):
932 """ Method check catalog and return catalog ID in UUID format.
933
934 Args
935 catalog_name: catalog name as string
936 catalogs: list of catalogs.
937
938 Return: catalogs uuid
939 """
940
941 for catalog in catalogs:
942 if catalog.name == catalog_name:
943 catalog_id = catalog.get_id().split(":")
944 return catalog_id[3]
945 return None
946
947 def get_catalogbyid(self, catalog_uuid=None, catalogs=None):
948 """ Method check catalog and return catalog name lookup done by catalog UUID.
949
950 Args
951 catalog_name: catalog name as string
952 catalogs: list of catalogs.
953
954 Return: catalogs name or None
955 """
956
957 if not self.validate_uuid4(uuid_string=catalog_uuid):
958 return None
959
960 for catalog in catalogs:
961 catalog_id = catalog.get_id().split(":")[3]
962 if catalog_id == catalog_uuid:
963 return catalog.name
964 return None
965
966 def get_image_id_from_path(self, path=None, progress=False):
967 """ Method upload OVF image to vCloud director.
968
969 Each OVF image represented as single catalog entry in vcloud director.
970 The method check for existing catalog entry. The check done by file name without file extension.
971
972 if given catalog name already present method will respond with existing catalog uuid otherwise
973 it will create new catalog entry and upload OVF file to newly created catalog.
974
975 If method can't create catalog entry or upload a file it will throw exception.
976
977 Method accept boolean flag progress that will output progress bar. It useful method
978 for standalone upload use case. In case to test large file upload.
979
980 Args
981 path: - valid path to OVF file.
982 progress - boolean progress bar show progress bar.
983
984 Return: if image uploaded correct method will provide image catalog UUID.
985 """
986 vca = self.connect()
987 if not vca:
988 raise vimconn.vimconnConnectionException("self.connect() is failed.")
989
990 if not path:
991 raise vimconn.vimconnException("Image path can't be None.")
992
993 if not os.path.isfile(path):
994 raise vimconn.vimconnException("Can't read file. File not found.")
995
996 if not os.access(path, os.R_OK):
997 raise vimconn.vimconnException("Can't read file. Check file permission to read.")
998
999 self.logger.debug("get_image_id_from_path() client requesting {} ".format(path))
1000
1001 dirpath, filename = os.path.split(path)
1002 flname, file_extension = os.path.splitext(path)
1003 if file_extension != '.ovf':
1004 self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
1005 raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.")
1006
1007 catalog_name = os.path.splitext(filename)[0]
1008 catalog_md5_name = hashlib.md5(path).hexdigest()
1009 self.logger.debug("File name {} Catalog Name {} file path {} "
1010 "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name))
1011
1012 catalogs = vca.get_catalogs()
1013 if len(catalogs) == 0:
1014 self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
1015 result = self.create_vimcatalog(vca, catalog_md5_name)
1016 if not result:
1017 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1018 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
1019 media_name=filename, medial_file_name=path, progress=progress)
1020 if not result:
1021 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name))
1022 return self.get_catalogid(catalog_name, vca.get_catalogs())
1023 else:
1024 for catalog in catalogs:
1025 # search for existing catalog if we find same name we return ID
1026 # TODO optimize this
1027 if catalog.name == catalog_md5_name:
1028 self.logger.debug("Found existing catalog entry for {} "
1029 "catalog id {}".format(catalog_name,
1030 self.get_catalogid(catalog_md5_name, catalogs)))
1031 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
1032
1033 # if we didn't find existing catalog we create a new one and upload image.
1034 self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name))
1035 result = self.create_vimcatalog(vca, catalog_md5_name)
1036 if not result:
1037 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1038
1039 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
1040 media_name=filename, medial_file_name=path, progress=progress)
1041 if not result:
1042 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name))
1043
1044 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
1045
1046 def get_vappid(self, vdc=None, vapp_name=None):
1047 """ Method takes vdc object and vApp name and returns vapp uuid or None
1048
1049 Args:
1050 vdc: The VDC object.
1051 vapp_name: is application vappp name identifier
1052
1053 Returns:
1054 The return vApp name otherwise None
1055 """
1056 if vdc is None or vapp_name is None:
1057 return None
1058 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1059 try:
1060 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1061 vdc.ResourceEntities.ResourceEntity)
1062 if len(refs) == 1:
1063 return refs[0].href.split("vapp")[1][1:]
1064 except Exception as e:
1065 self.logger.exception(e)
1066 return False
1067 return None
1068
1069 def check_vapp(self, vdc=None, vapp_uuid=None):
1070 """ Method Method returns True or False if vapp deployed in vCloud director
1071
1072 Args:
1073 vca: Connector to VCA
1074 vdc: The VDC object.
1075 vappid: vappid is application identifier
1076
1077 Returns:
1078 The return True if vApp deployed
1079 :param vdc:
1080 :param vapp_uuid:
1081 """
1082 try:
1083 refs = filter(lambda ref:
1084 ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1085 vdc.ResourceEntities.ResourceEntity)
1086 for ref in refs:
1087 vappid = ref.href.split("vapp")[1][1:]
1088 # find vapp with respected vapp uuid
1089 if vappid == vapp_uuid:
1090 return True
1091 except Exception as e:
1092 self.logger.exception(e)
1093 return False
1094 return False
1095
1096 def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None):
1097 """Method returns vApp name from vCD and lookup done by vapp_id.
1098
1099 Args:
1100 vca: Connector to VCA
1101 vdc: The VDC object.
1102 vapp_uuid: vappid is application identifier
1103
1104 Returns:
1105 The return vApp name otherwise None
1106 """
1107
1108 try:
1109 refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1110 vdc.ResourceEntities.ResourceEntity)
1111 for ref in refs:
1112 # we care only about UUID the rest doesn't matter
1113 vappid = ref.href.split("vapp")[1][1:]
1114 if vappid == vapp_uuid:
1115 response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
1116 logger=self.logger)
1117 tree = XmlElementTree.fromstring(response.content)
1118 return tree.attrib['name']
1119 except Exception as e:
1120 self.logger.exception(e)
1121 return None
1122 return None
1123
1124 def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list={},
1125 cloud_config=None, disk_list=None):
1126 """Adds a VM instance to VIM
1127 Params:
1128 start: indicates if VM must start or boot in pause mode. Ignored
1129 image_id,flavor_id: image and flavor uuid
1130 net_list: list of interfaces, each one is a dictionary with:
1131 name:
1132 net_id: network uuid to connect
1133 vpci: virtual vcpi to assign
1134 model: interface model, virtio, e2000, ...
1135 mac_address:
1136 use: 'data', 'bridge', 'mgmt'
1137 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1138 vim_id: filled/added by this function
1139 cloud_config: can be a text script to be passed directly to cloud-init,
1140 or an object to inject users and ssh keys with format:
1141 key-pairs: [] list of keys to install to the default user
1142 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1143 #TODO ip, security groups
1144 Returns >=0, the instance identifier
1145 <0, error_text
1146 """
1147
1148 self.logger.info("Creating new instance for entry {}".format(name))
1149 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1150 format(description, start, image_id, flavor_id, net_list, cloud_config))
1151 vca = self.connect()
1152 if not vca:
1153 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1154
1155 #new vm name = vmname + tenant_id + uuid
1156 new_vm_name = [name, '-', str(uuid.uuid4())]
1157 vmname_andid = ''.join(new_vm_name)
1158
1159 # if vm already deployed we return existing uuid
1160 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1161 # if vapp_uuid is not None:
1162 # return vapp_uuid
1163
1164 # we check for presence of VDC, Catalog entry and Flavor.
1165 vdc = vca.get_vdc(self.tenant_name)
1166 if vdc is None:
1167 raise vimconn.vimconnNotFoundException(
1168 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
1169 catalogs = vca.get_catalogs()
1170 if catalogs is None:
1171 raise vimconn.vimconnNotFoundException(
1172 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
1173
1174 catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1175 if catalog_hash_name:
1176 self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
1177 else:
1178 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1179 "(Failed retrieve catalog information {})".format(name, image_id))
1180
1181
1182 # Set vCPU and Memory based on flavor.
1183 #
1184 vm_cpus = None
1185 vm_memory = None
1186 if flavor_id is not None:
1187 if flavor_id not in flavorlist:
1188 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1189 "Failed retrieve flavor information "
1190 "flavor id {}".format(name, flavor_id))
1191 else:
1192 try:
1193 flavor = flavorlist[flavor_id]
1194 vm_cpus = flavor[FLAVOR_VCPUS_KEY]
1195 vm_memory = flavor[FLAVOR_RAM_KEY]
1196 except KeyError:
1197 raise vimconn.vimconnException("Corrupted flavor. {}".format(flavor_id))
1198
1199 # image upload creates template name as catalog name space Template.
1200 templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1201 power_on = 'false'
1202 if start:
1203 power_on = 'true'
1204
1205 # client must provide at least one entry in net_list if not we report error
1206 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1207 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1208 primary_net = None
1209 primary_netname = None
1210 network_mode = 'bridged'
1211 if net_list is not None and len(net_list) > 0:
1212 for net in net_list:
1213 if 'use' in net and net['use'] == 'mgmt':
1214 primary_net = net
1215 if primary_net is None:
1216 primary_net = net_list[0]
1217
1218 try:
1219 primary_net_id = primary_net['net_id']
1220 network_dict = self.get_vcd_network(network_uuid=primary_net_id)
1221 if 'name' in network_dict:
1222 primary_netname = network_dict['name']
1223
1224 except KeyError:
1225 raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
1226 else:
1227 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
1228
1229 # use: 'data', 'bridge', 'mgmt'
1230 # create vApp. Set vcpu and ram based on flavor id.
1231 vapptask = vca.create_vapp(self.tenant_name, vmname_andid, templateName,
1232 self.get_catalogbyid(image_id, catalogs),
1233 network_name=None, # None while creating vapp
1234 network_mode=network_mode,
1235 vm_name=vmname_andid,
1236 vm_cpus=vm_cpus, # can be None if flavor is None
1237 vm_memory=vm_memory) # can be None if flavor is None
1238
1239 if vapptask is None or vapptask is False:
1240 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid))
1241 if type(vapptask) is VappTask:
1242 vca.block_until_completed(vapptask)
1243
1244 # we should have now vapp in undeployed state.
1245 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1246 if vapp is None:
1247 raise vimconn.vimconnUnexpectedResponse(
1248 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(vmname_andid))
1249
1250 # add NICs & connect to networks in netlist
1251 try:
1252 self.logger.info("Request to connect VM to a network: {}".format(net_list))
1253 nicIndex = 0
1254 primary_nic_index = 0
1255 for net in net_list:
1256 # openmano uses network id in UUID format.
1257 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1258 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1259 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1260
1261 if 'net_id' not in net:
1262 continue
1263
1264 interface_net_id = net['net_id']
1265 interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
1266 interface_network_mode = net['use']
1267
1268 if interface_network_mode == 'mgmt':
1269 primary_nic_index = nicIndex
1270
1271 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1272 - DHCP (The IP address is obtained from a DHCP service.)
1273 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1274 - NONE (No IP addressing mode specified.)"""
1275
1276 if primary_netname is not None:
1277 nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name))
1278 if len(nets) == 1:
1279 self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name))
1280 task = vapp.connect_to_network(nets[0].name, nets[0].href)
1281 if type(task) is GenericTask:
1282 vca.block_until_completed(task)
1283 # connect network to VM - with all DHCP by default
1284 self.logger.info("new_vminstance(): Connecting VM to a network {}".format(nets[0].name))
1285 task = vapp.connect_vms(nets[0].name,
1286 connection_index=nicIndex,
1287 connections_primary_index=primary_nic_index,
1288 ip_allocation_mode='DHCP')
1289 if type(task) is GenericTask:
1290 vca.block_until_completed(task)
1291 nicIndex += 1
1292 except KeyError:
1293 # it might be a case if specific mandatory entry in dict is empty
1294 self.logger.debug("Key error {}".format(KeyError.message))
1295 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1296
1297 # deploy and power on vm
1298 task = vapp.poweron()
1299 if type(task) is TaskType:
1300 vca.block_until_completed(task)
1301 deploytask = vapp.deploy(powerOn='True')
1302 if type(task) is TaskType:
1303 vca.block_until_completed(deploytask)
1304
1305 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1306 wait_time = 0
1307 vapp_uuid = None
1308 while wait_time <= MAX_WAIT_TIME:
1309 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1310 if vapp and vapp.me.deployed:
1311 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1312 break
1313 else:
1314 self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
1315 time.sleep(INTERVAL_TIME)
1316
1317 wait_time +=INTERVAL_TIME
1318
1319 if vapp_uuid is not None:
1320 return vapp_uuid
1321 else:
1322 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1323
1324 ##
1325 ##
1326 ## based on current discussion
1327 ##
1328 ##
1329 ## server:
1330 # created: '2016-09-08T11:51:58'
1331 # description: simple-instance.linux1.1
1332 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1333 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1334 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1335 # status: ACTIVE
1336 # error_msg:
1337 # interfaces: …
1338 #
1339 def get_vminstance(self, vim_vm_uuid=None):
1340 """Returns the VM instance information from VIM"""
1341
1342 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
1343 vca = self.connect()
1344 if not vca:
1345 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1346
1347 vdc = vca.get_vdc(self.tenant_name)
1348 if vdc is None:
1349 raise vimconn.vimconnConnectionException(
1350 "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1351
1352 vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
1353 if not vm_info_dict:
1354 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1355 raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1356
1357 status_key = vm_info_dict['status']
1358 error = ''
1359 try:
1360 vm_dict = {'created': vm_info_dict['created'],
1361 'description': vm_info_dict['name'],
1362 'status': vcdStatusCode2manoFormat[int(status_key)],
1363 'hostId': vm_info_dict['vmuuid'],
1364 'error_msg': error,
1365 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1366
1367 if 'interfaces' in vm_info_dict:
1368 vm_dict['interfaces'] = vm_info_dict['interfaces']
1369 else:
1370 vm_dict['interfaces'] = []
1371 except KeyError:
1372 vm_dict = {'created': '',
1373 'description': '',
1374 'status': vcdStatusCode2manoFormat[int(-1)],
1375 'hostId': vm_info_dict['vmuuid'],
1376 'error_msg': "Inconsistency state",
1377 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1378
1379 return vm_dict
1380
1381 def delete_vminstance(self, vm__vim_uuid):
1382 """Method poweroff and remove VM instance from vcloud director network.
1383
1384 Args:
1385 vm__vim_uuid: VM UUID
1386
1387 Returns:
1388 Returns the instance identifier
1389 """
1390
1391 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
1392 vca = self.connect()
1393 if not vca:
1394 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1395
1396 vdc = vca.get_vdc(self.tenant_name)
1397 if vdc is None:
1398 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1399 self.tenant_name))
1400 raise vimconn.vimconnException(
1401 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1402
1403 try:
1404 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1405 if vapp_name is None:
1406 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1407 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1408 else:
1409 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1410
1411 # Delete vApp and wait for status change if task executed and vApp is None.
1412 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1413
1414 if vapp:
1415 if vapp.me.deployed:
1416 self.logger.info("Powering off vApp {}".format(vapp_name))
1417 #Power off vApp
1418 powered_off = False
1419 wait_time = 0
1420 while wait_time <= MAX_WAIT_TIME:
1421 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1422 if not vapp:
1423 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1424 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1425
1426 power_off_task = vapp.poweroff()
1427 if type(power_off_task) is GenericTask:
1428 result = vca.block_until_completed(power_off_task)
1429 if result:
1430 powered_off = True
1431 break
1432 else:
1433 self.logger.info("Wait for vApp {} to power off".format(vapp_name))
1434 time.sleep(INTERVAL_TIME)
1435
1436 wait_time +=INTERVAL_TIME
1437 if not powered_off:
1438 self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
1439 else:
1440 self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
1441
1442 #Undeploy vApp
1443 self.logger.info("Undeploy vApp {}".format(vapp_name))
1444 wait_time = 0
1445 undeployed = False
1446 while wait_time <= MAX_WAIT_TIME:
1447 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1448 if not vapp:
1449 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1450 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1451 undeploy_task = vapp.undeploy(action='powerOff')
1452
1453 if type(undeploy_task) is GenericTask:
1454 result = vca.block_until_completed(undeploy_task)
1455 if result:
1456 undeployed = True
1457 break
1458 else:
1459 self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
1460 time.sleep(INTERVAL_TIME)
1461
1462 wait_time +=INTERVAL_TIME
1463
1464 if not undeployed:
1465 self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
1466
1467 # delete vapp
1468 self.logger.info("Start deletion of vApp {} ".format(vapp_name))
1469 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1470
1471 if vapp is not None:
1472 wait_time = 0
1473 result = False
1474
1475 while wait_time <= MAX_WAIT_TIME:
1476 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1477 if not vapp:
1478 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1479 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1480
1481 delete_task = vapp.delete()
1482
1483 if type(delete_task) is GenericTask:
1484 vca.block_until_completed(delete_task)
1485 result = vca.block_until_completed(delete_task)
1486 if result:
1487 break
1488 else:
1489 self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
1490 time.sleep(INTERVAL_TIME)
1491
1492 wait_time +=INTERVAL_TIME
1493
1494 if not result:
1495 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
1496
1497 except:
1498 self.logger.debug(traceback.format_exc())
1499 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1500
1501 if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
1502 self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
1503 return vm__vim_uuid
1504 else:
1505 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1506
1507 def refresh_vms_status(self, vm_list):
1508 """Get the status of the virtual machines and their interfaces/ports
1509 Params: the list of VM identifiers
1510 Returns a dictionary with:
1511 vm_id: #VIM id of this Virtual Machine
1512 status: #Mandatory. Text with one of:
1513 # DELETED (not found at vim)
1514 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1515 # OTHER (Vim reported other status not understood)
1516 # ERROR (VIM indicates an ERROR status)
1517 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1518 # CREATING (on building process), ERROR
1519 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1520 #
1521 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1522 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1523 interfaces:
1524 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1525 mac_address: #Text format XX:XX:XX:XX:XX:XX
1526 vim_net_id: #network id where this interface is connected
1527 vim_interface_id: #interface/port VIM id
1528 ip_address: #null, or text with IPv4, IPv6 address
1529 """
1530
1531 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
1532 vca = self.connect()
1533 if not vca:
1534 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1535
1536 vdc = vca.get_vdc(self.tenant_name)
1537 if vdc is None:
1538 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1539
1540 vms_dict = {}
1541 for vmuuid in vm_list:
1542 vmname = self.get_namebyvappid(vca, vdc, vmuuid)
1543 if vmname is not None:
1544
1545 the_vapp = vca.get_vapp(vdc, vmname)
1546 vm_info = the_vapp.get_vms_details()
1547 vm_status = vm_info[0]['status']
1548
1549 vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1550 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1551 'vim_info': yaml.safe_dump(the_vapp.get_vms_details()), 'interfaces': []}
1552
1553 # get networks
1554 try:
1555 vm_app_networks = the_vapp.get_vms_network_info()
1556 for vapp_network in vm_app_networks:
1557 for vm_network in vapp_network:
1558 if vm_network['name'] == vmname:
1559 interface = {"mac_address": vm_network['mac'],
1560 "vim_net_id": self.get_network_id_by_name(vm_network['network_name']),
1561 "vim_interface_id": self.get_network_id_by_name(vm_network['network_name']),
1562 'ip_address': vm_network['ip']}
1563 # interface['vim_info'] = yaml.safe_dump(vm_network)
1564 vm_dict["interfaces"].append(interface)
1565 # add a vm to vm dict
1566 vms_dict.setdefault(vmuuid, vm_dict)
1567 except KeyError:
1568 self.logger.debug("Error in respond {}".format(KeyError.message))
1569 self.logger.debug(traceback.format_exc())
1570
1571 return vms_dict
1572
1573 def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
1574 """Send and action over a VM instance from VIM
1575 Returns the vm_id if the action was successfully sent to the VIM"""
1576
1577 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
1578 if vm__vim_uuid is None or action_dict is None:
1579 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
1580
1581 vca = self.connect()
1582 if not vca:
1583 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1584
1585 vdc = vca.get_vdc(self.tenant_name)
1586 if vdc is None:
1587 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
1588
1589 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1590 if vapp_name is None:
1591 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1592 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1593 else:
1594 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1595
1596 try:
1597 the_vapp = vca.get_vapp(vdc, vapp_name)
1598 # TODO fix all status
1599 if "start" in action_dict:
1600 if action_dict["start"] == "rebuild":
1601 the_vapp.deploy(powerOn=True)
1602 else:
1603 vm_info = the_vapp.get_vms_details()
1604 vm_status = vm_info[0]['status']
1605 if vm_status == "Suspended":
1606 the_vapp.poweron()
1607 elif vm_status.status == "Powered off":
1608 the_vapp.poweron()
1609 elif "pause" in action_dict:
1610 pass
1611 ## server.pause()
1612 elif "resume" in action_dict:
1613 pass
1614 ## server.resume()
1615 elif "shutoff" in action_dict or "shutdown" in action_dict:
1616 the_vapp.shutdown()
1617 elif "forceOff" in action_dict:
1618 the_vapp.reset()
1619 elif "terminate" in action_dict:
1620 the_vapp.delete()
1621 # elif "createImage" in action_dict:
1622 # server.create_image()
1623 else:
1624 pass
1625 except:
1626 pass
1627
1628 def get_vminstance_console(self, vm_id, console_type="vnc"):
1629 """
1630 Get a console for the virtual machine
1631 Params:
1632 vm_id: uuid of the VM
1633 console_type, can be:
1634 "novnc" (by default), "xvpvnc" for VNC types,
1635 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1636 Returns dict with the console parameters:
1637 protocol: ssh, ftp, http, https, ...
1638 server: usually ip address
1639 port: the http, ssh, ... port
1640 suffix: extra text, e.g. the http path and query string
1641 """
1642 raise vimconn.vimconnNotImplemented("Should have implemented this")
1643
1644 # NOT USED METHODS in current version
1645
1646 def host_vim2gui(self, host, server_dict):
1647 """Transform host dictionary from VIM format to GUI format,
1648 and append to the server_dict
1649 """
1650 raise vimconn.vimconnNotImplemented("Should have implemented this")
1651
1652 def get_hosts_info(self):
1653 """Get the information of deployed hosts
1654 Returns the hosts content"""
1655 raise vimconn.vimconnNotImplemented("Should have implemented this")
1656
1657 def get_hosts(self, vim_tenant):
1658 """Get the hosts and deployed instances
1659 Returns the hosts content"""
1660 raise vimconn.vimconnNotImplemented("Should have implemented this")
1661
1662 def get_processor_rankings(self):
1663 """Get the processor rankings in the VIM database"""
1664 raise vimconn.vimconnNotImplemented("Should have implemented this")
1665
1666 def new_host(self, host_data):
1667 """Adds a new host to VIM"""
1668 '''Returns status code of the VIM response'''
1669 raise vimconn.vimconnNotImplemented("Should have implemented this")
1670
1671 def new_external_port(self, port_data):
1672 """Adds a external port to VIM"""
1673 '''Returns the port identifier'''
1674 raise vimconn.vimconnNotImplemented("Should have implemented this")
1675
1676 def new_external_network(self, net_name, net_type):
1677 """Adds a external network to VIM (shared)"""
1678 '''Returns the network identifier'''
1679 raise vimconn.vimconnNotImplemented("Should have implemented this")
1680
1681 def connect_port_network(self, port_id, network_id, admin=False):
1682 """Connects a external port to a network"""
1683 '''Returns status code of the VIM response'''
1684 raise vimconn.vimconnNotImplemented("Should have implemented this")
1685
1686 def new_vminstancefromJSON(self, vm_data):
1687 """Adds a VM instance to VIM"""
1688 '''Returns the instance identifier'''
1689 raise vimconn.vimconnNotImplemented("Should have implemented this")
1690
1691 def get_network_name_by_id(self, network_uuid=None):
1692 """Method gets vcloud director network named based on supplied uuid.
1693
1694 Args:
1695 network_uuid: network_id
1696
1697 Returns:
1698 The return network name.
1699 """
1700
1701 vca = self.connect()
1702 if not vca:
1703 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1704
1705 if not network_uuid:
1706 return None
1707
1708 try:
1709 org_dict = self.get_org(self.org_uuid)
1710 if 'networks' in org_dict:
1711 org_network_dict = org_dict['networks']
1712 for net_uuid in org_network_dict:
1713 if net_uuid == network_uuid:
1714 return org_network_dict[net_uuid]
1715 except:
1716 self.logger.debug("Exception in get_network_name_by_id")
1717 self.logger.debug(traceback.format_exc())
1718
1719 return None
1720
1721 def get_network_id_by_name(self, network_name=None):
1722 """Method gets vcloud director network uuid based on supplied name.
1723
1724 Args:
1725 network_name: network_name
1726 Returns:
1727 The return network uuid.
1728 network_uuid: network_id
1729 """
1730
1731 vca = self.connect()
1732 if not vca:
1733 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1734
1735 if not network_name:
1736 self.logger.debug("get_network_id_by_name() : Network name is empty")
1737 return None
1738
1739 try:
1740 org_dict = self.get_org(self.org_uuid)
1741 if org_dict and 'networks' in org_dict:
1742 org_network_dict = org_dict['networks']
1743 for net_uuid,net_name in org_network_dict.iteritems():
1744 if net_name == network_name:
1745 return net_uuid
1746
1747 except KeyError as exp:
1748 self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
1749
1750 return None
1751
1752 def list_org_action(self):
1753 """
1754 Method leverages vCloud director and query for available organization for particular user
1755
1756 Args:
1757 vca - is active VCA connection.
1758 vdc_name - is a vdc name that will be used to query vms action
1759
1760 Returns:
1761 The return XML respond
1762 """
1763
1764 vca = self.connect()
1765 if not vca:
1766 raise vimconn.vimconnConnectionException("self.connect() is failed")
1767
1768 url_list = [vca.host, '/api/org']
1769 vm_list_rest_call = ''.join(url_list)
1770
1771 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1772 response = Http.get(url=vm_list_rest_call,
1773 headers=vca.vcloud_session.get_vcloud_headers(),
1774 verify=vca.verify,
1775 logger=vca.logger)
1776 if response.status_code == requests.codes.ok:
1777 return response.content
1778
1779 return None
1780
1781 def get_org_action(self, org_uuid=None):
1782 """
1783 Method leverages vCloud director and retrieve available object fdr organization.
1784
1785 Args:
1786 vca - is active VCA connection.
1787 vdc_name - is a vdc name that will be used to query vms action
1788
1789 Returns:
1790 The return XML respond
1791 """
1792
1793 vca = self.connect()
1794 if not vca:
1795 raise vimconn.vimconnConnectionException("self.connect() is failed")
1796
1797 if org_uuid is None:
1798 return None
1799
1800 url_list = [vca.host, '/api/org/', org_uuid]
1801 vm_list_rest_call = ''.join(url_list)
1802
1803 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1804 response = Http.get(url=vm_list_rest_call,
1805 headers=vca.vcloud_session.get_vcloud_headers(),
1806 verify=vca.verify,
1807 logger=vca.logger)
1808 if response.status_code == requests.codes.ok:
1809 return response.content
1810
1811 return None
1812
1813 def get_org(self, org_uuid=None):
1814 """
1815 Method retrieves available organization in vCloud Director
1816
1817 Args:
1818 org_uuid - is a organization uuid.
1819
1820 Returns:
1821 The return dictionary with following key
1822 "network" - for network list under the org
1823 "catalogs" - for network list under the org
1824 "vdcs" - for vdc list under org
1825 """
1826
1827 org_dict = {}
1828 vca = self.connect()
1829 if not vca:
1830 raise vimconn.vimconnConnectionException("self.connect() is failed")
1831
1832 if org_uuid is None:
1833 return org_dict
1834
1835 content = self.get_org_action(org_uuid=org_uuid)
1836 try:
1837 vdc_list = {}
1838 network_list = {}
1839 catalog_list = {}
1840 vm_list_xmlroot = XmlElementTree.fromstring(content)
1841 for child in vm_list_xmlroot:
1842 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1843 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1844 org_dict['vdcs'] = vdc_list
1845 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1846 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1847 org_dict['networks'] = network_list
1848 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1849 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1850 org_dict['catalogs'] = catalog_list
1851 except:
1852 pass
1853
1854 return org_dict
1855
1856 def get_org_list(self):
1857 """
1858 Method retrieves available organization in vCloud Director
1859
1860 Args:
1861 vca - is active VCA connection.
1862
1863 Returns:
1864 The return dictionary and key for each entry VDC UUID
1865 """
1866
1867 org_dict = {}
1868 vca = self.connect()
1869 if not vca:
1870 raise vimconn.vimconnConnectionException("self.connect() is failed")
1871
1872 content = self.list_org_action()
1873 try:
1874 vm_list_xmlroot = XmlElementTree.fromstring(content)
1875 for vm_xml in vm_list_xmlroot:
1876 if vm_xml.tag.split("}")[1] == 'Org':
1877 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
1878 org_dict[org_uuid[0]] = vm_xml.attrib['name']
1879 except:
1880 pass
1881
1882 return org_dict
1883
1884 def vms_view_action(self, vdc_name=None):
1885 """ Method leverages vCloud director vms query call
1886
1887 Args:
1888 vca - is active VCA connection.
1889 vdc_name - is a vdc name that will be used to query vms action
1890
1891 Returns:
1892 The return XML respond
1893 """
1894 vca = self.connect()
1895 if vdc_name is None:
1896 return None
1897
1898 url_list = [vca.host, '/api/vms/query']
1899 vm_list_rest_call = ''.join(url_list)
1900
1901 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1902 refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
1903 vca.vcloud_session.organization.Link)
1904 if len(refs) == 1:
1905 response = Http.get(url=vm_list_rest_call,
1906 headers=vca.vcloud_session.get_vcloud_headers(),
1907 verify=vca.verify,
1908 logger=vca.logger)
1909 if response.status_code == requests.codes.ok:
1910 return response.content
1911
1912 return None
1913
1914 def get_vapp_list(self, vdc_name=None):
1915 """
1916 Method retrieves vApp list deployed vCloud director and returns a dictionary
1917 contains a list of all vapp deployed for queried VDC.
1918 The key for a dictionary is vApp UUID
1919
1920
1921 Args:
1922 vca - is active VCA connection.
1923 vdc_name - is a vdc name that will be used to query vms action
1924
1925 Returns:
1926 The return dictionary and key for each entry vapp UUID
1927 """
1928
1929 vapp_dict = {}
1930 if vdc_name is None:
1931 return vapp_dict
1932
1933 content = self.vms_view_action(vdc_name=vdc_name)
1934 try:
1935 vm_list_xmlroot = XmlElementTree.fromstring(content)
1936 for vm_xml in vm_list_xmlroot:
1937 if vm_xml.tag.split("}")[1] == 'VMRecord':
1938 if vm_xml.attrib['isVAppTemplate'] == 'true':
1939 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
1940 if 'vappTemplate-' in rawuuid[0]:
1941 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1942 # vm and use raw UUID as key
1943 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
1944 except:
1945 pass
1946
1947 return vapp_dict
1948
1949 def get_vm_list(self, vdc_name=None):
1950 """
1951 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1952 contains a list of all VM's deployed for queried VDC.
1953 The key for a dictionary is VM UUID
1954
1955
1956 Args:
1957 vca - is active VCA connection.
1958 vdc_name - is a vdc name that will be used to query vms action
1959
1960 Returns:
1961 The return dictionary and key for each entry vapp UUID
1962 """
1963 vm_dict = {}
1964
1965 if vdc_name is None:
1966 return vm_dict
1967
1968 content = self.vms_view_action(vdc_name=vdc_name)
1969 try:
1970 vm_list_xmlroot = XmlElementTree.fromstring(content)
1971 for vm_xml in vm_list_xmlroot:
1972 if vm_xml.tag.split("}")[1] == 'VMRecord':
1973 if vm_xml.attrib['isVAppTemplate'] == 'false':
1974 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
1975 if 'vm-' in rawuuid[0]:
1976 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1977 # vm and use raw UUID as key
1978 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
1979 except:
1980 pass
1981
1982 return vm_dict
1983
1984 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
1985 """
1986 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
1987 contains a list of all VM's deployed for queried VDC.
1988 The key for a dictionary is VM UUID
1989
1990
1991 Args:
1992 vca - is active VCA connection.
1993 vdc_name - is a vdc name that will be used to query vms action
1994
1995 Returns:
1996 The return dictionary and key for each entry vapp UUID
1997 """
1998 vm_dict = {}
1999 vca = self.connect()
2000 if not vca:
2001 raise vimconn.vimconnConnectionException("self.connect() is failed")
2002
2003 if vdc_name is None:
2004 return vm_dict
2005
2006 content = self.vms_view_action(vdc_name=vdc_name)
2007 try:
2008 vm_list_xmlroot = XmlElementTree.fromstring(content)
2009 for vm_xml in vm_list_xmlroot:
2010 if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
2011 # lookup done by UUID
2012 if isuuid:
2013 if vapp_name in vm_xml.attrib['container']:
2014 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2015 if 'vm-' in rawuuid[0]:
2016 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2017 break
2018 # lookup done by Name
2019 else:
2020 if vapp_name in vm_xml.attrib['name']:
2021 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2022 if 'vm-' in rawuuid[0]:
2023 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2024 break
2025 except:
2026 pass
2027
2028 return vm_dict
2029
2030 def get_network_action(self, network_uuid=None):
2031 """
2032 Method leverages vCloud director and query network based on network uuid
2033
2034 Args:
2035 vca - is active VCA connection.
2036 network_uuid - is a network uuid
2037
2038 Returns:
2039 The return XML respond
2040 """
2041
2042 vca = self.connect()
2043 if not vca:
2044 raise vimconn.vimconnConnectionException("self.connect() is failed")
2045
2046 if network_uuid is None:
2047 return None
2048
2049 url_list = [vca.host, '/api/network/', network_uuid]
2050 vm_list_rest_call = ''.join(url_list)
2051
2052 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2053 response = Http.get(url=vm_list_rest_call,
2054 headers=vca.vcloud_session.get_vcloud_headers(),
2055 verify=vca.verify,
2056 logger=vca.logger)
2057 if response.status_code == requests.codes.ok:
2058 return response.content
2059
2060 return None
2061
2062 def get_vcd_network(self, network_uuid=None):
2063 """
2064 Method retrieves available network from vCloud Director
2065
2066 Args:
2067 network_uuid - is VCD network UUID
2068
2069 Each element serialized as key : value pair
2070
2071 Following keys available for access. network_configuration['Gateway'}
2072 <Configuration>
2073 <IpScopes>
2074 <IpScope>
2075 <IsInherited>true</IsInherited>
2076 <Gateway>172.16.252.100</Gateway>
2077 <Netmask>255.255.255.0</Netmask>
2078 <Dns1>172.16.254.201</Dns1>
2079 <Dns2>172.16.254.202</Dns2>
2080 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2081 <IsEnabled>true</IsEnabled>
2082 <IpRanges>
2083 <IpRange>
2084 <StartAddress>172.16.252.1</StartAddress>
2085 <EndAddress>172.16.252.99</EndAddress>
2086 </IpRange>
2087 </IpRanges>
2088 </IpScope>
2089 </IpScopes>
2090 <FenceMode>bridged</FenceMode>
2091
2092 Returns:
2093 The return dictionary and key for each entry vapp UUID
2094 """
2095
2096 network_configuration = {}
2097 if network_uuid is None:
2098 return network_uuid
2099
2100 content = self.get_network_action(network_uuid=network_uuid)
2101 try:
2102 vm_list_xmlroot = XmlElementTree.fromstring(content)
2103
2104 network_configuration['status'] = vm_list_xmlroot.get("status")
2105 network_configuration['name'] = vm_list_xmlroot.get("name")
2106 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
2107
2108 for child in vm_list_xmlroot:
2109 if child.tag.split("}")[1] == 'IsShared':
2110 network_configuration['isShared'] = child.text.strip()
2111 if child.tag.split("}")[1] == 'Configuration':
2112 for configuration in child.iter():
2113 tagKey = configuration.tag.split("}")[1].strip()
2114 if tagKey != "":
2115 network_configuration[tagKey] = configuration.text.strip()
2116 return network_configuration
2117 except:
2118 pass
2119
2120 return network_configuration
2121
2122 def delete_network_action(self, network_uuid=None):
2123 """
2124 Method delete given network from vCloud director
2125
2126 Args:
2127 network_uuid - is a network uuid that client wish to delete
2128
2129 Returns:
2130 The return None or XML respond or false
2131 """
2132
2133 vca = self.connect_as_admin()
2134 if not vca:
2135 raise vimconn.vimconnConnectionException("self.connect() is failed")
2136 if network_uuid is None:
2137 return False
2138
2139 url_list = [vca.host, '/api/admin/network/', network_uuid]
2140 vm_list_rest_call = ''.join(url_list)
2141
2142 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2143 response = Http.delete(url=vm_list_rest_call,
2144 headers=vca.vcloud_session.get_vcloud_headers(),
2145 verify=vca.verify,
2146 logger=vca.logger)
2147
2148 if response.status_code == 202:
2149 return True
2150
2151 return False
2152
2153 def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2154 ip_profile=None, isshared='true'):
2155 """
2156 Method create network in vCloud director
2157
2158 Args:
2159 network_name - is network name to be created.
2160 net_type - can be 'bridge','data','ptp','mgmt'.
2161 ip_profile is a dict containing the IP parameters of the network
2162 isshared - is a boolean
2163 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2164 It optional attribute. by default if no parent network indicate the first available will be used.
2165
2166 Returns:
2167 The return network uuid or return None
2168 """
2169
2170 new_network_name = [network_name, '-', str(uuid.uuid4())]
2171 content = self.create_network_rest(network_name=''.join(new_network_name),
2172 ip_profile=ip_profile,
2173 net_type=net_type,
2174 parent_network_uuid=parent_network_uuid,
2175 isshared=isshared)
2176 if content is None:
2177 self.logger.debug("Failed create network {}.".format(network_name))
2178 return None
2179
2180 try:
2181 vm_list_xmlroot = XmlElementTree.fromstring(content)
2182 vcd_uuid = vm_list_xmlroot.get('id').split(":")
2183 if len(vcd_uuid) == 4:
2184 self.logger.info("Create new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
2185 return vcd_uuid[3]
2186 except:
2187 self.logger.debug("Failed create network {}".format(network_name))
2188 return None
2189
2190 def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2191 ip_profile=None, isshared='true'):
2192 """
2193 Method create network in vCloud director
2194
2195 Args:
2196 network_name - is network name to be created.
2197 net_type - can be 'bridge','data','ptp','mgmt'.
2198 ip_profile is a dict containing the IP parameters of the network
2199 isshared - is a boolean
2200 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2201 It optional attribute. by default if no parent network indicate the first available will be used.
2202
2203 Returns:
2204 The return network uuid or return None
2205 """
2206
2207 vca = self.connect_as_admin()
2208 if not vca:
2209 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2210 if network_name is None:
2211 return None
2212
2213 url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
2214 vm_list_rest_call = ''.join(url_list)
2215 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2216 response = Http.get(url=vm_list_rest_call,
2217 headers=vca.vcloud_session.get_vcloud_headers(),
2218 verify=vca.verify,
2219 logger=vca.logger)
2220
2221 provider_network = None
2222 available_networks = None
2223 add_vdc_rest_url = None
2224
2225 if response.status_code != requests.codes.ok:
2226 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2227 response.status_code))
2228 return None
2229 else:
2230 try:
2231 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2232 for child in vm_list_xmlroot:
2233 if child.tag.split("}")[1] == 'ProviderVdcReference':
2234 provider_network = child.attrib.get('href')
2235 # application/vnd.vmware.admin.providervdc+xml
2236 if child.tag.split("}")[1] == 'Link':
2237 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2238 and child.attrib.get('rel') == 'add':
2239 add_vdc_rest_url = child.attrib.get('href')
2240 except:
2241 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2242 self.logger.debug("Respond body {}".format(response.content))
2243 return None
2244
2245 # find pvdc provided available network
2246 response = Http.get(url=provider_network,
2247 headers=vca.vcloud_session.get_vcloud_headers(),
2248 verify=vca.verify,
2249 logger=vca.logger)
2250 if response.status_code != requests.codes.ok:
2251 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2252 response.status_code))
2253 return None
2254
2255 # available_networks.split("/")[-1]
2256
2257 if parent_network_uuid is None:
2258 try:
2259 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2260 for child in vm_list_xmlroot.iter():
2261 if child.tag.split("}")[1] == 'AvailableNetworks':
2262 for networks in child.iter():
2263 # application/vnd.vmware.admin.network+xml
2264 if networks.attrib.get('href') is not None:
2265 available_networks = networks.attrib.get('href')
2266 break
2267 except:
2268 return None
2269
2270 #Configure IP profile of the network
2271 ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
2272
2273 gateway_address=ip_profile['gateway_address']
2274 dhcp_count=int(ip_profile['dhcp_count'])
2275 subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
2276
2277 if ip_profile['dhcp_enabled']==True:
2278 dhcp_enabled='true'
2279 else:
2280 dhcp_enabled='false'
2281 dhcp_start_address=ip_profile['dhcp_start_address']
2282
2283 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2284 end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
2285 end_ip_int += dhcp_count - 1
2286 dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
2287
2288 ip_version=ip_profile['ip_version']
2289 dns_address=ip_profile['dns_address']
2290
2291 # either use client provided UUID or search for a first available
2292 # if both are not defined we return none
2293 if parent_network_uuid is not None:
2294 url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
2295 add_vdc_rest_url = ''.join(url_list)
2296
2297 if net_type=='ptp':
2298 fence_mode="isolated"
2299 isshared='false'
2300 is_inherited='false'
2301 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2302 <Description>Openmano created</Description>
2303 <Configuration>
2304 <IpScopes>
2305 <IpScope>
2306 <IsInherited>{1:s}</IsInherited>
2307 <Gateway>{2:s}</Gateway>
2308 <Netmask>{3:s}</Netmask>
2309 <Dns1>{4:s}</Dns1>
2310 <IsEnabled>{5:s}</IsEnabled>
2311 <IpRanges>
2312 <IpRange>
2313 <StartAddress>{6:s}</StartAddress>
2314 <EndAddress>{7:s}</EndAddress>
2315 </IpRange>
2316 </IpRanges>
2317 </IpScope>
2318 </IpScopes>
2319 <FenceMode>{8:s}</FenceMode>
2320 </Configuration>
2321 <IsShared>{9:s}</IsShared>
2322 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2323 subnet_address, dns_address, dhcp_enabled,
2324 dhcp_start_address, dhcp_end_address, fence_mode, isshared)
2325
2326 else:
2327 fence_mode="bridged"
2328 is_inherited='false'
2329 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2330 <Description>Openmano created</Description>
2331 <Configuration>
2332 <IpScopes>
2333 <IpScope>
2334 <IsInherited>{1:s}</IsInherited>
2335 <Gateway>{2:s}</Gateway>
2336 <Netmask>{3:s}</Netmask>
2337 <Dns1>{4:s}</Dns1>
2338 <IsEnabled>{5:s}</IsEnabled>
2339 <IpRanges>
2340 <IpRange>
2341 <StartAddress>{6:s}</StartAddress>
2342 <EndAddress>{7:s}</EndAddress>
2343 </IpRange>
2344 </IpRanges>
2345 </IpScope>
2346 </IpScopes>
2347 <ParentNetwork href="{8:s}"/>
2348 <FenceMode>{9:s}</FenceMode>
2349 </Configuration>
2350 <IsShared>{10:s}</IsShared>
2351 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2352 subnet_address, dns_address, dhcp_enabled,
2353 dhcp_start_address, dhcp_end_address, available_networks,
2354 fence_mode, isshared)
2355
2356 headers = vca.vcloud_session.get_vcloud_headers()
2357 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2358 try:
2359 response = Http.post(url=add_vdc_rest_url,
2360 headers=headers,
2361 data=data,
2362 verify=vca.verify,
2363 logger=vca.logger)
2364
2365 if response.status_code != 201:
2366 self.logger.debug("Create Network POST REST API call failed. Return status code {}"
2367 .format(response.status_code))
2368 else:
2369 network = networkType.parseString(response.content, True)
2370 create_nw_task = network.get_Tasks().get_Task()[0]
2371
2372 # if we all ok we respond with content after network creation completes
2373 # otherwise by default return None
2374 if create_nw_task is not None:
2375 self.logger.debug("Create Network REST : Waiting for Nw creation complete")
2376 status = vca.block_until_completed(create_nw_task)
2377 if status:
2378 return response.content
2379 else:
2380 self.logger.debug("create_network_rest task failed. Network Create response : {}"
2381 .format(response.content))
2382 except Exception as exp:
2383 self.logger.debug("create_network_rest : Exception : {} ".format(exp))
2384
2385 return None
2386
2387 def convert_cidr_to_netmask(self, cidr_ip=None):
2388 """
2389 Method sets convert CIDR netmask address to normal IP format
2390 Args:
2391 cidr_ip : CIDR IP address
2392 Returns:
2393 netmask : Converted netmask
2394 """
2395 if cidr_ip is not None:
2396 if '/' in cidr_ip:
2397 network, net_bits = cidr_ip.split('/')
2398 netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
2399 else:
2400 netmask = cidr_ip
2401 return netmask
2402 return None
2403
2404 def get_provider_rest(self, vca=None):
2405 """
2406 Method gets provider vdc view from vcloud director
2407
2408 Args:
2409 network_name - is network name to be created.
2410 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2411 It optional attribute. by default if no parent network indicate the first available will be used.
2412
2413 Returns:
2414 The return xml content of respond or None
2415 """
2416
2417 url_list = [vca.host, '/api/admin']
2418 response = Http.get(url=''.join(url_list),
2419 headers=vca.vcloud_session.get_vcloud_headers(),
2420 verify=vca.verify,
2421 logger=vca.logger)
2422
2423 if response.status_code == requests.codes.ok:
2424 return response.content
2425 return None
2426
2427 def create_vdc(self, vdc_name=None):
2428
2429 vdc_dict = {}
2430
2431 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
2432 if xml_content is not None:
2433 try:
2434 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
2435 for child in task_resp_xmlroot:
2436 if child.tag.split("}")[1] == 'Owner':
2437 vdc_id = child.attrib.get('href').split("/")[-1]
2438 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
2439 return vdc_dict
2440 except:
2441 self.logger.debug("Respond body {}".format(xml_content))
2442
2443 return None
2444
2445 def create_vdc_from_tmpl_rest(self, vdc_name=None):
2446 """
2447 Method create vdc in vCloud director based on VDC template.
2448 it uses pre-defined template that must be named openmano
2449
2450 Args:
2451 vdc_name - name of a new vdc.
2452
2453 Returns:
2454 The return xml content of respond or None
2455 """
2456
2457 self.logger.info("Creating new vdc {}".format(vdc_name))
2458 vca = self.connect()
2459 if not vca:
2460 raise vimconn.vimconnConnectionException("self.connect() is failed")
2461 if vdc_name is None:
2462 return None
2463
2464 url_list = [vca.host, '/api/vdcTemplates']
2465 vm_list_rest_call = ''.join(url_list)
2466 response = Http.get(url=vm_list_rest_call,
2467 headers=vca.vcloud_session.get_vcloud_headers(),
2468 verify=vca.verify,
2469 logger=vca.logger)
2470
2471 # container url to a template
2472 vdc_template_ref = None
2473 try:
2474 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2475 for child in vm_list_xmlroot:
2476 # application/vnd.vmware.admin.providervdc+xml
2477 # we need find a template from witch we instantiate VDC
2478 if child.tag.split("}")[1] == 'VdcTemplate':
2479 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
2480 'name') == 'openmano':
2481 vdc_template_ref = child.attrib.get('href')
2482 except:
2483 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2484 self.logger.debug("Respond body {}".format(response.content))
2485 return None
2486
2487 # if we didn't found required pre defined template we return None
2488 if vdc_template_ref is None:
2489 return None
2490
2491 try:
2492 # instantiate vdc
2493 url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
2494 vm_list_rest_call = ''.join(url_list)
2495 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2496 <Source href="{1:s}"></Source>
2497 <Description>opnemano</Description>
2498 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
2499 headers = vca.vcloud_session.get_vcloud_headers()
2500 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2501 response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
2502 logger=vca.logger)
2503 # if we all ok we respond with content otherwise by default None
2504 if response.status_code >= 200 and response.status_code < 300:
2505 return response.content
2506 return None
2507 except:
2508 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2509 self.logger.debug("Respond body {}".format(response.content))
2510
2511 return None
2512
2513 def create_vdc_rest(self, vdc_name=None):
2514 """
2515 Method create network in vCloud director
2516
2517 Args:
2518 network_name - is network name to be created.
2519 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2520 It optional attribute. by default if no parent network indicate the first available will be used.
2521
2522 Returns:
2523 The return network uuid or return None
2524 """
2525
2526 self.logger.info("Creating new vdc {}".format(vdc_name))
2527
2528 vca = self.connect_as_admin()
2529 if not vca:
2530 raise vimconn.vimconnConnectionException("self.connect() is failed")
2531 if vdc_name is None:
2532 return None
2533
2534 url_list = [vca.host, '/api/admin/org/', self.org_uuid]
2535 vm_list_rest_call = ''.join(url_list)
2536 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2537 response = Http.get(url=vm_list_rest_call,
2538 headers=vca.vcloud_session.get_vcloud_headers(),
2539 verify=vca.verify,
2540 logger=vca.logger)
2541
2542 provider_vdc_ref = None
2543 add_vdc_rest_url = None
2544 available_networks = None
2545
2546 if response.status_code != requests.codes.ok:
2547 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2548 response.status_code))
2549 return None
2550 else:
2551 try:
2552 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2553 for child in vm_list_xmlroot:
2554 # application/vnd.vmware.admin.providervdc+xml
2555 if child.tag.split("}")[1] == 'Link':
2556 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2557 and child.attrib.get('rel') == 'add':
2558 add_vdc_rest_url = child.attrib.get('href')
2559 except:
2560 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2561 self.logger.debug("Respond body {}".format(response.content))
2562 return None
2563
2564 response = self.get_provider_rest(vca=vca)
2565 try:
2566 vm_list_xmlroot = XmlElementTree.fromstring(response)
2567 for child in vm_list_xmlroot:
2568 if child.tag.split("}")[1] == 'ProviderVdcReferences':
2569 for sub_child in child:
2570 provider_vdc_ref = sub_child.attrib.get('href')
2571 except:
2572 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2573 self.logger.debug("Respond body {}".format(response))
2574 return None
2575
2576 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
2577 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2578 <AllocationModel>ReservationPool</AllocationModel>
2579 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2580 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2581 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2582 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2583 <ProviderVdcReference
2584 name="Main Provider"
2585 href="{2:s}" />
2586 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
2587 escape(vdc_name),
2588 provider_vdc_ref)
2589
2590 headers = vca.vcloud_session.get_vcloud_headers()
2591 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2592 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
2593 logger=vca.logger)
2594
2595 # if we all ok we respond with content otherwise by default None
2596 if response.status_code == 201:
2597 return response.content
2598 return None
2599
2600 def get_vapp_details_rest(self, vapp_uuid=None):
2601 """
2602 Method retrieve vapp detail from vCloud director
2603
2604 Args:
2605 vapp_uuid - is vapp identifier.
2606
2607 Returns:
2608 The return network uuid or return None
2609 """
2610
2611 parsed_respond = {}
2612
2613 vca = self.connect()
2614 if not vca:
2615 raise vimconn.vimconnConnectionException("self.connect() is failed")
2616 if vapp_uuid is None:
2617 return None
2618
2619 url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
2620 get_vapp_restcall = ''.join(url_list)
2621 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2622 response = Http.get(url=get_vapp_restcall,
2623 headers=vca.vcloud_session.get_vcloud_headers(),
2624 verify=vca.verify,
2625 logger=vca.logger)
2626
2627 if response.status_code != requests.codes.ok:
2628 self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
2629 response.status_code))
2630 return parsed_respond
2631
2632 try:
2633 xmlroot_respond = XmlElementTree.fromstring(response.content)
2634 parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
2635
2636 namespaces_ovf = {'ovf': 'http://schemas.dmtf.org/ovf/envelope/1'}
2637 namespace_vmm = {'vmw': 'http://www.vmware.com/schema/ovf'}
2638 namespace_vm = {'vm': 'http://www.vmware.com/vcloud/v1.5'}
2639
2640 created_section = xmlroot_respond.find('vm:DateCreated', namespace_vm)
2641 if created_section is not None:
2642 parsed_respond['created'] = created_section.text
2643
2644 network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespace_vm)
2645 if network_section is not None and 'networkName' in network_section.attrib:
2646 parsed_respond['networkname'] = network_section.attrib['networkName']
2647
2648 ipscopes_section = \
2649 xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2650 namespace_vm)
2651 if ipscopes_section is not None:
2652 for ipscope in ipscopes_section:
2653 for scope in ipscope:
2654 tag_key = scope.tag.split("}")[1]
2655 if tag_key == 'IpRanges':
2656 ip_ranges = scope.getchildren()
2657 for ipblock in ip_ranges:
2658 for block in ipblock:
2659 parsed_respond[block.tag.split("}")[1]] = block.text
2660 else:
2661 parsed_respond[tag_key] = scope.text
2662
2663 # parse children section for other attrib
2664 children_section = xmlroot_respond.find('vm:Children/', namespace_vm)
2665 if children_section is not None:
2666 parsed_respond['name'] = children_section.attrib['name']
2667 parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled']
2668 parsed_respond['deployed'] = children_section.attrib['deployed']
2669 parsed_respond['status'] = children_section.attrib['status']
2670 parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
2671 network_adapter = children_section.find('vm:NetworkConnectionSection', namespace_vm)
2672 nic_list = []
2673 for adapters in network_adapter:
2674 adapter_key = adapters.tag.split("}")[1]
2675 if adapter_key == 'PrimaryNetworkConnectionIndex':
2676 parsed_respond['primarynetwork'] = adapters.text
2677 if adapter_key == 'NetworkConnection':
2678 vnic = {}
2679 if 'network' in adapters.attrib:
2680 vnic['network'] = adapters.attrib['network']
2681 for adapter in adapters:
2682 setting_key = adapter.tag.split("}")[1]
2683 vnic[setting_key] = adapter.text
2684 nic_list.append(vnic)
2685
2686 for link in children_section:
2687 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
2688 if link.attrib['rel'] == 'screen:acquireTicket':
2689 parsed_respond['acquireTicket'] = link.attrib
2690 if link.attrib['rel'] == 'screen:acquireMksTicket':
2691 parsed_respond['acquireMksTicket'] = link.attrib
2692
2693 parsed_respond['interfaces'] = nic_list
2694 except:
2695 pass
2696
2697 return parsed_respond
2698
2699 def acuire_console(self, vm_uuid=None):
2700
2701 vca = self.connect()
2702 if not vca:
2703 raise vimconn.vimconnConnectionException("self.connect() is failed")
2704 if vm_uuid is None:
2705 return None
2706
2707 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2708 vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
2709 console_dict = vm_dict['acquireTicket']
2710 console_rest_call = console_dict['href']
2711
2712 response = Http.post(url=console_rest_call,
2713 headers=vca.vcloud_session.get_vcloud_headers(),
2714 verify=vca.verify,
2715 logger=vca.logger)
2716
2717 if response.status_code == requests.codes.ok:
2718 return response.content
2719
2720 return None
2721
2722