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