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