Fixes bug #57
[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 import os
29 import requests
30
31
32 from xml.etree import ElementTree as ET
33
34 from pyvcloud import Http
35 from pyvcloud.vcloudair import VCA
36 from pyvcloud.schema.vcd.v1_5.schemas.vcloud import sessionType, organizationType, \
37 vAppType, organizationListType, vdcType, catalogType, queryRecordViewType, \
38 networkType, vcloudType, taskType, diskType, vmsType, vdcTemplateListType, mediaType
39 from xml.sax.saxutils import escape
40
41 import logging
42 import json
43 import vimconn
44 import time
45 import uuid
46 import httplib
47
48
49 __author__="Mustafa Bayramov"
50 __date__ ="$26-Aug-2016 11:09:29$"
51
52
53 #Error variables
54 HTTP_Bad_Request = 400
55 HTTP_Unauthorized = 401
56 HTTP_Not_Found = 404
57 HTTP_Method_Not_Allowed = 405
58 HTTP_Request_Timeout = 408
59 HTTP_Conflict = 409
60 HTTP_Not_Implemented = 501
61 HTTP_Service_Unavailable = 503
62 HTTP_Internal_Server_Error = 500
63
64 class vimconnException(Exception):
65 '''Common and base class Exception for all vimconnector exceptions'''
66 def __init__(self, message, http_code=HTTP_Bad_Request):
67 Exception.__init__(self, message)
68 self.http_code = http_code
69
70 class vimconnConnectionException(vimconnException):
71 '''Connectivity error with the VIM'''
72 def __init__(self, message, http_code=HTTP_Service_Unavailable):
73 vimconnException.__init__(self, message, http_code)
74
75 class vimconnUnexpectedResponse(vimconnException):
76 '''Get an wrong response from VIM'''
77 def __init__(self, message, http_code=HTTP_Service_Unavailable):
78 vimconnException.__init__(self, message, http_code)
79
80 class vimconnAuthException(vimconnException):
81 '''Invalid credentials or authorization to perform this action over the VIM'''
82 def __init__(self, message, http_code=HTTP_Unauthorized):
83 vimconnException.__init__(self, message, http_code)
84
85 class vimconnNotFoundException(vimconnException):
86 '''The item is not found at VIM'''
87 def __init__(self, message, http_code=HTTP_Not_Found):
88 vimconnException.__init__(self, message, http_code)
89
90 class vimconnConflictException(vimconnException):
91 '''There is a conflict, e.g. more item found than one'''
92 def __init__(self, message, http_code=HTTP_Conflict):
93 vimconnException.__init__(self, message, http_code)
94
95 class vimconnNotImplemented(vimconnException):
96 '''The method is not implemented by the connected'''
97 def __init__(self, message, http_code=HTTP_Not_Implemented):
98 vimconnException.__init__(self, message, http_code)
99
100
101 flavorlist = {}
102
103 class vimconnector():
104 '''Vmware VIM Connector base class
105 '''
106 def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level="ERROR", config={}):
107 self.id = uuid
108 self.name = name
109 self.url = url
110 self.url_admin = url_admin
111 self.tenant_id = tenant_id
112 self.tenant_name = tenant_name
113 self.user = user
114 self.passwd = passwd
115 self.config = config
116 self.logger = logging.getLogger('openmano.vim.vmware')
117
118 # formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
119 # ch = logging.StreamHandler()
120 # ch.setLevel(log_level)
121 # ch.setFormatter(formatter)
122 # self.logger.addHandler(ch)
123 # self.logger.setLevel( getattr(logging, log_level))
124
125 # self.logger = logging.getLogger('mano.vim.vmware')
126
127 self.logger.debug("Vmware tenant from VIM filter: '%s'", user)
128 self.logger.debug("Vmware tenant from VIM filter: '%s'", passwd)
129
130 if not url:
131 raise TypeError, 'url param can not be NoneType'
132
133 if not self.url_admin: #try to use normal url
134 self.url_admin = self.url
135
136 self.vcaversion = '5.6'
137
138 print "Calling constructor with following paramters"
139 print " UUID: {} ".format(uuid)
140 print " name: {} ".format(name)
141 print " tenant_id: {} ".format(tenant_id)
142 print " tenant_name: {} ".format(tenant_name)
143 print " url: {} ".format(url)
144 print " url_admin: {} ".format(url_admin)
145 print " user: {} ".format(user)
146 print " passwd: {} ".format(passwd)
147 print " debug: {} ".format(log_level)
148
149 def __getitem__(self,index):
150 if index=='tenant_id':
151 return self.tenant_id
152 if index=='tenant_name':
153 return self.tenant_name
154 elif index=='id':
155 return self.id
156 elif index=='name':
157 return self.name
158 elif index=='user':
159 return self.user
160 elif index=='passwd':
161 return self.passwd
162 elif index=='url':
163 return self.url
164 elif index=='url_admin':
165 return self.url_admin
166 elif index=="config":
167 return self.config
168 else:
169 raise KeyError("Invalid key '%s'" %str(index))
170
171 def __setitem__(self,index, value):
172 if index=='tenant_id':
173 self.tenant_id = value
174 if index=='tenant_name':
175 self.tenant_name = value
176 elif index=='id':
177 self.id = value
178 elif index=='name':
179 self.name = value
180 elif index=='user':
181 self.user = value
182 elif index=='passwd':
183 self.passwd = value
184 elif index=='url':
185 self.url = value
186 elif index=='url_admin':
187 self.url_admin = value
188 else:
189 raise KeyError("Invalid key '%s'" %str(index))
190
191 def connect(self):
192
193 service_type = 'standalone'
194 version = '5.6'
195
196 self.logger.debug("Logging in to a VCA '%s'", self.name)
197
198 vca = VCA(host=self.url, username=self.user, service_type=service_type, version=version, verify=False, log=True)
199 result = vca.login(password=self.passwd, org=self.name)
200 if not result:
201 raise KeyError("Can't connect to a vCloud director.")
202 result = vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url)
203 if result is True:
204 self.logger.debug("Successfully logged to a VCA '%s'", self.name)
205
206 # vca = VCA(host='172.16.254.206', username=self.user, service_type='standalone', version='5.6', verify=False, log=True)
207 # vca.login(password=self.passwd, org=self.name)
208 # vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url)
209
210 # if not result:
211 # result = vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url)
212 # if not result:
213 # raise KeyError("Can't connect to a vcloud director")
214 # else:
215 # print "Logged to VCA via existing token"
216 # else:
217 # print "Logged to VCA"
218
219 return vca
220
221
222 def new_tenant(self,tenant_name,tenant_description):
223 '''Adds a new tenant to VIM with this name and description,
224 returns the tenant identifier'''
225 raise vimconnNotImplemented( "Should have implemented this" )
226
227 def delete_tenant(self,tenant_id,):
228 '''Delete a tenant from VIM'''
229 '''Returns the tenant identifier'''
230 raise vimconnNotImplemented( "Should have implemented this" )
231
232 def get_tenant_list(self, filter_dict={}):
233 '''Obtain tenants of VIM
234 filter_dict can contain the following keys:
235 name: filter by tenant name
236 id: filter by tenant uuid/id
237 <other VIM specific>
238 Returns the tenant list of dictionaries:
239 [{'name':'<name>, 'id':'<id>, ...}, ...]
240 '''
241 raise vimconnNotImplemented( "Should have implemented this" )
242
243 def new_network(self,net_name, net_type, ip_profile=None, shared=False):
244 '''Adds a tenant network to VIM
245 net_name is the name
246 net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised
247 ip_profile is a dict containing the IP parameters of the network
248 shared is a boolean
249 Returns the network identifier'''
250
251 self.logger.debug("Vmware tenant from VIM filter: '%s'", net_name)
252 self.logger.debug("Vmware tenant from VIM filter: '%s'", net_type)
253 self.logger.debug("Vmware tenant from VIM filter: '%s'", ip_profile)
254 self.logger.debug("Vmware tenant from VIM filter: '%s'", shared)
255
256 raise vimconnNotImplemented( "Should have implemented this" )
257
258 def get_network_list(self, filter_dict={}):
259 '''Obtain tenant networks of VIM
260 Filter_dict can be:
261 name: network name
262 id: network uuid
263 shared: boolean
264 tenant_id: tenant
265 admin_state_up: boolean
266 status: 'ACTIVE'
267 Returns the network list of dictionaries:
268 [{<the fields at Filter_dict plus some VIM specific>}, ...]
269 List can be empty
270 '''
271
272 vca = self.connect()
273 if not vca:
274 raise vimconn.vimconnConnectionException("self.connect() is failed")
275
276 vdc = vca.get_vdc(self.tenant_name)
277 vdcid = vdc.get_id().split(":")
278
279 networks = vca.get_networks(vdc.get_name())
280 network_list = []
281 for network in networks:
282 filter_dict = {}
283 netid = network.get_id().split(":")
284 self.logger.debug ("Adding {} to a list".format(netid[3]))
285 self.logger.debug ("VDC ID {} to a list".format(vdcid[3]))
286 self.logger.debug ("Network {} to a list".format(network.get_name()))
287
288 filter_dict["name"] = network.get_name()
289 filter_dict["id"] = netid[3]
290 filter_dict["shared"] = network.get_IsShared()
291 filter_dict["tenant_id"] = vdcid[3]
292 if network.get_status() == 1:
293 filter_dict["admin_state_up"] = True
294 else:
295 filter_dict["admin_state_up"] = False
296 filter_dict["status"] = "ACTIVE"
297 filter_dict["type"] = "bridge"
298 network_list.append(filter_dict)
299
300 self.logger.debug("Returning {}".format(network_list))
301 return network_list
302
303 def get_network(self, net_id):
304 '''Obtain network details of net_id VIM network'
305 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]'''
306 raise vimconnNotImplemented( "Should have implemented this" )
307
308 def delete_network(self, net_id):
309 '''Deletes a tenant network from VIM, provide the network id.
310 Returns the network identifier or raise an exception'''
311 raise vimconnNotImplemented( "Should have implemented this" )
312
313 def refresh_nets_status(self, net_list):
314 '''Get the status of the networks
315 Params: the list of network identifiers
316 Returns a dictionary with:
317 net_id: #VIM id of this network
318 status: #Mandatory. Text with one of:
319 # DELETED (not found at vim)
320 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
321 # OTHER (Vim reported other status not understood)
322 # ERROR (VIM indicates an ERROR status)
323 # ACTIVE, INACTIVE, DOWN (admin down),
324 # BUILD (on building process)
325 #
326 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
327 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
328
329 '''
330 raise vimconnNotImplemented( "Should have implemented this" )
331
332 def get_flavor(self, flavor_id):
333 '''Obtain flavor details from the VIM
334 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
335 '''
336
337 print " get_flavor contains {}".format(flavor_id)
338
339 vca = self.connect()
340 if not vca:
341 raise vimconn.vimconnConnectionException("self.connect() is failed.")
342
343 def new_flavor(self, flavor_data):
344 '''Adds a tenant flavor to VIM
345 flavor_data contains a dictionary with information, keys:
346 name: flavor name
347 ram: memory (cloud type) in MBytes
348 vpcus: cpus (cloud type)
349 extended: EPA parameters
350 - numas: #items requested in same NUMA
351 memory: number of 1G huge pages memory
352 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
353 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
354 - name: interface name
355 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
356 bandwidth: X Gbps; requested guarantee bandwidth
357 vpci: requested virtual PCI address
358 disk: disk size
359 is_public:
360
361
362
363 #TODO to concrete
364 Returns the flavor identifier'''
365
366 flavor_uuid = uuid.uuid4()
367 flavorlist[flavor_uuid] = flavor_data
368
369 print " new_flavor contains {}".format(flavor_data)
370 print " flavor list contains {}".format(flavorlist)
371
372 return flavor_uuid
373
374 def delete_flavor(self, flavor_id):
375 '''Deletes a tenant flavor from VIM identify by its id
376 Returns the used id or raise an exception'''
377 raise vimconnNotImplemented( "Should have implemented this" )
378
379 def new_image(self,image_dict):
380 '''
381 Adds a tenant image to VIM
382 Returns:
383 200, image-id if the image is created
384 <0, message if there is an error
385 '''
386
387 print " ################################################################### "
388 print " new_image contains {}".format(image_dict)
389 print " ################################################################### "
390
391
392 def delete_image(self, image_id):
393 '''Deletes a tenant image from VIM'''
394 '''Returns the HTTP response code and a message indicating details of the success or fail'''
395
396 print " ################################################################### "
397 print " delete_image contains {}".format(image_id)
398 print " ################################################################### "
399
400 raise vimconnNotImplemented( "Should have implemented this" )
401
402 def catalog_exists(self, catalog_name, catalogs):
403 for catalog in catalogs:
404 if catalog.name == catalog_name:
405 return True
406 return False
407
408 def create_vimcatalog(self, vca, catalog_name):
409 """Create Catalog entry in VIM"""
410 task = vca.create_catalog(catalog_name, catalog_name)
411 result = vca.block_until_completed(task)
412 if not result:
413 return False
414 catalogs = vca.get_catalogs()
415 return self.catalog_exists(catalog_name, catalogs)
416
417
418 def upload_ovf(self, vca, catalog_name, item_name, media_file_name, description='', display_progress=False,
419 chunk_bytes=128 * 1024):
420 """
421 Uploads a OVF file to a vCloud catalog
422
423 :param catalog_name: (str): The name of the catalog to upload the media.
424 :param item_name: (str): The name of the media file in the catalog.
425 :param media_file_name: (str): The name of the local media file to upload.
426 :return: (bool) True if the media file was successfully uploaded, false otherwise.
427 """
428 os.path.isfile(media_file_name)
429 statinfo = os.stat(media_file_name)
430 statinfo.st_size
431
432 # find a catalog entry where we upload OVF.
433 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
434 # status change.
435 # if VCD can parse OVF we upload VMDK file
436 for catalog in vca.get_catalogs():
437 if catalog_name != catalog.name:
438 continue
439 link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
440 link.get_rel() == 'add', catalog.get_Link())
441 assert len(link) == 1
442 data = """
443 <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>
444 """ % (escape(item_name), escape(description))
445 headers = vca.vcloud_session.get_vcloud_headers()
446 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
447 response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
448 if response.status_code == requests.codes.created:
449 catalogItem = ET.fromstring(response.content)
450 entity = [child for child in catalogItem if
451 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
452 href = entity.get('href')
453 template = href
454 response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
455 verify=vca.verify, logger=self.logger)
456
457 if response.status_code == requests.codes.ok:
458 media = mediaType.parseString(response.content, True)
459 link = filter(lambda link: link.get_rel() == 'upload:default', media.get_Files().get_File()[0].get_Link())[0]
460 headers = vca.vcloud_session.get_vcloud_headers()
461 headers['Content-Type'] = 'Content-Type text/xml'
462 response = Http.put(link.get_href(), data=open(media_file_name, 'rb'), headers=headers, verify=vca.verify,logger=self.logger)
463 if response.status_code != requests.codes.ok:
464 self.logger.debug("Failed create vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
465 return False
466
467 time.sleep(5)
468
469 self.logger.debug("Failed create vApp template for catalog name {} and image {}".
470 format(catalog_name, media_file_name))
471
472 # uploading VMDK file
473 # check status of OVF upload
474 response = Http.get(template, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify, logger=self.logger)
475 if response.status_code == requests.codes.ok:
476 media = mediaType.parseString(response.content, True)
477 link = filter(lambda link: link.get_rel() == 'upload:default', media.get_Files().get_File()[0].get_Link())[0]
478
479 # The OVF file and VMDK must be in a same directory
480 head, tail = os.path.split(media_file_name)
481 filevmdk = head + '/' + os.path.basename(link.get_href())
482
483 os.path.isfile(filevmdk)
484 statinfo = os.stat(filevmdk)
485
486 # TODO debug output remove it
487 #print media.get_Files().get_File()[0].get_Link()[0].get_href()
488 #print media.get_Files().get_File()[1].get_Link()[0].get_href()
489 #print link.get_href()
490
491 # in case first element is pointer to OVF.
492 hrefvmdk = link.get_href().replace("descriptor.ovf","Cirros-disk1.vmdk")
493
494 f = open(filevmdk, 'rb')
495 bytes_transferred = 0
496 while bytes_transferred < statinfo.st_size:
497 my_bytes = f.read(chunk_bytes)
498 if len(my_bytes) <= chunk_bytes:
499 headers = vca.vcloud_session.get_vcloud_headers()
500 headers['Content-Range'] = 'bytes %s-%s/%s' % (bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
501 headers['Content-Length'] = str(len(my_bytes))
502 response = Http.put(hrefvmdk, headers=headers, data=my_bytes, verify=vca.verify,logger=None)
503 if response.status_code == requests.codes.ok:
504 bytes_transferred += len(my_bytes)
505 self.logger.debug('transferred %s of %s bytes' % (str(bytes_transferred),
506 str(statinfo.st_size)))
507 else:
508 self.logger.debug('file upload failed with error: [%s] %s' % (response.status_code,
509 response.content))
510 return False
511 f.close()
512 return True
513 else:
514 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
515 format(catalog_name, media_file_name))
516 return False
517
518 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
519 return False
520
521 def upload_vimimage(self,vca, catalog_name, media_name, medial_file_name):
522 """Upload media file"""
523 return self.upload_ovf(vca, catalog_name, media_name.split(".")[0], medial_file_name, medial_file_name, True)
524
525 def get_catalogid(self, catalog_name, catalogs):
526 for catalog in catalogs:
527 if catalog.name == catalog_name:
528 print catalog.name
529 catalog_id = catalog.get_id().split(":")
530 return catalog_id[3]
531 return None
532
533 def get_catalogbyid(self, catalog_id, catalogs):
534 for catalog in catalogs:
535 catalogid = catalog.get_id().split(":")[3]
536 if catalogid == catalog_id:
537 return catalog.name
538 return None
539
540 def get_image_id_from_path(self, path):
541 '''Get the image id from image path in the VIM database'''
542 '''Returns:
543 0,"Image not found" if there are no images with that path
544 1,image-id if there is one image with that path
545 <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.)
546 '''
547
548 vca = self.connect()
549 if not vca:
550 raise vimconn.vimconnConnectionException("self.connect() is failed")
551
552 self.logger.debug("get_image_id_from_path path {}".format(path))
553
554 dirpath, filename = os.path.split(path)
555 flname, file_extension = os.path.splitext(path)
556 if file_extension != '.ovf':
557 self.logger.debug("Wrong file extension {}".format(file_extension))
558 return -1, "Wrong container. vCloud director supports only OVF."
559 catalog_name = os.path.splitext(filename)[0]
560
561 self.logger.debug("File name {} Catalog Name {} file path {}".format(filename, catalog_name, path))
562 self.logger.debug("Catalog name {}".format(catalog_name))
563
564 catalogs = vca.get_catalogs()
565 if len(catalogs) == 0:
566 self.logger.info("Creating new catalog entry {} in vcloud director".format(catalog_name))
567 result = self.create_vimcatalog(vca, catalog_name)
568 if not result:
569 return -1, "Failed create new catalog {} ".format(catalog_name)
570 result = self.upload_vimimage(vca, catalog_name, filename, path)
571 if not result:
572 return -1, "Failed create vApp template for catalog {} ".format(catalog_name)
573 return self.get_catalogid(catalog_name, vca.get_catalogs())
574 else:
575 for catalog in catalogs:
576 # search for existing catalog if we find same name we return ID
577 # TODO optimize this
578 if catalog.name == catalog_name:
579 self.logger.debug("Found existing catalog entry for {} catalog id {}".format(catalog_name, self.get_catalogid(catalog_name, catalogs)))
580 return self.get_catalogid(catalog_name, vca.get_catalogs())
581
582 # if we didn't find existing catalog we create a new one.
583 self.logger.debug("Creating new catalog entry".format(catalog_name))
584 result = self.create_vimcatalog(vca, catalog_name)
585 if not result:
586 return -1, "Failed create new catalog {} ".format(catalog_name)
587 result = self.upload_vimimage(vca, catalog_name, filename, path)
588 if not result:
589 return -1, "Failed create vApp template for catalog {} ".format(catalog_name)
590
591 return self.get_catalogid(catalog_name, vca.get_catalogs())
592
593 def get_vappid(self, vdc, vapp_name):
594 """ Take vdc object and vApp name and returns vapp uuid or None
595 """
596 #UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
597
598 try:
599 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
600 vdc.ResourceEntities.ResourceEntity)
601
602 if len(refs) == 1:
603 return refs[0].href.split("vapp")[1][1:]
604 except:
605 return None
606 return None
607
608 def get_vappbyid(self, vdc, vapp_id):
609 refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
610 vdc.ResourceEntities.ResourceEntity)
611 for ref in refs:
612 print ref.href
613
614 if len(refs) == 1:
615 return refs[0].href.split("vapp")[1][1:]
616
617 def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=None):
618 """Adds a VM instance to VIM
619 Params:
620 start: indicates if VM must start or boot in pause mode. Ignored
621 image_id,flavor_id: image and flavor uuid
622 net_list: list of interfaces, each one is a dictionary with:
623 name:
624 net_id: network uuid to connect
625 vpci: virtual vcpi to assign
626 model: interface model, virtio, e2000, ...
627 mac_address:
628 use: 'data', 'bridge', 'mgmt'
629 type: 'virtual', 'PF', 'VF', 'VFnotShared'
630 vim_id: filled/added by this function
631 cloud_config: can be a text script to be passed directly to cloud-init,
632 or an object to inject users and ssh keys with format:
633 key-pairs: [] list of keys to install to the default user
634 users: [{ name, key-pairs: []}] list of users to add with their key-pair
635 #TODO ip, security groups
636 Returns >=0, the instance identifier
637 <0, error_text
638 """
639
640 self.logger.info("Creating new instance for entry".format(name))
641 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
642 format(description, start, image_id, flavor_id, net_list, cloud_config))
643 vca = self.connect()
644 if not vca:
645 raise vimconn.vimconnConnectionException("self.connect() is failed.")
646
647 # TODO following attribute need be featched from flavor / OVF file must contain same data.
648 # task = self.vca.create_vapp(vdc_name, vapp_name, template, catalog,
649 # vm_name=vm_name,
650 # vm_cpus=cpu,
651 # vm_memory=memory)
652 #
653
654 catalogs = vca.get_catalogs()
655 #image upload creates template name as catalog name space Template.
656 templateName = self.get_catalogbyid(image_id, catalogs) + ' Template'
657 task = vca.create_vapp(self.tenant_name, name, templateName, self.get_catalogbyid(image_id,catalogs), vm_name=name)
658 if task is False:
659 return -1, " Failed deploy vApp {}".format(name)
660
661 result = vca.block_until_completed(task)
662 if result:
663 vappID = self.get_vappid(vca.get_vdc(self.tenant_name), name)
664 if vappID is None:
665 return -1, " Failed featch UUID for vApp {}".format(name)
666 else:
667 return vappID
668
669 return -1, " Failed create vApp {}".format(name)
670
671 def get_vminstance(self,vm_id):
672 '''Returns the VM instance information from VIM'''
673
674 vca = self.connect()
675 if not vca:
676 raise vimconn.vimconnConnectionException("self.connect() is failed.")
677
678
679 raise vimconnNotImplemented( "Should have implemented this" )
680
681 def delete_vminstance(self, vm_id):
682 '''Removes a VM instance from VIM'''
683 '''Returns the instance identifier'''
684
685 print " ###### {} ".format(vm_id)
686
687 vca = self.connect()
688 if not vca:
689 raise vimconn.vimconnConnectionException("self.connect() is failed.")
690
691 thevdc = vca.get_vdc(self.tenant_name)
692 self.get_vappid(vca.get_vdc(self.tenant_name), name)
693
694
695
696
697 def refresh_vms_status(self, vm_list):
698 '''Get the status of the virtual machines and their interfaces/ports
699 Params: the list of VM identifiers
700 Returns a dictionary with:
701 vm_id: #VIM id of this Virtual Machine
702 status: #Mandatory. Text with one of:
703 # DELETED (not found at vim)
704 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
705 # OTHER (Vim reported other status not understood)
706 # ERROR (VIM indicates an ERROR status)
707 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
708 # CREATING (on building process), ERROR
709 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
710 #
711 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
712 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
713 interfaces:
714 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
715 mac_address: #Text format XX:XX:XX:XX:XX:XX
716 vim_net_id: #network id where this interface is connected
717 vim_interface_id: #interface/port VIM id
718 ip_address: #null, or text with IPv4, IPv6 address
719 '''
720 raise vimconnNotImplemented( "Should have implemented this" )
721
722 def action_vminstance(self, vm_id, action_dict):
723 '''Send and action over a VM instance from VIM
724 Returns the vm_id if the action was successfully sent to the VIM'''
725 raise vimconnNotImplemented( "Should have implemented this" )
726
727 def get_vminstance_console(self,vm_id, console_type="vnc"):
728 '''
729 Get a console for the virtual machine
730 Params:
731 vm_id: uuid of the VM
732 console_type, can be:
733 "novnc" (by default), "xvpvnc" for VNC types,
734 "rdp-html5" for RDP types, "spice-html5" for SPICE types
735 Returns dict with the console parameters:
736 protocol: ssh, ftp, http, https, ...
737 server: usually ip address
738 port: the http, ssh, ... port
739 suffix: extra text, e.g. the http path and query string
740 '''
741 raise vimconnNotImplemented( "Should have implemented this" )
742
743 #NOT USED METHODS in current version
744
745 def host_vim2gui(self, host, server_dict):
746 '''Transform host dictionary from VIM format to GUI format,
747 and append to the server_dict
748 '''
749 raise vimconnNotImplemented( "Should have implemented this" )
750
751 def get_hosts_info(self):
752 '''Get the information of deployed hosts
753 Returns the hosts content'''
754 raise vimconnNotImplemented( "Should have implemented this" )
755
756 def get_hosts(self, vim_tenant):
757 '''Get the hosts and deployed instances
758 Returns the hosts content'''
759 raise vimconnNotImplemented( "Should have implemented this" )
760
761 def get_processor_rankings(self):
762 '''Get the processor rankings in the VIM database'''
763 raise vimconnNotImplemented( "Should have implemented this" )
764
765 def new_host(self, host_data):
766 '''Adds a new host to VIM'''
767 '''Returns status code of the VIM response'''
768 raise vimconnNotImplemented( "Should have implemented this" )
769
770 def new_external_port(self, port_data):
771 '''Adds a external port to VIM'''
772 '''Returns the port identifier'''
773 raise vimconnNotImplemented( "Should have implemented this" )
774
775 def new_external_network(self,net_name,net_type):
776 '''Adds a external network to VIM (shared)'''
777 '''Returns the network identifier'''
778 raise vimconnNotImplemented( "Should have implemented this" )
779
780 def connect_port_network(self, port_id, network_id, admin=False):
781 '''Connects a external port to a network'''
782 '''Returns status code of the VIM response'''
783 raise vimconnNotImplemented( "Should have implemented this" )
784
785 def new_vminstancefromJSON(self, vm_data):
786 '''Adds a VM instance to VIM'''
787 '''Returns the instance identifier'''
788 raise vimconnNotImplemented( "Should have implemented this" )
789