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