191a06f42011d308814ded0e2c449ca5b0075969
[osm/SO.git] / rwcal / plugins / vala / rwcal_openstack / rift / rwcal / openstack / openstack_drv.py
1 #!/usr/bin/python
2
3 #
4 # Copyright 2016 RIFT.IO Inc
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18
19 import json
20 import logging
21 import ipaddress
22
23 from keystoneclient import v3 as ksclientv3
24 from keystoneclient.v2_0 import client as ksclientv2
25 from novaclient import client as nova_client
26 from neutronclient.neutron import client as ntclient
27 from glanceclient.v2 import client as glclient
28 from ceilometerclient import client as ceilo_client
29 from cinderclient.v2 import client as cinder_client
30
31 # Exceptions
32 import novaclient.exceptions as NovaException
33 import keystoneclient.exceptions as KeystoneExceptions
34 import neutronclient.common.exceptions as NeutronException
35 import glanceclient.exc as GlanceException
36 import cinderclient.exceptions as CinderException
37
38 logger = logging.getLogger('rwcal.openstack.drv')
39 logger.setLevel(logging.DEBUG)
40
41 class ValidationError(Exception):
42 pass
43
44
45 class KeystoneDriver(object):
46 """
47 Driver base-class for keystoneclient APIs
48 """
49 def __init__(self, ksclient):
50 """
51 Constructor for KeystoneDriver base class
52 Arguments: None
53 Returns: None
54 """
55 self.ksclient = ksclient
56
57 def get_username(self):
58 """
59 Returns the username associated with keystoneclient connection
60 """
61 return self._username
62
63 def get_password(self):
64 """
65 Returns the password associated with keystoneclient connection
66 """
67 return self._password
68
69 def get_tenant_name(self):
70 """
71 Returns the tenant name associated with keystoneclient connection
72 """
73 return self._tenant_name
74
75 def get_user_domain_name(self):
76 """
77 Returns None as this field does not exist for v2.
78 """
79 return None;
80
81 def get_project_domain_name(self):
82 """
83 Returns None as this field does not exist for v2.
84 """
85 return None;
86
87 def _get_keystone_connection(self):
88 """
89 Returns object of class python-keystoneclient class
90 """
91 if not hasattr(self, '_keystone_connection'):
92 self._keystone_connection = self.ksclient(**self._get_keystone_credentials())
93 return self._keystone_connection
94
95 def is_auth_token_valid(self, token_expiry, time_fmt):
96 """
97 Performs validity on auth_token
98 Arguments:
99 token_expiry (string): Expiry time for token
100 time_fmt (string) : Format for expiry string in auth_ref
101
102 Returns:
103 True/False (Boolean): (auth_token is valid or auth_token is invalid)
104 """
105 import time
106 import datetime
107 import dateutil.parser
108 try:
109 now = datetime.datetime.timetuple(datetime.datetime.utcnow())
110 expires_at = dateutil.parser.parse(token_expiry)
111 t_now = time.mktime(now)
112 t_expiry = time.mktime(expires_at.timetuple())
113
114 if (t_expiry <= t_now) or ((t_expiry - t_now) < 300 ):
115 ### Token has expired or about to expire (5 minute)
116 delattr(self, '_keystone_connection')
117 return False
118 else:
119 return True
120 except Exception as e:
121 logger.error("Received except %s during auth_token validity check" %str(e))
122 logger.info("Can not validate the auth_token. Assuming invalid")
123 return False
124
125
126 def get_service_endpoint(self, service_type, endpoint_type):
127 """
128 Returns requested type of endpoint for requested service type
129 Arguments:
130 service_type (string): Service Type (e.g. computev3, image, network)
131 endpoint_type(string): Endpoint Type (e.g. publicURL,adminURL,internalURL)
132 Returns:
133 service_endpoint(string): Service endpoint string
134 """
135 endpoint_kwargs = {'service_type' : service_type,
136 'endpoint_type' : endpoint_type}
137 try:
138 ksconn = self._get_keystone_connection()
139 service_endpoint = ksconn.service_catalog.url_for(**endpoint_kwargs)
140 except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure) as e:
141 raise
142 except Exception as e:
143 logger.error("OpenstackDriver: Service Catalog discovery operation failed for service_type: %s, endpoint_type: %s. Exception: %s" %(service_type, endpoint_type, str(e)))
144 raise
145 return service_endpoint
146
147
148 def get_raw_token(self):
149 """
150 Returns a valid raw_auth_token string
151
152 Returns (string): raw_auth_token string
153 """
154 ksconn = self._get_keystone_connection()
155 try:
156 raw_token = ksconn.get_raw_token_from_identity_service(auth_url = self._auth_url,
157 token = self.get_auth_token())
158 except KeystoneExceptions.AuthorizationFailure as e:
159 logger.error("OpenstackDriver: get_raw_token_from_identity_service Failure. Exception: %s" %(str(e)))
160 return None
161
162 except Exception as e:
163 logger.error("OpenstackDriver: Could not retrieve raw_token. Exception: %s" %(str(e)))
164
165 return raw_token
166
167 def get_tenant_id(self):
168 """
169 Returns tenant_id for the project/tenant. Tenant name is provided during
170 class instantiation
171
172 Returns (string): Tenant ID
173 """
174 ksconn = self._get_keystone_connection()
175 return ksconn.tenant_id
176
177 def get_security_mode(self):
178 """
179 Returns certificate_validation policy in case of SSL/TLS connection.
180 This policy is provided during class instantiation
181
182 Returns (boolean):
183 The boolean returned are designed to match the python-client class instantiation ("insecure") value.
184 for nova/neutron/glance/keystone clients
185
186 True: No certificate validation required -- Insecure mode
187 False: Certificate validation required -- Secure mode
188 """
189 return self._insecure
190
191 def tenant_list(self):
192 """
193 Returns list of tenants
194 """
195 pass
196
197 def tenant_create(self, name):
198 """
199 Create a new tenant
200 """
201 pass
202
203 def tenant_delete(self, tenant_id):
204 """
205 Deletes a tenant identified by tenant_id
206 """
207 pass
208
209 def roles_list(self):
210 pass
211
212 def roles_create(self):
213 pass
214
215 def roles_delete(self):
216 pass
217
218 class KeystoneDriverV2(KeystoneDriver):
219 """
220 Driver class for keystoneclient V2 APIs
221 """
222 def __init__(self, username, password, auth_url,tenant_name, insecure, region):
223 """
224 Constructor for KeystoneDriverV3 class
225 Arguments:
226 username (string) : Username
227 password (string) : Password
228 auth_url (string) : Authentication URL
229 tenant_name(string): Tenant Name
230 region (string) : Region name
231 Returns: None
232 """
233 self._username = username
234 self._password = password
235 self._auth_url = auth_url
236 self._tenant_name = tenant_name
237 self._insecure = insecure
238 self._region = region
239 super(KeystoneDriverV2, self).__init__(ksclientv2.Client)
240
241 def _get_keystone_credentials(self):
242 """
243 Returns the dictionary of kwargs required to instantiate python-keystoneclient class
244 """
245 creds = {}
246 #creds['user_domain'] = self._domain_name
247 creds['username'] = self._username
248 creds['password'] = self._password
249 creds['auth_url'] = self._auth_url
250 creds['tenant_name'] = self._tenant_name
251 creds['insecure'] = self.get_security_mode()
252 creds['region_name'] = self._region
253 return creds
254
255 def get_auth_token(self):
256 """
257 Returns a valid auth_token
258
259 Returns (string): auth_token string
260 """
261 ksconn = self._get_keystone_connection()
262 return ksconn.auth_token
263
264 def is_auth_token_valid(self):
265 """
266 Performs validity on auth_token
267 Arguments:
268
269 Returns:
270 True/False (Boolean): (auth_token is valid or auth_token is invalid)
271 """
272 ksconn = self._get_keystone_connection()
273 result = super(KeystoneDriverV2, self).is_auth_token_valid(ksconn.auth_ref['token']['expires'],
274 "%Y-%m-%dT%H:%M:%SZ")
275 return result
276
277
278 class KeystoneDriverV3(KeystoneDriver):
279 """
280 Driver class for keystoneclient V3 APIs
281 """
282 def __init__(self, username,
283 password,
284 auth_url,
285 tenant_name,
286 insecure,
287 user_domain_name = None,
288 project_domain_name = None,
289 region = None):
290 """
291 Constructor for KeystoneDriverV3 class
292 Arguments:
293 username (string) : Username
294 password (string) : Password
295 auth_url (string) : Authentication URL
296 tenant_name(string): Tenant Name
297 user_domain_name (string) : User domain name
298 project_domain_name (string): Project domain name
299 region (string) : Region name
300 Returns: None
301 """
302 self._username = username
303 self._password = password
304 self._auth_url = auth_url
305 self._tenant_name = tenant_name
306 self._insecure = insecure
307 self._user_domain_name = user_domain_name
308 self._project_domain_name = project_domain_name
309 self._region = region
310 super(KeystoneDriverV3, self).__init__(ksclientv3.Client)
311
312 def _get_keystone_credentials(self):
313 """
314 Returns the dictionary of kwargs required to instantiate python-keystoneclient class
315 """
316 creds = {}
317 creds['username'] = self._username
318 creds['password'] = self._password
319 creds['auth_url'] = self._auth_url
320 creds['project_name'] = self._tenant_name
321 creds['insecure'] = self._insecure
322 creds['user_domain_name'] = self._user_domain_name
323 creds['project_domain_name'] = self._project_domain_name
324 creds['region_name'] = self._region
325 return creds
326
327 def get_user_domain_name(self):
328 """
329 Returns the domain_name of the associated OpenStack user account
330 """
331 return self._user_domain_name;
332
333 def get_project_domain_name(self):
334 """
335 Returns the domain_name of the associated OpenStack project
336 """
337 return self._project_domain_name;
338
339 def get_auth_token(self):
340 """
341 Returns a valid auth_token
342
343 Returns (string): auth_token string
344 """
345 ksconn = self._get_keystone_connection()
346 return ksconn.auth_ref['auth_token']
347
348 def is_auth_token_valid(self):
349 """
350 Performs validity on auth_token
351 Arguments:
352
353 Returns:
354 True/False (Boolean): (auth_token is valid or auth_token is invalid)
355 """
356 ksconn = self._get_keystone_connection()
357 result = super(KeystoneDriverV3, self).is_auth_token_valid(ksconn.auth_ref['expires_at'],
358 "%Y-%m-%dT%H:%M:%S.%fZ")
359 return result
360
361 class NovaDriver(object):
362 """
363 Driver for openstack nova_client
364 """
365 def __init__(self, ks_drv, service_name, version):
366 """
367 Constructor for NovaDriver
368 Arguments: KeystoneDriver class object
369 """
370 self.ks_drv = ks_drv
371 self._service_name = service_name
372 self._version = version
373
374 def _get_nova_credentials(self):
375 """
376 Returns a dictionary of kwargs required to instantiate python-novaclient class
377 """
378 creds = {}
379 creds['version'] = self._version
380 creds['bypass_url'] = self.ks_drv.get_service_endpoint(self._service_name, "publicURL")
381 creds['username'] = self.ks_drv.get_username()
382 creds['project_id'] = self.ks_drv.get_tenant_name()
383 creds['auth_token'] = self.ks_drv.get_auth_token()
384 creds['insecure'] = self.ks_drv.get_security_mode()
385 #creds['user_domain_name'] = self.ks_drv.get_user_domain_name()
386 #creds['project_domain_name'] = self.ks_drv.get_project_domain_name()
387
388 return creds
389
390 def _get_nova_connection(self):
391 """
392 Returns an object of class python-novaclient
393 """
394 if not hasattr(self, '_nova_connection'):
395 self._nova_connection = nova_client.Client(**self._get_nova_credentials())
396 else:
397 # Reinitialize if auth_token is no longer valid
398 if not self.ks_drv.is_auth_token_valid():
399 self._nova_connection = nova_client.Client(**self._get_nova_credentials())
400 return self._nova_connection
401
402 def _flavor_get(self, flavor_id):
403 """
404 Get flavor by flavor_id
405 Arguments:
406 flavor_id(string): UUID of flavor_id
407
408 Returns:
409 dictionary of flavor parameters
410 """
411 nvconn = self._get_nova_connection()
412 try:
413 flavor = nvconn.flavors.get(flavor_id)
414 except Exception as e:
415 logger.info("OpenstackDriver: Did not find flavor with flavor_id : %s. Exception: %s"%(flavor_id, str(e)))
416 raise
417
418 try:
419 extra_specs = flavor.get_keys()
420 except Exception as e:
421 logger.info("OpenstackDriver: Could not get the EPA attributes for flavor with flavor_id : %s. Exception: %s"%(flavor_id, str(e)))
422 raise
423
424 response = flavor.to_dict()
425 assert 'extra_specs' not in response, "Key extra_specs present as flavor attribute"
426 response['extra_specs'] = extra_specs
427 return response
428
429 def flavor_get(self, flavor_id):
430 """
431 Get flavor by flavor_id
432 Arguments:
433 flavor_id(string): UUID of flavor_id
434
435 Returns:
436 dictionary of flavor parameters
437 """
438 return self._flavor_get(flavor_id)
439
440 def flavor_list(self):
441 """
442 Returns list of all flavors (dictionary per flavor)
443
444 Arguments:
445 None
446 Returns:
447 A list of dictionaries. Each dictionary contains attributes for a single flavor instance
448 """
449 flavors = []
450 flavor_info = []
451 nvconn = self._get_nova_connection()
452 try:
453 flavors = nvconn.flavors.list()
454 except Exception as e:
455 logger.error("OpenstackDriver: List Flavor operation failed. Exception: %s"%(str(e)))
456 raise
457 if flavors:
458 flavor_info = [ self.flavor_get(flv.id) for flv in flavors ]
459 return flavor_info
460
461 def flavor_create(self, name, ram, vcpu, disk, extra_specs):
462 """
463 Create a new flavor
464
465 Arguments:
466 name (string): Name of the new flavor
467 ram (int) : Memory in MB
468 vcpus (int) : Number of VCPUs
469 disk (int) : Secondary storage size in GB
470 extra_specs (dictionary): EPA attributes dictionary
471
472 Returns:
473 flavor_id (string): UUID of flavor created
474 """
475 nvconn = self._get_nova_connection()
476 try:
477 flavor = nvconn.flavors.create(name = name,
478 ram = ram,
479 vcpus = vcpu,
480 disk = disk,
481 flavorid = 'auto',
482 ephemeral = 0,
483 swap = 0,
484 rxtx_factor = 1.0,
485 is_public = True)
486 except Exception as e:
487 logger.error("OpenstackDriver: Create Flavor operation failed. Exception: %s"%(str(e)))
488 raise
489
490 if extra_specs:
491 try:
492 flavor.set_keys(extra_specs)
493 except Exception as e:
494 logger.error("OpenstackDriver: Set Key operation failed for flavor: %s. Exception: %s" %(flavor.id, str(e)))
495 raise
496 return flavor.id
497
498 def flavor_delete(self, flavor_id):
499 """
500 Deletes a flavor identified by flavor_id
501
502 Arguments:
503 flavor_id (string): UUID of flavor to be deleted
504
505 Returns: None
506 """
507 assert flavor_id == self._flavor_get(flavor_id)['id']
508 nvconn = self._get_nova_connection()
509 try:
510 nvconn.flavors.delete(flavor_id)
511 except Exception as e:
512 logger.error("OpenstackDriver: Delete flavor operation failed for flavor: %s. Exception: %s" %(flavor_id, str(e)))
513 raise
514
515
516 def server_list(self):
517 """
518 Returns a list of available VMs for the project
519
520 Arguments: None
521
522 Returns:
523 A list of dictionaries. Each dictionary contains attributes associated
524 with individual VM
525 """
526 servers = []
527 server_info = []
528 nvconn = self._get_nova_connection()
529 try:
530 servers = nvconn.servers.list()
531 except Exception as e:
532 logger.error("OpenstackDriver: List Server operation failed. Exception: %s" %(str(e)))
533 raise
534 server_info = [ server.to_dict() for server in servers]
535 return server_info
536
537 def _nova_server_get(self, server_id):
538 """
539 Returns a dictionary of attributes associated with VM identified by service_id
540
541 Arguments:
542 server_id (string): UUID of the VM/server for which information is requested
543
544 Returns:
545 A dictionary object with attributes associated with VM identified by server_id
546 """
547 nvconn = self._get_nova_connection()
548 try:
549 server = nvconn.servers.get(server = server_id)
550 except Exception as e:
551 logger.info("OpenstackDriver: Get Server operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
552 raise
553 else:
554 return server.to_dict()
555
556 def server_get(self, server_id):
557 """
558 Returns a dictionary of attributes associated with VM identified by service_id
559
560 Arguments:
561 server_id (string): UUID of the VM/server for which information is requested
562
563 Returns:
564 A dictionary object with attributes associated with VM identified by server_id
565 """
566 return self._nova_server_get(server_id)
567
568 def server_create(self, **kwargs):
569 """
570 Creates a new VM/server instance
571
572 Arguments:
573 A dictionary of following key-value pairs
574 {
575 server_name(string) : Name of the VM/Server
576 flavor_id (string) : UUID of the flavor to be used for VM
577 image_id (string) : UUID of the image to be used VM/Server instance,
578 This could be None if volumes (with images) are being used
579 network_list(List) : A List of network_ids. A port will be created in these networks
580 port_list (List) : A List of port-ids. These ports will be added to VM.
581 metadata (dict) : A dictionary of arbitrary key-value pairs associated with VM/server
582 userdata (string) : A script which shall be executed during first boot of the VM
583 availability_zone (string) : A name of the availability zone where instance should be launched
584 scheduler_hints (string) : Openstack scheduler_hints to be passed to nova scheduler
585 }
586 Returns:
587 server_id (string): UUID of the VM/server created
588
589 """
590 nics = []
591 if 'network_list' in kwargs:
592 for network_id in kwargs['network_list']:
593 nics.append({'net-id': network_id})
594
595 if 'port_list' in kwargs:
596 for port_id in kwargs['port_list']:
597 nics.append({'port-id': port_id})
598
599 nvconn = self._get_nova_connection()
600
601
602 try:
603 server = nvconn.servers.create(kwargs['name'],
604 kwargs['image_id'],
605 kwargs['flavor_id'],
606 meta = kwargs['metadata'],
607 files = kwargs['files'],
608 reservation_id = None,
609 min_count = None,
610 max_count = None,
611 userdata = kwargs['userdata'],
612 security_groups = kwargs['security_groups'],
613 availability_zone = kwargs['availability_zone'],
614 block_device_mapping_v2 = kwargs['block_device_mapping_v2'],
615 nics = nics,
616 scheduler_hints = kwargs['scheduler_hints'],
617 config_drive = kwargs['config_drive'])
618 except Exception as e:
619 logger.info("OpenstackDriver: Create Server operation failed. Exception: %s" %(str(e)))
620 raise
621 return server.to_dict()['id']
622
623 def server_delete(self, server_id):
624 """
625 Deletes a server identified by server_id
626
627 Arguments:
628 server_id (string): UUID of the server to be deleted
629
630 Returns: None
631 """
632 nvconn = self._get_nova_connection()
633 try:
634 nvconn.servers.delete(server_id)
635 except Exception as e:
636 logger.error("OpenstackDriver: Delete server operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
637 raise
638
639 def server_start(self, server_id):
640 """
641 Starts a server identified by server_id
642
643 Arguments:
644 server_id (string): UUID of the server to be started
645
646 Returns: None
647 """
648 nvconn = self._get_nova_connection()
649 try:
650 nvconn.servers.start(server_id)
651 except Exception as e:
652 logger.error("OpenstackDriver: Start Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
653 raise
654
655 def server_stop(self, server_id):
656 """
657 Arguments:
658 server_id (string): UUID of the server to be stopped
659
660 Returns: None
661 """
662 nvconn = self._get_nova_connection()
663 try:
664 nvconn.servers.stop(server_id)
665 except Exception as e:
666 logger.error("OpenstackDriver: Stop Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
667 raise
668
669 def server_pause(self, server_id):
670 """
671 Arguments:
672 server_id (string): UUID of the server to be paused
673
674 Returns: None
675 """
676 nvconn = self._get_nova_connection()
677 try:
678 nvconn.servers.pause(server_id)
679 except Exception as e:
680 logger.error("OpenstackDriver: Pause Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
681 raise
682
683 def server_unpause(self, server_id):
684 """
685 Arguments:
686 server_id (string): UUID of the server to be unpaused
687
688 Returns: None
689 """
690 nvconn = self._get_nova_connection()
691 try:
692 nvconn.servers.unpause(server_id)
693 except Exception as e:
694 logger.error("OpenstackDriver: Resume Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
695 raise
696
697
698 def server_suspend(self, server_id):
699 """
700 Arguments:
701 server_id (string): UUID of the server to be suspended
702
703 Returns: None
704 """
705 nvconn = self._get_nova_connection()
706 try:
707 nvconn.servers.suspend(server_id)
708 except Exception as e:
709 logger.error("OpenstackDriver: Suspend Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
710
711
712 def server_resume(self, server_id):
713 """
714 Arguments:
715 server_id (string): UUID of the server to be resumed
716
717 Returns: None
718 """
719 nvconn = self._get_nova_connection()
720 try:
721 nvconn.servers.resume(server_id)
722 except Exception as e:
723 logger.error("OpenstackDriver: Resume Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
724 raise
725
726 def server_reboot(self, server_id, reboot_type):
727 """
728 Arguments:
729 server_id (string) : UUID of the server to be rebooted
730 reboot_type(string):
731 'SOFT': Soft Reboot
732 'HARD': Hard Reboot
733 Returns: None
734 """
735 nvconn = self._get_nova_connection()
736 try:
737 nvconn.servers.reboot(server_id, reboot_type)
738 except Exception as e:
739 logger.error("OpenstackDriver: Reboot Server operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
740 raise
741
742 def server_console(self, server_id, console_type = 'novnc'):
743 """
744 Arguments:
745 server_id (string) : UUID of the server to be rebooted
746 console_type(string):
747 'novnc',
748 'xvpvnc'
749 Returns:
750 A dictionary object response for console information
751 """
752 nvconn = self._get_nova_connection()
753 try:
754 console_info = nvconn.servers.get_vnc_console(server_id, console_type)
755 except Exception as e:
756 logger.error("OpenstackDriver: Server Get-Console operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
757 raise
758 return console_info
759
760 def server_rebuild(self, server_id, image_id):
761 """
762 Arguments:
763 server_id (string) : UUID of the server to be rebooted
764 image_id (string) : UUID of the image to use
765 Returns: None
766 """
767
768 nvconn = self._get_nova_connection()
769 try:
770 nvconn.servers.rebuild(server_id, image_id)
771 except Exception as e:
772 logger.error("OpenstackDriver: Rebuild Server operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
773 raise
774
775
776 def server_add_port(self, server_id, port_id):
777 """
778 Arguments:
779 server_id (string): UUID of the server
780 port_id (string): UUID of the port to be attached
781
782 Returns: None
783 """
784 nvconn = self._get_nova_connection()
785 try:
786 nvconn.servers.interface_attach(server_id,
787 port_id,
788 net_id = None,
789 fixed_ip = None)
790 except Exception as e:
791 logger.error("OpenstackDriver: Server Port Add operation failed for server_id : %s, port_id : %s. Exception: %s" %(server_id, port_id, str(e)))
792 raise
793
794 def server_delete_port(self, server_id, port_id):
795 """
796 Arguments:
797 server_id (string): UUID of the server
798 port_id (string): UUID of the port to be deleted
799 Returns: None
800
801 """
802 nvconn = self._get_nova_connection()
803 try:
804 nvconn.servers.interface_detach(server_id, port_id)
805 except Exception as e:
806 logger.error("OpenstackDriver: Server Port Delete operation failed for server_id : %s, port_id : %s. Exception: %s" %(server_id, port_id, str(e)))
807 raise
808
809 def floating_ip_list(self):
810 """
811 Arguments:
812 None
813 Returns:
814 List of objects of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
815 """
816 nvconn = self._get_nova_connection()
817 try:
818 ip_list = nvconn.floating_ips.list()
819 except Exception as e:
820 logger.error("OpenstackDriver: Floating IP List operation failed. Exception: %s" %str(e))
821 raise
822
823 return ip_list
824
825 def floating_ip_create(self, pool):
826 """
827 Arguments:
828 pool (string): Name of the pool (optional)
829 Returns:
830 An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
831 """
832 nvconn = self._get_nova_connection()
833 try:
834 floating_ip = nvconn.floating_ips.create(pool)
835 except Exception as e:
836 logger.error("OpenstackDriver: Floating IP Create operation failed. Exception: %s" %str(e))
837 raise
838
839 return floating_ip
840
841 def floating_ip_delete(self, floating_ip):
842 """
843 Arguments:
844 floating_ip: An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
845 Returns:
846 None
847 """
848 nvconn = self._get_nova_connection()
849 try:
850 floating_ip = nvconn.floating_ips.delete(floating_ip)
851 except Exception as e:
852 logger.error("OpenstackDriver: Floating IP Delete operation failed. Exception: %s" %str(e))
853 raise
854
855 def floating_ip_assign(self, server_id, floating_ip, fixed_ip):
856 """
857 Arguments:
858 server_id (string) : UUID of the server
859 floating_ip (string): IP address string for floating-ip
860 fixed_ip (string) : IP address string for the fixed-ip with which floating ip will be associated
861 Returns:
862 None
863 """
864 nvconn = self._get_nova_connection()
865 try:
866 nvconn.servers.add_floating_ip(server_id, floating_ip, fixed_ip)
867 except Exception as e:
868 logger.error("OpenstackDriver: Assign Floating IP operation failed. Exception: %s" %str(e))
869 raise
870
871 def floating_ip_release(self, server_id, floating_ip):
872 """
873 Arguments:
874 server_id (string) : UUID of the server
875 floating_ip (string): IP address string for floating-ip
876 Returns:
877 None
878 """
879 nvconn = self._get_nova_connection()
880 try:
881 nvconn.servers.remove_floating_ip(server_id, floating_ip)
882 except Exception as e:
883 logger.error("OpenstackDriver: Release Floating IP operation failed. Exception: %s" %str(e))
884 raise
885
886 def volume_list(self, server_id):
887 """
888 List of volumes attached to the server
889
890 Arguments:
891 None
892 Returns:
893 List of dictionary objects where dictionary is representation of class (novaclient.v2.volumes.Volume)
894 """
895 nvconn = self._get_nova_connection()
896 try:
897 volumes = nvconn.volumes.get_server_volumes(server_id=server_id)
898 except Exception as e:
899 logger.error("OpenstackDriver: Get volume information failed. Exception: %s" %str(e))
900 raise
901
902 volume_info = [v.to_dict() for v in volumes]
903 return volume_info
904
905
906 def group_list(self):
907 """
908 List of Server Affinity and Anti-Affinity Groups
909
910 Arguments:
911 None
912 Returns:
913 List of dictionary objects where dictionary is representation of class (novaclient.v2.server_groups.ServerGroup)
914 """
915 nvconn = self._get_nova_connection()
916 try:
917 group_list = nvconn.server_groups.list()
918 except Exception as e:
919 logger.error("OpenstackDriver: Server Group List operation failed. Exception: %s" %str(e))
920 raise
921
922 group_info = [ group.to_dict() for group in group_list ]
923 return group_info
924
925
926
927 class NovaDriverV2(NovaDriver):
928 """
929 Driver class for novaclient V2 APIs
930 """
931 def __init__(self, ks_drv):
932 """
933 Constructor for NovaDriver
934 Arguments: KeystoneDriver class object
935 """
936 super(NovaDriverV2, self).__init__(ks_drv, 'compute', '2.0')
937
938 class NovaDriverV21(NovaDriver):
939 """
940 Driver class for novaclient V2 APIs
941 """
942 def __init__(self, ks_drv):
943 """
944 Constructor for NovaDriver
945 Arguments: KeystoneDriver class object
946 """
947 super(NovaDriverV21, self).__init__(ks_drv, 'compute', '2.1')
948
949 class GlanceDriver(object):
950 """
951 Driver for openstack glance-client
952 """
953 def __init__(self, ks_drv, service_name, version):
954 """
955 Constructor for GlanceDriver
956 Arguments: KeystoneDriver class object
957 """
958 self.ks_drv = ks_drv
959 self._service_name = service_name
960 self._version = version
961
962 def _get_glance_credentials(self):
963 """
964 Returns a dictionary of kwargs required to instantiate python-glanceclient class
965
966 Arguments: None
967
968 Returns:
969 A dictionary object of arguments
970 """
971 creds = {}
972 creds['version'] = self._version
973 creds['endpoint'] = self.ks_drv.get_service_endpoint(self._service_name, 'publicURL')
974 creds['token'] = self.ks_drv.get_auth_token()
975 creds['insecure'] = self.ks_drv.get_security_mode()
976 return creds
977
978 def _get_glance_connection(self):
979 """
980 Returns a object of class python-glanceclient
981 """
982 if not hasattr(self, '_glance_connection'):
983 self._glance_connection = glclient.Client(**self._get_glance_credentials())
984 else:
985 # Reinitialize if auth_token is no longer valid
986 if not self.ks_drv.is_auth_token_valid():
987 self._glance_connection = glclient.Client(**self._get_glance_credentials())
988 return self._glance_connection
989
990 def image_list(self):
991 """
992 Returns list of dictionaries. Each dictionary contains attributes associated with
993 image
994
995 Arguments: None
996
997 Returns: List of dictionaries.
998 """
999 glconn = self._get_glance_connection()
1000 images = []
1001 try:
1002 image_info = glconn.images.list()
1003 except Exception as e:
1004 logger.error("OpenstackDriver: List Image operation failed. Exception: %s" %(str(e)))
1005 raise
1006 images = [ img for img in image_info ]
1007 return images
1008
1009 def image_create(self, **kwargs):
1010 """
1011 Creates an image
1012 Arguments:
1013 A dictionary of kwargs with following keys
1014 {
1015 'name'(string) : Name of the image
1016 'location'(string) : URL (http://....) where image is located
1017 'disk_format'(string) : Disk format
1018 Possible values are 'ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso'
1019 'container_format'(string): Container format
1020 Possible values are 'ami', 'ari', 'aki', 'bare', 'ovf'
1021 'tags' : A list of user tags
1022 'checksum' : The image md5 checksum
1023 }
1024 Returns:
1025 image_id (string) : UUID of the image
1026
1027 """
1028 glconn = self._get_glance_connection()
1029 try:
1030 image = glconn.images.create(**kwargs)
1031 except Exception as e:
1032 logger.error("OpenstackDriver: Create Image operation failed. Exception: %s" %(str(e)))
1033 raise
1034
1035 return image.id
1036
1037 def image_upload(self, image_id, fd):
1038 """
1039 Upload the image
1040
1041 Arguments:
1042 image_id: UUID of the image
1043 fd : File descriptor for the image file
1044 Returns: None
1045 """
1046 glconn = self._get_glance_connection()
1047 try:
1048 glconn.images.upload(image_id, fd)
1049 except Exception as e:
1050 logger.error("OpenstackDriver: Image upload operation failed. Exception: %s" %(str(e)))
1051 raise
1052
1053 def image_add_location(self, image_id, location, metadata):
1054 """
1055 Add image URL location
1056
1057 Arguments:
1058 image_id : UUID of the image
1059 location : http URL for the image
1060
1061 Returns: None
1062 """
1063 glconn = self._get_glance_connection()
1064 try:
1065 image = glconn.images.add_location(image_id, location, metadata)
1066 except Exception as e:
1067 logger.error("OpenstackDriver: Image location add operation failed. Exception: %s" %(str(e)))
1068 raise
1069
1070 def image_update(self):
1071 pass
1072
1073 def image_delete(self, image_id):
1074 """
1075 Delete an image
1076
1077 Arguments:
1078 image_id: UUID of the image
1079
1080 Returns: None
1081
1082 """
1083 assert image_id == self._image_get(image_id)['id']
1084 glconn = self._get_glance_connection()
1085 try:
1086 glconn.images.delete(image_id)
1087 except Exception as e:
1088 logger.error("OpenstackDriver: Delete Image operation failed for image_id : %s. Exception: %s" %(image_id, str(e)))
1089 raise
1090
1091
1092 def _image_get(self, image_id):
1093 """
1094 Returns a dictionary object of VM image attributes
1095
1096 Arguments:
1097 image_id (string): UUID of the image
1098
1099 Returns:
1100 A dictionary of the image attributes
1101 """
1102 glconn = self._get_glance_connection()
1103 try:
1104 image = glconn.images.get(image_id)
1105 except GlanceException.HTTPBadRequest:
1106 # RIFT-14241: The get image request occasionally returns the below message. Retry in case of bad request exception.
1107 # Error code 400.: Message: Bad request syntax ('0').: Error code explanation: 400 = Bad request syntax or unsupported method. (HTTP 400)
1108 logger.warning("OpenstackDriver: Got bad request response during get_image request. Retrying.")
1109 image = glconn.images.get(image_id)
1110 except Exception as e:
1111 logger.error("OpenstackDriver: Get Image operation failed for image_id : %s. Exception: %s" %(image_id, str(e)))
1112 raise
1113
1114 return image
1115
1116 def image_get(self, image_id):
1117 """
1118 Returns a dictionary object of VM image attributes
1119
1120 Arguments:
1121 image_id (string): UUID of the image
1122
1123 Returns:
1124 A dictionary of the image attributes
1125 """
1126 return self._image_get(image_id)
1127
1128 class GlanceDriverV2(GlanceDriver):
1129 """
1130 Driver for openstack glance-client V2
1131 """
1132 def __init__(self, ks_drv):
1133 super(GlanceDriverV2, self).__init__(ks_drv, 'image', 2)
1134
1135 class NeutronDriver(object):
1136 """
1137 Driver for openstack neutron neutron-client
1138 """
1139 def __init__(self, ks_drv, service_name, version):
1140 """
1141 Constructor for NeutronDriver
1142 Arguments: KeystoneDriver class object
1143 """
1144 self.ks_drv = ks_drv
1145 self._service_name = service_name
1146 self._version = version
1147
1148 def _get_neutron_credentials(self):
1149 """
1150 Returns a dictionary of kwargs required to instantiate python-neutronclient class
1151
1152 Returns:
1153 Dictionary of kwargs
1154 """
1155 creds = {}
1156 creds['api_version'] = self._version
1157 creds['endpoint_url'] = self.ks_drv.get_service_endpoint(self._service_name, 'publicURL')
1158 creds['token'] = self.ks_drv.get_auth_token()
1159 creds['tenant_name'] = self.ks_drv.get_tenant_name()
1160 creds['insecure'] = self.ks_drv.get_security_mode()
1161 return creds
1162
1163 def _get_neutron_connection(self):
1164 """
1165 Returns an object of class python-neutronclient
1166 """
1167 if not hasattr(self, '_neutron_connection'):
1168 self._neutron_connection = ntclient.Client(**self._get_neutron_credentials())
1169 else:
1170 # Reinitialize if auth_token is no longer valid
1171 if not self.ks_drv.is_auth_token_valid():
1172 self._neutron_connection = ntclient.Client(**self._get_neutron_credentials())
1173 return self._neutron_connection
1174
1175 def network_list(self):
1176 """
1177 Returns list of dictionaries. Each dictionary contains the attributes for a network
1178 under project
1179
1180 Arguments: None
1181
1182 Returns:
1183 A list of dictionaries
1184 """
1185 networks = []
1186 ntconn = self._get_neutron_connection()
1187 try:
1188 networks = ntconn.list_networks()
1189 except Exception as e:
1190 logger.error("OpenstackDriver: List Network operation failed. Exception: %s" %(str(e)))
1191 raise
1192 return networks['networks']
1193
1194 def network_create(self, **kwargs):
1195 """
1196 Creates a new network for the project
1197
1198 Arguments:
1199 A dictionary with following key-values
1200 {
1201 name (string) : Name of the network
1202 admin_state_up(Boolean) : True/False (Defaults: True)
1203 external_router(Boolean) : Connectivity with external router. True/False (Defaults: False)
1204 shared(Boolean) : Shared among tenants. True/False (Defaults: False)
1205 physical_network(string) : The physical network where this network object is implemented (optional).
1206 network_type : The type of physical network that maps to this network resource (optional).
1207 Possible values are: 'flat', 'vlan', 'vxlan', 'gre'
1208 segmentation_id : An isolated segment on the physical network. The network_type attribute
1209 defines the segmentation model. For example, if the network_type value
1210 is vlan, this ID is a vlan identifier. If the network_type value is gre,
1211 this ID is a gre key.
1212 }
1213 """
1214 params = {'network':
1215 {'name' : kwargs['name'],
1216 'admin_state_up' : kwargs['admin_state_up'],
1217 'tenant_id' : self.ks_drv.get_tenant_id(),
1218 'shared' : kwargs['shared'],
1219 #'port_security_enabled': port_security_enabled,
1220 'router:external' : kwargs['external_router']}}
1221
1222 if 'physical_network' in kwargs:
1223 params['network']['provider:physical_network'] = kwargs['physical_network']
1224 if 'network_type' in kwargs:
1225 params['network']['provider:network_type'] = kwargs['network_type']
1226 if 'segmentation_id' in kwargs:
1227 params['network']['provider:segmentation_id'] = kwargs['segmentation_id']
1228
1229 ntconn = self._get_neutron_connection()
1230 try:
1231 logger.debug("Calling neutron create_network() with params: %s", str(params))
1232 net = ntconn.create_network(params)
1233 except Exception as e:
1234 logger.error("OpenstackDriver: Create Network operation failed. Exception: %s" %(str(e)))
1235 raise
1236 logger.debug("Got create_network response from neutron connection: %s", str(net))
1237 network_id = net['network']['id']
1238 if not network_id:
1239 raise Exception("Empty network id returned from create_network. (params: %s)" % str(params))
1240
1241 return network_id
1242
1243 def network_delete(self, network_id):
1244 """
1245 Deletes a network identified by network_id
1246
1247 Arguments:
1248 network_id (string): UUID of the network
1249
1250 Returns: None
1251 """
1252 assert network_id == self._network_get(network_id)['id']
1253 ntconn = self._get_neutron_connection()
1254 try:
1255 ntconn.delete_network(network_id)
1256 except Exception as e:
1257 logger.error("OpenstackDriver: Delete Network operation failed. Exception: %s" %(str(e)))
1258 raise
1259
1260 def _network_get(self, network_id):
1261 """
1262 Returns a dictionary object describing the attributes of the network
1263
1264 Arguments:
1265 network_id (string): UUID of the network
1266
1267 Returns:
1268 A dictionary object of the network attributes
1269 """
1270 ntconn = self._get_neutron_connection()
1271 network = ntconn.list_networks(id = network_id)['networks']
1272 if not network:
1273 raise NeutronException.NotFound("Network with id %s not found"%(network_id))
1274
1275 return network[0]
1276
1277 def network_get(self, network_id):
1278 """
1279 Returns a dictionary object describing the attributes of the network
1280
1281 Arguments:
1282 network_id (string): UUID of the network
1283
1284 Returns:
1285 A dictionary object of the network attributes
1286 """
1287 return self._network_get(network_id)
1288
1289 def subnet_create(self, **kwargs):
1290 """
1291 Creates a subnet on the network
1292
1293 Arguments:
1294 A dictionary with following key value pairs
1295 {
1296 network_id(string) : UUID of the network where subnet needs to be created
1297 subnet_cidr(string) : IPv4 address prefix (e.g. '1.1.1.0/24') for the subnet
1298 ip_version (integer): 4 for IPv4 and 6 for IPv6
1299
1300 }
1301
1302 Returns:
1303 subnet_id (string): UUID of the created subnet
1304 """
1305 params = {}
1306 params['network_id'] = kwargs['network_id']
1307 params['ip_version'] = kwargs['ip_version']
1308
1309 # if params['ip_version'] == 6:
1310 # assert 0, "IPv6 is not supported"
1311
1312 if 'subnetpool_id' in kwargs:
1313 params['subnetpool_id'] = kwargs['subnetpool_id']
1314 else:
1315 params['cidr'] = kwargs['cidr']
1316
1317 if 'gateway_ip' in kwargs:
1318 params['gateway_ip'] = kwargs['gateway_ip']
1319 else:
1320 params['gateway_ip'] = None
1321
1322 if 'dhcp_params' in kwargs:
1323 params['enable_dhcp'] = kwargs['dhcp_params']['enable_dhcp']
1324 if 'start_address' in kwargs['dhcp_params'] and 'count' in kwargs['dhcp_params']:
1325 end_address = (ipaddress.IPv4Address(kwargs['dhcp_params']['start_address']) + kwargs['dhcp_params']['count']).compressed
1326 params['allocation_pools'] = [ {'start': kwargs['dhcp_params']['start_address'] ,
1327 'end' : end_address} ]
1328
1329 if 'dns_server' in kwargs:
1330 params['dns_nameservers'] = []
1331 for server in kwargs['dns_server']:
1332 params['dns_nameservers'].append(server)
1333
1334 ntconn = self._get_neutron_connection()
1335 try:
1336 subnet = ntconn.create_subnet({'subnets': [params]})
1337 except Exception as e:
1338 logger.error("OpenstackDriver: Create Subnet operation failed. Exception: %s" %(str(e)))
1339 raise
1340
1341 return subnet['subnets'][0]['id']
1342
1343 def subnet_list(self):
1344 """
1345 Returns a list of dictionaries. Each dictionary contains attributes describing the subnet
1346
1347 Arguments: None
1348
1349 Returns:
1350 A dictionary of the objects of subnet attributes
1351 """
1352 ntconn = self._get_neutron_connection()
1353 try:
1354 subnets = ntconn.list_subnets()['subnets']
1355 except Exception as e:
1356 logger.error("OpenstackDriver: List Subnet operation failed. Exception: %s" %(str(e)))
1357 raise
1358 return subnets
1359
1360 def _subnet_get(self, subnet_id):
1361 """
1362 Returns a dictionary object describing the attributes of a subnet.
1363
1364 Arguments:
1365 subnet_id (string): UUID of the subnet
1366
1367 Returns:
1368 A dictionary object of the subnet attributes
1369 """
1370 ntconn = self._get_neutron_connection()
1371 subnets = ntconn.list_subnets(id=subnet_id)
1372 if not subnets['subnets']:
1373 logger.error("OpenstackDriver: Get subnet operation failed for subnet_id: %s" %(subnet_id))
1374 #raise NeutronException.NotFound("Could not find subnet_id %s" %(subnet_id))
1375 return {'cidr': ''}
1376 else:
1377 return subnets['subnets'][0]
1378
1379 def subnet_get(self, subnet_id):
1380 """
1381 Returns a dictionary object describing the attributes of a subnet.
1382
1383 Arguments:
1384 subnet_id (string): UUID of the subnet
1385
1386 Returns:
1387 A dictionary object of the subnet attributes
1388 """
1389 return self._subnet_get(subnet_id)
1390
1391 def subnet_delete(self, subnet_id):
1392 """
1393 Deletes a subnet identified by subnet_id
1394
1395 Arguments:
1396 subnet_id (string): UUID of the subnet to be deleted
1397
1398 Returns: None
1399 """
1400 ntconn = self._get_neutron_connection()
1401 assert subnet_id == self._subnet_get(self,subnet_id)
1402 try:
1403 ntconn.delete_subnet(subnet_id)
1404 except Exception as e:
1405 logger.error("OpenstackDriver: Delete Subnet operation failed for subnet_id : %s. Exception: %s" %(subnet_id, str(e)))
1406 raise
1407
1408 def port_list(self, **kwargs):
1409 """
1410 Returns a list of dictionaries. Each dictionary contains attributes describing the port
1411
1412 Arguments:
1413 kwargs (dictionary): A dictionary for filters for port_list operation
1414
1415 Returns:
1416 A dictionary of the objects of port attributes
1417
1418 """
1419 ports = []
1420 ntconn = self._get_neutron_connection()
1421
1422 kwargs['tenant_id'] = self.ks_drv.get_tenant_id()
1423
1424 try:
1425 ports = ntconn.list_ports(**kwargs)
1426 except Exception as e:
1427 logger.info("OpenstackDriver: List Port operation failed. Exception: %s" %(str(e)))
1428 raise
1429 return ports['ports']
1430
1431 def port_create(self, **kwargs):
1432 """
1433 Create a port in network
1434
1435 Arguments:
1436 A dictionary of following
1437 {
1438 name (string) : Name of the port
1439 network_id(string) : UUID of the network_id identifying the network to which port belongs
1440 subnet_id(string) : UUID of the subnet_id from which IP-address will be assigned to port
1441 vnic_type(string) : Possible values are "normal", "direct", "macvtap"
1442 }
1443 Returns:
1444 port_id (string) : UUID of the port
1445 """
1446 params = {
1447 "port": {
1448 "admin_state_up" : kwargs['admin_state_up'],
1449 "name" : kwargs['name'],
1450 "network_id" : kwargs['network_id'],
1451 "fixed_ips" : [ {"subnet_id": kwargs['subnet_id']}],
1452 "binding:vnic_type" : kwargs['port_type']}}
1453 if 'port_security_enabled' in kwargs:
1454 params["port"]["port_security_enabled"] = kwargs['port_security_enabled']
1455
1456 ntconn = self._get_neutron_connection()
1457 try:
1458 port = ntconn.create_port(params)
1459 except Exception as e:
1460 logger.error("OpenstackDriver: Port Create operation failed. Exception: %s" %(str(e)))
1461 raise
1462 return port['port']['id']
1463
1464 def _port_get(self, port_id):
1465 """
1466 Returns a dictionary object describing the attributes of the port
1467
1468 Arguments:
1469 port_id (string): UUID of the port
1470
1471 Returns:
1472 A dictionary object of the port attributes
1473 """
1474 ntconn = self._get_neutron_connection()
1475 port = ntconn.list_ports(id=port_id)['ports']
1476 if not port:
1477 raise NeutronException.NotFound("Could not find port_id %s" %(port_id))
1478 return port[0]
1479
1480 def port_get(self, port_id):
1481 """
1482 Returns a dictionary object describing the attributes of the port
1483
1484 Arguments:
1485 port_id (string): UUID of the port
1486
1487 Returns:
1488 A dictionary object of the port attributes
1489 """
1490 return self._port_get(port_id)
1491
1492 def port_delete(self, port_id):
1493 """
1494 Deletes a port identified by port_id
1495
1496 Arguments:
1497 port_id (string) : UUID of the port
1498
1499 Returns: None
1500 """
1501 assert port_id == self._port_get(port_id)['id']
1502 ntconn = self._get_neutron_connection()
1503 try:
1504 ntconn.delete_port(port_id)
1505 except Exception as e:
1506 logger.error("Port Delete operation failed for port_id : %s. Exception: %s" %(port_id, str(e)))
1507 raise
1508
1509 def security_group_list(self):
1510 """
1511 Returns a list of dictionaries. Each dictionary contains attributes describing the security group
1512
1513 Arguments:
1514 None
1515
1516 Returns:
1517 A dictionary of the objects of security group attributes
1518 """
1519 ntconn = self._get_neutron_connection()
1520 try:
1521 group_list = ntconn.list_security_groups(tenant_id=self.ks_drv.get_tenant_id())
1522 except Exception as e:
1523 logger.error("List Security group operation, Exception: %s" %(str(e)))
1524 raise
1525
1526 if 'security_groups' in group_list:
1527 return group_list['security_groups']
1528 else:
1529 return []
1530
1531 def subnetpool_list(self, **kwargs):
1532 """
1533 Returns a list of dictionaries. Each dictionary contains attributes describing a subnet prefix pool
1534
1535 Arguments:
1536 None
1537
1538 Returns:
1539 A dictionary of the objects of subnet prefix pool
1540 """
1541 ntconn = self._get_neutron_connection()
1542 try:
1543 pool_list = ntconn.list_subnetpools(**kwargs)
1544 except Exception as e:
1545 logger.error("List SubnetPool operation, Exception: %s" %(str(e)))
1546 raise
1547
1548 if 'subnetpools' in pool_list:
1549 return pool_list['subnetpools']
1550 else:
1551 return []
1552
1553 class NeutronDriverV2(NeutronDriver):
1554 """
1555 Driver for openstack neutron neutron-client v2
1556 """
1557 def __init__(self, ks_drv):
1558 """
1559 Constructor for NeutronDriver
1560 Arguments: KeystoneDriver class object
1561 """
1562 super(NeutronDriverV2, self).__init__(ks_drv, 'network', '2.0')
1563
1564
1565
1566 class CeilometerDriver(object):
1567 """
1568 Driver for openstack ceilometer client
1569 """
1570
1571 def __init__(self, ks_drv, service_name, version):
1572 """
1573 Constructor for CeilometerDriver
1574 Arguments: KeystoneDriver class object
1575 """
1576 self.ks_drv = ks_drv
1577 self._service_name = service_name
1578 self._version = version
1579 self._client = None
1580
1581 @property
1582 def version(self):
1583 """The version of the ceilometer client used by the driver"""
1584 return self._version
1585
1586 @property
1587 def client(self):
1588 """The instance of ceilometer client used by the driver"""
1589 if self._client is None or not self.ks_drv.is_auth_token_valid():
1590 self._client = ceilo_client.Client(**self.credentials)
1591
1592 return self._client
1593
1594 @property
1595 def auth_token(self):
1596 """The authorization token for the ceilometer client"""
1597 try:
1598 return self.ks_drv.get_auth_token()
1599 except KeystoneExceptions.EndpointNotFound as e:
1600 logger.error("OpenstackDriver: unable to get authorization token for ceilometer. Exception: %s" %(str(e)))
1601 raise
1602
1603 @property
1604 def security_mode(self):
1605 """The security mode for the ceilometer client"""
1606 try:
1607 return self.ks_drv.get_security_mode()
1608 except KeystoneExceptions.EndpointNotFound as e:
1609 logger.error("OpenstackDriver: unable to get security mode for ceilometer. Exception: %s" %(str(e)))
1610 raise
1611
1612 @property
1613 def endpoint(self):
1614 """The service endpoint for the ceilometer client"""
1615 try:
1616 return self.ks_drv.get_service_endpoint(self._service_name, "publicURL")
1617 except KeystoneExceptions.EndpointNotFound as e:
1618 logger.error("OpenstackDriver: unable to get endpoint for ceilometer. Exception: %s" %(str(e)))
1619 raise
1620
1621 @property
1622 def credentials(self):
1623 """A dictionary of credentials for the ceilometer client"""
1624 return dict(
1625 version=self.version,
1626 endpoint=self.endpoint,
1627 token=self.auth_token,
1628 insecure=self.security_mode,
1629 )
1630
1631 @property
1632 def meters(self):
1633 """A list of the available meters"""
1634 try:
1635 return self.client.meters.list()
1636 except Exception as e:
1637 logger.error("OpenstackDriver: List meters operation failed. Exception: %s" %(str(e)))
1638 raise
1639
1640 @property
1641 def alarms(self):
1642 """The ceilometer client alarms manager"""
1643 return self.client.alarms
1644
1645 def query_samples(self, vim_instance_id, counter_name, limit=1):
1646 """Returns a list of samples
1647
1648 Arguments:
1649 vim_instance_id - the ID of the VIM that the samples are from
1650 counter_name - the counter that the samples will come from
1651 limit - a limit on the number of samples to return
1652 (default: 1)
1653
1654 Returns:
1655 A list of samples
1656
1657 """
1658 try:
1659 filter = json.dumps({
1660 "and": [
1661 {"=": {"resource": vim_instance_id}},
1662 {"=": {"counter_name": counter_name}}
1663 ]
1664 })
1665 result = self.client.query_samples.query(filter=filter, limit=limit)
1666 return result[-limit:]
1667
1668 except Exception as e:
1669 logger.exception(e)
1670
1671 return []
1672
1673
1674 class CeilometerDriverV2(CeilometerDriver):
1675 """
1676 Driver for openstack ceilometer ceilometer-client
1677 """
1678 def __init__(self, ks_drv):
1679 """
1680 Constructor for CeilometerDriver
1681 Arguments: CeilometerDriver class object
1682 """
1683 super(CeilometerDriverV2, self).__init__(ks_drv, 'metering', '2')
1684
1685 class OpenstackDriver(object):
1686 """
1687 Driver for openstack nova, neutron, glance, keystone, swift, cinder services
1688 """
1689 def __init__(self, username,
1690 password,
1691 auth_url,
1692 tenant_name,
1693 mgmt_network = None,
1694 cert_validate = False,
1695 user_domain_name = None,
1696 project_domain_name = None,
1697 region = None):
1698 """
1699 OpenstackDriver Driver constructor
1700 Arguments:
1701 username (string) : Username for project/tenant.
1702 password (string) : Password
1703 auth_url (string) : Keystone Authentication URL.
1704 tenant_name (string) : Openstack project name
1705 mgmt_network(string, optional) : Management network name. Each VM created with this cloud-account will
1706 have a default interface into management network.
1707 cert_validate (boolean, optional) : In case of SSL/TLS connection if certificate validation is required or not.
1708 user_domain_name : Domain name for user
1709 project_domain_name : Domain name for project
1710 region : Region name
1711 """
1712 insecure = not cert_validate
1713 if auth_url.find('/v3') != -1:
1714 self.ks_drv = KeystoneDriverV3(username,
1715 password,
1716 auth_url,
1717 tenant_name,
1718 insecure,
1719 user_domain_name,
1720 project_domain_name,
1721 region)
1722 self.glance_drv = GlanceDriverV2(self.ks_drv)
1723 self.nova_drv = NovaDriverV21(self.ks_drv)
1724 self.neutron_drv = NeutronDriverV2(self.ks_drv)
1725 self.ceilo_drv = CeilometerDriverV2(self.ks_drv)
1726 self.cinder_drv = CinderDriverV2(self.ks_drv)
1727 elif auth_url.find('/v2') != -1:
1728
1729 self.ks_drv = KeystoneDriverV2(username,
1730 password,
1731 auth_url,
1732 tenant_name,
1733 insecure,
1734 region)
1735 self.glance_drv = GlanceDriverV2(self.ks_drv)
1736 self.nova_drv = NovaDriverV2(self.ks_drv)
1737 self.neutron_drv = NeutronDriverV2(self.ks_drv)
1738 self.ceilo_drv = CeilometerDriverV2(self.ks_drv)
1739 self.cinder_drv = CinderDriverV2(self.ks_drv)
1740 else:
1741 logger.error("Could not identity the version information for openstack service endpoints. Auth_URL should contain \"/v2\" or \"/v3\" string in it")
1742 raise NotImplementedError("Auth URL is wrong or invalid. Only Keystone v2 & v3 supported")
1743
1744 self._mgmt_network_id = None
1745 if mgmt_network != None:
1746 self._mgmt_network = mgmt_network
1747
1748 networks = []
1749 try:
1750 ntconn = self.neutron_drv._get_neutron_connection()
1751 networks = ntconn.list_networks()
1752 except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure) as e:
1753 raise
1754 except Exception as e:
1755 logger.error("OpenstackDriver: List Network operation failed. Exception: %s" %(str(e)))
1756 raise
1757
1758 network_list = [ network for network in networks['networks'] if network['name'] == mgmt_network ]
1759
1760 if not network_list:
1761 raise NeutronException.NotFound("Could not find network %s" %(mgmt_network))
1762 self._mgmt_network_id = network_list[0]['id']
1763
1764 def validate_account_creds(self):
1765 try:
1766 ksconn = self.ks_drv._get_keystone_connection()
1767 except KeystoneExceptions.AuthorizationFailure as e:
1768 logger.error("OpenstackDriver: Unable to authenticate or validate the existing credentials. Exception: %s" %(str(e)))
1769 raise ValidationError("Invalid Credentials: "+ str(e))
1770 except Exception as e:
1771 logger.error("OpenstackDriver: Could not connect to Openstack. Exception: %s" %(str(e)))
1772 raise ValidationError("Connection Error: "+ str(e))
1773
1774 def get_mgmt_network_id(self):
1775 return self._mgmt_network_id
1776
1777 def glance_image_create(self, **kwargs):
1778 if not 'disk_format' in kwargs:
1779 kwargs['disk_format'] = 'qcow2'
1780 if not 'container_format' in kwargs:
1781 kwargs['container_format'] = 'bare'
1782 if not 'min_disk' in kwargs:
1783 kwargs['min_disk'] = 0
1784 if not 'min_ram' in kwargs:
1785 kwargs['min_ram'] = 0
1786 return self.glance_drv.image_create(**kwargs)
1787
1788 def glance_image_upload(self, image_id, fd):
1789 self.glance_drv.image_upload(image_id, fd)
1790
1791 def glance_image_add_location(self, image_id, location):
1792 self.glance_drv.image_add_location(image_id, location)
1793
1794 def glance_image_delete(self, image_id):
1795 self.glance_drv.image_delete(image_id)
1796
1797 def glance_image_list(self):
1798 return self.glance_drv.image_list()
1799
1800 def glance_image_get(self, image_id):
1801 return self.glance_drv.image_get(image_id)
1802
1803
1804 def nova_flavor_list(self):
1805 return self.nova_drv.flavor_list()
1806
1807 def nova_flavor_create(self, name, ram, vcpus, disk, epa_specs):
1808 extra_specs = epa_specs if epa_specs else {}
1809 return self.nova_drv.flavor_create(name,
1810 ram = ram,
1811 vcpu = vcpus,
1812 disk = disk,
1813 extra_specs = extra_specs)
1814
1815 def nova_flavor_delete(self, flavor_id):
1816 self.nova_drv.flavor_delete(flavor_id)
1817
1818 def nova_flavor_get(self, flavor_id):
1819 return self.nova_drv.flavor_get(flavor_id)
1820
1821 def nova_server_create(self, **kwargs):
1822 def _verify_image(image_id):
1823 image = self.glance_drv.image_get(image_id)
1824 if image['status'] != 'active':
1825 raise GlanceException.NotFound("Image with image_id: %s not found in active state. Current State: %s" %(image['id'], image['status']))
1826
1827 assert kwargs['flavor_id'] == self.nova_drv.flavor_get(kwargs['flavor_id'])['id']
1828
1829 if kwargs['block_device_mapping_v2'] is not None:
1830 for block_map in kwargs['block_device_mapping_v2']:
1831 if 'uuid' in block_map:
1832 _verify_image(block_map['uuid'])
1833 else:
1834 _verify_image(kwargs['image_id'])
1835
1836 # if 'network_list' in kwargs:
1837 # kwargs['network_list'].append(self._mgmt_network_id)
1838 # else:
1839 # kwargs['network_list'] = [self._mgmt_network_id]
1840
1841 if 'security_groups' not in kwargs:
1842 nvconn = self.nova_drv._get_nova_connection()
1843 sec_groups = nvconn.security_groups.list()
1844 if sec_groups:
1845 ## Should we add VM in all availability security_groups ???
1846 kwargs['security_groups'] = [x.name for x in sec_groups]
1847 else:
1848 kwargs['security_groups'] = None
1849
1850 return self.nova_drv.server_create(**kwargs)
1851
1852 def nova_server_add_port(self, server_id, port_id):
1853 self.nova_drv.server_add_port(server_id, port_id)
1854
1855 def nova_server_delete_port(self, server_id, port_id):
1856 self.nova_drv.server_delete_port(server_id, port_id)
1857
1858 def nova_server_start(self, server_id):
1859 self.nova_drv.server_start(server_id)
1860
1861 def nova_server_stop(self, server_id):
1862 self.nova_drv.server_stop(server_id)
1863
1864 def nova_server_delete(self, server_id):
1865 self.nova_drv.server_delete(server_id)
1866
1867 def nova_server_reboot(self, server_id):
1868 self.nova_drv.server_reboot(server_id, reboot_type='HARD')
1869
1870 def nova_server_rebuild(self, server_id, image_id):
1871 self.nova_drv.server_rebuild(server_id, image_id)
1872
1873 def nova_floating_ip_list(self):
1874 return self.nova_drv.floating_ip_list()
1875
1876 def nova_floating_ip_create(self, pool = None):
1877 return self.nova_drv.floating_ip_create(pool)
1878
1879 def nova_floating_ip_delete(self, floating_ip):
1880 self.nova_drv.floating_ip_delete(floating_ip)
1881
1882 def nova_floating_ip_assign(self, server_id, floating_ip, fixed_ip):
1883 self.nova_drv.floating_ip_assign(server_id, floating_ip, fixed_ip)
1884
1885 def nova_floating_ip_release(self, server_id, floating_ip):
1886 self.nova_drv.floating_ip_release(server_id, floating_ip)
1887
1888 def nova_server_list(self):
1889 return self.nova_drv.server_list()
1890
1891 def nova_server_get(self, server_id):
1892 return self.nova_drv.server_get(server_id)
1893
1894 def nova_server_console(self, server_id):
1895 return self.nova_drv.server_console(server_id)
1896
1897 def nova_server_group_list(self):
1898 return self.nova_drv.group_list()
1899
1900 def nova_volume_list(self, server_id):
1901 return self.nova_drv.volume_list(server_id)
1902
1903 def neutron_network_list(self):
1904 return self.neutron_drv.network_list()
1905
1906 def neutron_network_get(self, network_id):
1907 return self.neutron_drv.network_get(network_id)
1908
1909 def neutron_network_create(self, **kwargs):
1910 return self.neutron_drv.network_create(**kwargs)
1911
1912 def neutron_network_delete(self, network_id):
1913 self.neutron_drv.network_delete(network_id)
1914
1915 def neutron_subnet_list(self):
1916 return self.neutron_drv.subnet_list()
1917
1918 def neutron_subnet_get(self, subnet_id):
1919 return self.neutron_drv.subnet_get(subnet_id)
1920
1921 def neutron_subnet_create(self, **kwargs):
1922 return self.neutron_drv.subnet_create(**kwargs)
1923
1924 def netruon_subnet_delete(self, subnet_id):
1925 self.neutron_drv.subnet_delete(subnet_id)
1926
1927 def neutron_subnetpool_list(self):
1928 return self.neutron_drv.subnetpool_list()
1929
1930 def netruon_subnetpool_by_name(self, pool_name):
1931 pool_list = self.neutron_drv.subnetpool_list(**{'name': pool_name})
1932 if pool_list:
1933 return pool_list[0]
1934 else:
1935 return None
1936
1937 def neutron_port_list(self, **kwargs):
1938 return self.neutron_drv.port_list(**kwargs)
1939
1940 def neutron_port_get(self, port_id):
1941 return self.neutron_drv.port_get(port_id)
1942
1943 def neutron_port_create(self, **kwargs):
1944 subnets = [subnet for subnet in self.neutron_drv.subnet_list() if subnet['network_id'] == kwargs['network_id']]
1945 assert len(subnets) == 1
1946 kwargs['subnet_id'] = subnets[0]['id']
1947 if not 'admin_state_up' in kwargs:
1948 kwargs['admin_state_up'] = True
1949 port_id = self.neutron_drv.port_create(**kwargs)
1950
1951 if 'vm_id' in kwargs:
1952 self.nova_server_add_port(kwargs['vm_id'], port_id)
1953 return port_id
1954
1955 def neutron_security_group_list(self):
1956 return self.neutron_drv.security_group_list()
1957
1958 def neutron_security_group_by_name(self, group_name):
1959 group_list = self.neutron_drv.security_group_list()
1960 groups = [group for group in group_list if group['name'] == group_name]
1961 if groups:
1962 return groups[0]
1963 else:
1964 return None
1965
1966 def neutron_port_delete(self, port_id):
1967 self.neutron_drv.port_delete(port_id)
1968
1969 def ceilo_meter_endpoint(self):
1970 return self.ceilo_drv.endpoint
1971
1972 def ceilo_meter_list(self):
1973 return self.ceilo_drv.meters
1974
1975 def ceilo_nfvi_metrics(self, vim_id):
1976 """Returns a dict of NFVI metrics for a given VM
1977
1978 Arguments:
1979 vim_id - the VIM ID of the VM to retrieve the metrics for
1980
1981 Returns:
1982 A dict of NFVI metrics
1983
1984 """
1985 def query_latest_sample(counter_name):
1986 try:
1987 filter = json.dumps({
1988 "and": [
1989 {"=": {"resource": vim_id}},
1990 {"=": {"counter_name": counter_name}}
1991 ]
1992 })
1993 orderby = json.dumps([{"timestamp": "DESC"}])
1994 result = self.ceilo_drv.client.query_samples.query(
1995 filter=filter,
1996 orderby=orderby,
1997 limit=1,
1998 )
1999 return result[0]
2000
2001 except IndexError:
2002 pass
2003
2004 except Exception as e:
2005 logger.error("Got exception while querying ceilometer, exception details:%s " %str(e))
2006
2007 return None
2008
2009 memory_usage = query_latest_sample("memory.usage")
2010 disk_usage = query_latest_sample("disk.usage")
2011 cpu_util = query_latest_sample("cpu_util")
2012
2013 metrics = dict()
2014
2015 if memory_usage is not None:
2016 memory_usage.volume = 1e6 * memory_usage.volume
2017 metrics["memory_usage"] = memory_usage.to_dict()
2018
2019 if disk_usage is not None:
2020 metrics["disk_usage"] = disk_usage.to_dict()
2021
2022 if cpu_util is not None:
2023 metrics["cpu_util"] = cpu_util.to_dict()
2024
2025 return metrics
2026
2027 def ceilo_alarm_list(self):
2028 """Returns a list of ceilometer alarms"""
2029 return self.ceilo_drv.client.alarms.list()
2030
2031 def ceilo_alarm_create(self,
2032 name,
2033 meter,
2034 statistic,
2035 operation,
2036 threshold,
2037 period,
2038 evaluations,
2039 severity='low',
2040 repeat=True,
2041 enabled=True,
2042 actions=None,
2043 **kwargs):
2044 """Create a new Alarm
2045
2046 Arguments:
2047 name - the name of the alarm
2048 meter - the name of the meter to measure
2049 statistic - the type of statistic used to trigger the alarm
2050 ('avg', 'min', 'max', 'count', 'sum')
2051 operation - the relational operator that, combined with the
2052 threshold value, determines when the alarm is
2053 triggered ('lt', 'le', 'eq', 'ge', 'gt')
2054 threshold - the value of the statistic that will trigger the
2055 alarm
2056 period - the duration (seconds) over which to evaluate the
2057 specified statistic
2058 evaluations - the number of samples of the meter statistic to
2059 collect when evaluating the threshold
2060 severity - a measure of the urgency or importance of the alarm
2061 ('low', 'moderate', 'critical')
2062 repeat - a flag that indicates whether the alarm should be
2063 triggered once (False) or repeatedly while the alarm
2064 condition is true (True)
2065 enabled - a flag that indicates whether the alarm is enabled
2066 (True) or disabled (False)
2067 actions - a dict specifying the URLs for webhooks. The dict can
2068 have up to 3 keys: 'insufficient_data', 'alarm',
2069 'ok'. Each key is associated with a list of URLs to
2070 webhooks that will be invoked when one of the 3
2071 actions is taken.
2072 kwargs - an arbitrary dict of keyword arguments that are
2073 passed to the ceilometer client
2074
2075 """
2076 ok_actions = actions.get('ok') if actions is not None else None
2077 alarm_actions = actions.get('alarm') if actions is not None else None
2078 insufficient_data_actions = actions.get('insufficient_data') if actions is not None else None
2079
2080 return self.ceilo_drv.client.alarms.create(
2081 name=name,
2082 meter_name=meter,
2083 statistic=statistic,
2084 comparison_operator=operation,
2085 threshold=threshold,
2086 period=period,
2087 evaluation_periods=evaluations,
2088 severity=severity,
2089 repeat_actions=repeat,
2090 enabled=enabled,
2091 ok_actions=ok_actions,
2092 alarm_actions=alarm_actions,
2093 insufficient_data_actions=insufficient_data_actions,
2094 **kwargs
2095 )
2096
2097 def ceilo_alarm_update(self, alarm_id, **kwargs):
2098 """Updates an existing alarm
2099
2100 Arguments:
2101 alarm_id - the identifier of the alarm to update
2102 kwargs - a dict of the alarm attributes to update
2103
2104 """
2105 return self.ceilo_drv.client.alarms.update(alarm_id, **kwargs)
2106
2107 def ceilo_alarm_delete(self, alarm_id):
2108 self.ceilo_drv.client.alarms.delete(alarm_id)
2109
2110 def cinder_volume_list(self):
2111 return self.cinder_drv.volume_list()
2112
2113 def cinder_volume_get(self,vol_id):
2114 return self.cinder_drv.volume_get(vol_id)
2115
2116 def cinder_volume_set_metadata(self, volumeid, metadata):
2117 return self.cinder_drv.volume_set_metadata(volumeid, metadata)
2118
2119 def cinder_volume_delete_metadata(self, volumeid, metadata):
2120 return self.cinder_drv.volume_delete_metadata(volumeid, metadata)
2121
2122
2123
2124 class CinderDriver(object):
2125 """
2126 Driver for openstack cinder-client
2127 """
2128 def __init__(self, ks_drv, service_name, version):
2129 """
2130 Constructor for CinderDriver
2131 Arguments: KeystoneDriver class object
2132 """
2133 self.ks_drv = ks_drv
2134 self._service_name = service_name
2135 self._version = version
2136
2137 def _get_cinder_credentials(self):
2138 """
2139 Returns a dictionary of kwargs required to instantiate python-cinderclient class
2140
2141 Arguments: None
2142
2143 Returns:
2144 A dictionary object of arguments
2145 """
2146 creds = {}
2147 creds['version'] = self._version
2148 creds['username'] = self.ks_drv.get_username()
2149 creds['api_key'] = self.ks_drv.get_password()
2150 creds['auth_url'] = self.ks_drv.get_service_endpoint("identity", "publicURL")
2151 creds['project_id'] = self.ks_drv.get_tenant_name()
2152 creds['insecure'] = self.ks_drv.get_security_mode()
2153
2154 return creds
2155
2156 def _get_cinder_connection(self):
2157 """
2158 Returns a object of class python-cinderclient
2159 """
2160 if not hasattr(self, '_cinder_connection'):
2161 self._cinder_connection = cinder_client.Client(**self._get_cinder_credentials())
2162 else:
2163 # Reinitialize if auth_token is no longer valid
2164 if not self.ks_drv.is_auth_token_valid():
2165 self._cinder_connection = cinder_client.Client(**self._get_cinder_credentials())
2166 return self._cinder_connection
2167
2168 def volume_list(self):
2169 """
2170 Returns list of dictionaries. Each dictionary contains attributes associated with
2171 volumes
2172
2173 Arguments: None
2174
2175 Returns: List of dictionaries.
2176 """
2177 cinderconn = self._get_cinder_connection()
2178 volumes = []
2179 try:
2180 volume_info = cinderconn.volumes.list()
2181 except Exception as e:
2182 logger.error("OpenstackDriver: List volumes operation failed. Exception: %s" %(str(e)))
2183 raise
2184 volumes = [ volume for volume in volume_info ]
2185 return volumes
2186
2187 def volume_get(self, volume_id):
2188 """
2189 Get details volume
2190
2191 Arguments: None
2192
2193 Returns: List of dictionaries.
2194 """
2195 cinderconn = self._get_cinder_connection()
2196 try:
2197 vol = cinderconn.volumes.get(volume_id)
2198 except Exception as e:
2199 logger.error("OpenstackDriver: Get volume operation failed. Exception: %s" %(str(e)))
2200 raise
2201 return vol
2202
2203 def volume_set_metadata(self, volume_id, metadata):
2204 """
2205 Set metadata for volume
2206 Metadata is a dictionary of key-value pairs
2207
2208 Arguments: None
2209
2210 Returns: List of dictionaries.
2211 """
2212 cinderconn = self._get_cinder_connection()
2213 try:
2214 cinderconn.volumes.set_metadata(volume_id, metadata)
2215 except Exception as e:
2216 logger.error("OpenstackDriver: Set metadata operation failed. Exception: %s" %(str(e)))
2217 raise
2218
2219 def volume_delete_metadata(self, volume_id, metadata):
2220 """
2221 Delete metadata for volume
2222 Metadata is a dictionary of key-value pairs
2223
2224 Arguments: None
2225
2226 Returns: List of dictionaries.
2227 """
2228 cinderconn = self._get_cinder_connection()
2229 try:
2230 cinderconn.volumes.delete_metadata(volume_id, metadata)
2231 except Exception as e:
2232 logger.error("OpenstackDriver: Delete metadata operation failed. Exception: %s" %(str(e)))
2233 raise
2234
2235 class CinderDriverV2(CinderDriver):
2236 """
2237 Driver for openstack cinder-client V2
2238 """
2239 def __init__(self, ks_drv):
2240 super(CinderDriverV2, self).__init__(ks_drv, 'volumev2', 2)
2241