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