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 if 'port_security_enabled' in kwargs:
1441 params["port"]["port_security_enabled"] = kwargs['port_security_enabled']
1442
1443 ntconn = self._get_neutron_connection()
1444 try:
1445 port = ntconn.create_port(params)
1446 except Exception as e:
1447 logger.error("OpenstackDriver: Port Create operation failed. Exception: %s" %(str(e)))
1448 raise
1449 return port['port']['id']
1450
1451 def _port_get(self, port_id):
1452 """
1453 Returns a dictionary object describing the attributes of the port
1454
1455 Arguments:
1456 port_id (string): UUID of the port
1457
1458 Returns:
1459 A dictionary object of the port attributes
1460 """
1461 ntconn = self._get_neutron_connection()
1462 port = ntconn.list_ports(id=port_id)['ports']
1463 if not port:
1464 raise NeutronException.NotFound("Could not find port_id %s" %(port_id))
1465 return port[0]
1466
1467 def port_get(self, port_id):
1468 """
1469 Returns a dictionary object describing the attributes of the port
1470
1471 Arguments:
1472 port_id (string): UUID of the port
1473
1474 Returns:
1475 A dictionary object of the port attributes
1476 """
1477 return self._port_get(port_id)
1478
1479 def port_delete(self, port_id):
1480 """
1481 Deletes a port identified by port_id
1482
1483 Arguments:
1484 port_id (string) : UUID of the port
1485
1486 Returns: None
1487 """
1488 assert port_id == self._port_get(port_id)['id']
1489 ntconn = self._get_neutron_connection()
1490 try:
1491 ntconn.delete_port(port_id)
1492 except Exception as e:
1493 logger.error("Port Delete operation failed for port_id : %s. Exception: %s" %(port_id, str(e)))
1494 raise
1495
1496 def security_group_list(self):
1497 """
1498 Returns a list of dictionaries. Each dictionary contains attributes describing the security group
1499
1500 Arguments:
1501 None
1502
1503 Returns:
1504 A dictionary of the objects of security group attributes
1505 """
1506 ntconn = self._get_neutron_connection()
1507 try:
1508 group_list = ntconn.list_security_groups(tenant_id=self.ks_drv.get_tenant_id())
1509 except Exception as e:
1510 logger.error("List Security group operation, Exception: %s" %(str(e)))
1511 raise
1512
1513 if 'security_groups' in group_list:
1514 return group_list['security_groups']
1515 else:
1516 return []
1517
1518 def subnetpool_list(self, **kwargs):
1519 """
1520 Returns a list of dictionaries. Each dictionary contains attributes describing a subnet prefix pool
1521
1522 Arguments:
1523 None
1524
1525 Returns:
1526 A dictionary of the objects of subnet prefix pool
1527 """
1528 ntconn = self._get_neutron_connection()
1529 try:
1530 pool_list = ntconn.list_subnetpools(**kwargs)
1531 except Exception as e:
1532 logger.error("List SubnetPool operation, Exception: %s" %(str(e)))
1533 raise
1534
1535 if 'subnetpools' in pool_list:
1536 return pool_list['subnetpools']
1537 else:
1538 return []
1539
1540 class NeutronDriverV2(NeutronDriver):
1541 """
1542 Driver for openstack neutron neutron-client v2
1543 """
1544 def __init__(self, ks_drv):
1545 """
1546 Constructor for NeutronDriver
1547 Arguments: KeystoneDriver class object
1548 """
1549 super(NeutronDriverV2, self).__init__(ks_drv, 'network', '2.0')
1550
1551
1552
1553 class CeilometerDriver(object):
1554 """
1555 Driver for openstack ceilometer client
1556 """
1557
1558 def __init__(self, ks_drv, service_name, version):
1559 """
1560 Constructor for CeilometerDriver
1561 Arguments: KeystoneDriver class object
1562 """
1563 self.ks_drv = ks_drv
1564 self._service_name = service_name
1565 self._version = version
1566 self._client = None
1567
1568 @property
1569 def version(self):
1570 """The version of the ceilometer client used by the driver"""
1571 return self._version
1572
1573 @property
1574 def client(self):
1575 """The instance of ceilometer client used by the driver"""
1576 if self._client is None or not self.ks_drv.is_auth_token_valid():
1577 self._client = ceilo_client.Client(**self.credentials)
1578
1579 return self._client
1580
1581 @property
1582 def auth_token(self):
1583 """The authorization token for the ceilometer client"""
1584 try:
1585 return self.ks_drv.get_auth_token()
1586 except KeystoneExceptions.EndpointNotFound as e:
1587 logger.error("OpenstackDriver: unable to get authorization token for ceilometer. Exception: %s" %(str(e)))
1588 raise
1589
1590 @property
1591 def security_mode(self):
1592 """The security mode for the ceilometer client"""
1593 try:
1594 return self.ks_drv.get_security_mode()
1595 except KeystoneExceptions.EndpointNotFound as e:
1596 logger.error("OpenstackDriver: unable to get security mode for ceilometer. Exception: %s" %(str(e)))
1597 raise
1598
1599 @property
1600 def endpoint(self):
1601 """The service endpoint for the ceilometer client"""
1602 try:
1603 return self.ks_drv.get_service_endpoint(self._service_name, "publicURL")
1604 except KeystoneExceptions.EndpointNotFound as e:
1605 logger.error("OpenstackDriver: unable to get endpoint for ceilometer. Exception: %s" %(str(e)))
1606 raise
1607
1608 @property
1609 def credentials(self):
1610 """A dictionary of credentials for the ceilometer client"""
1611 return dict(
1612 version=self.version,
1613 endpoint=self.endpoint,
1614 token=self.auth_token,
1615 insecure=self.security_mode,
1616 )
1617
1618 @property
1619 def meters(self):
1620 """A list of the available meters"""
1621 try:
1622 return self.client.meters.list()
1623 except Exception as e:
1624 logger.error("OpenstackDriver: List meters operation failed. Exception: %s" %(str(e)))
1625 raise
1626
1627 @property
1628 def alarms(self):
1629 """The ceilometer client alarms manager"""
1630 return self.client.alarms
1631
1632 def query_samples(self, vim_instance_id, counter_name, limit=1):
1633 """Returns a list of samples
1634
1635 Arguments:
1636 vim_instance_id - the ID of the VIM that the samples are from
1637 counter_name - the counter that the samples will come from
1638 limit - a limit on the number of samples to return
1639 (default: 1)
1640
1641 Returns:
1642 A list of samples
1643
1644 """
1645 try:
1646 filter = json.dumps({
1647 "and": [
1648 {"=": {"resource": vim_instance_id}},
1649 {"=": {"counter_name": counter_name}}
1650 ]
1651 })
1652 result = self.client.query_samples.query(filter=filter, limit=limit)
1653 return result[-limit:]
1654
1655 except Exception as e:
1656 logger.exception(e)
1657
1658 return []
1659
1660
1661 class CeilometerDriverV2(CeilometerDriver):
1662 """
1663 Driver for openstack ceilometer ceilometer-client
1664 """
1665 def __init__(self, ks_drv):
1666 """
1667 Constructor for CeilometerDriver
1668 Arguments: CeilometerDriver class object
1669 """
1670 super(CeilometerDriverV2, self).__init__(ks_drv, 'metering', '2')
1671
1672 class OpenstackDriver(object):
1673 """
1674 Driver for openstack nova, neutron, glance, keystone, swift, cinder services
1675 """
1676 def __init__(self, username, password, auth_url, tenant_name, mgmt_network = None, cert_validate = False, user_domain_name = None, project_domain_name = None):
1677 """
1678 OpenstackDriver Driver constructor
1679 Arguments:
1680 username (string) : Username for project/tenant.
1681 password (string) : Password
1682 auth_url (string) : Keystone Authentication URL.
1683 tenant_name (string) : Openstack project name
1684 mgmt_network(string, optional) : Management network name. Each VM created with this cloud-account will
1685 have a default interface into management network.
1686 cert_validate (boolean, optional) : In case of SSL/TLS connection if certificate validation is required or not.
1687
1688 """
1689 insecure = not cert_validate
1690 if auth_url.find('/v3') != -1:
1691 self.ks_drv = KeystoneDriverV3(username, password, auth_url, tenant_name, insecure, user_domain_name, project_domain_name)
1692 self.glance_drv = GlanceDriverV2(self.ks_drv)
1693 self.nova_drv = NovaDriverV21(self.ks_drv)
1694 self.neutron_drv = NeutronDriverV2(self.ks_drv)
1695 self.ceilo_drv = CeilometerDriverV2(self.ks_drv)
1696 self.cinder_drv = CinderDriverV2(self.ks_drv)
1697 elif auth_url.find('/v2') != -1:
1698 self.ks_drv = KeystoneDriverV2(username, password, auth_url, tenant_name, insecure)
1699 self.glance_drv = GlanceDriverV2(self.ks_drv)
1700 self.nova_drv = NovaDriverV2(self.ks_drv)
1701 self.neutron_drv = NeutronDriverV2(self.ks_drv)
1702 self.ceilo_drv = CeilometerDriverV2(self.ks_drv)
1703 self.cinder_drv = CinderDriverV2(self.ks_drv)
1704 else:
1705 logger.error("Could not identity the version information for openstack service endpoints. Auth_URL should contain \"/v2\" or \"/v3\" string in it")
1706 raise NotImplementedError("Auth URL is wrong or invalid. Only Keystone v2 & v3 supported")
1707
1708 self._mgmt_network_id = None
1709 if mgmt_network != None:
1710 self._mgmt_network = mgmt_network
1711
1712 networks = []
1713 try:
1714 ntconn = self.neutron_drv._get_neutron_connection()
1715 networks = ntconn.list_networks()
1716 except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure) as e:
1717 raise
1718 except Exception as e:
1719 logger.error("OpenstackDriver: List Network operation failed. Exception: %s" %(str(e)))
1720 raise
1721
1722 network_list = [ network for network in networks['networks'] if network['name'] == mgmt_network ]
1723
1724 if not network_list:
1725 raise NeutronException.NotFound("Could not find network %s" %(mgmt_network))
1726 self._mgmt_network_id = network_list[0]['id']
1727
1728 def validate_account_creds(self):
1729 try:
1730 ksconn = self.ks_drv._get_keystone_connection()
1731 except KeystoneExceptions.AuthorizationFailure as e:
1732 logger.error("OpenstackDriver: Unable to authenticate or validate the existing credentials. Exception: %s" %(str(e)))
1733 raise ValidationError("Invalid Credentials: "+ str(e))
1734 except Exception as e:
1735 logger.error("OpenstackDriver: Could not connect to Openstack. Exception: %s" %(str(e)))
1736 raise ValidationError("Connection Error: "+ str(e))
1737
1738 def get_mgmt_network_id(self):
1739 return self._mgmt_network_id
1740
1741 def glance_image_create(self, **kwargs):
1742 if not 'disk_format' in kwargs:
1743 kwargs['disk_format'] = 'qcow2'
1744 if not 'container_format' in kwargs:
1745 kwargs['container_format'] = 'bare'
1746 if not 'min_disk' in kwargs:
1747 kwargs['min_disk'] = 0
1748 if not 'min_ram' in kwargs:
1749 kwargs['min_ram'] = 0
1750 return self.glance_drv.image_create(**kwargs)
1751
1752 def glance_image_upload(self, image_id, fd):
1753 self.glance_drv.image_upload(image_id, fd)
1754
1755 def glance_image_add_location(self, image_id, location):
1756 self.glance_drv.image_add_location(image_id, location)
1757
1758 def glance_image_delete(self, image_id):
1759 self.glance_drv.image_delete(image_id)
1760
1761 def glance_image_list(self):
1762 return self.glance_drv.image_list()
1763
1764 def glance_image_get(self, image_id):
1765 return self.glance_drv.image_get(image_id)
1766
1767
1768 def nova_flavor_list(self):
1769 return self.nova_drv.flavor_list()
1770
1771 def nova_flavor_create(self, name, ram, vcpus, disk, epa_specs):
1772 extra_specs = epa_specs if epa_specs else {}
1773 return self.nova_drv.flavor_create(name,
1774 ram = ram,
1775 vcpu = vcpus,
1776 disk = disk,
1777 extra_specs = extra_specs)
1778
1779 def nova_flavor_delete(self, flavor_id):
1780 self.nova_drv.flavor_delete(flavor_id)
1781
1782 def nova_flavor_get(self, flavor_id):
1783 return self.nova_drv.flavor_get(flavor_id)
1784
1785 def nova_server_create(self, **kwargs):
1786 def _verify_image(image_id):
1787 image = self.glance_drv.image_get(image_id)
1788 if image['status'] != 'active':
1789 raise GlanceException.NotFound("Image with image_id: %s not found in active state. Current State: %s" %(image['id'], image['status']))
1790
1791 assert kwargs['flavor_id'] == self.nova_drv.flavor_get(kwargs['flavor_id'])['id']
1792
1793 if kwargs['block_device_mapping_v2'] is not None:
1794 for block_map in kwargs['block_device_mapping_v2']:
1795 if 'uuid' in block_map:
1796 _verify_image(block_map['uuid'])
1797 else:
1798 _verify_image(kwargs['image_id'])
1799
1800 # if 'network_list' in kwargs:
1801 # kwargs['network_list'].append(self._mgmt_network_id)
1802 # else:
1803 # kwargs['network_list'] = [self._mgmt_network_id]
1804
1805 if 'security_groups' not in kwargs:
1806 nvconn = self.nova_drv._get_nova_connection()
1807 sec_groups = nvconn.security_groups.list()
1808 if sec_groups:
1809 ## Should we add VM in all availability security_groups ???
1810 kwargs['security_groups'] = [x.name for x in sec_groups]
1811 else:
1812 kwargs['security_groups'] = None
1813
1814 return self.nova_drv.server_create(**kwargs)
1815
1816 def nova_server_add_port(self, server_id, port_id):
1817 self.nova_drv.server_add_port(server_id, port_id)
1818
1819 def nova_server_delete_port(self, server_id, port_id):
1820 self.nova_drv.server_delete_port(server_id, port_id)
1821
1822 def nova_server_start(self, server_id):
1823 self.nova_drv.server_start(server_id)
1824
1825 def nova_server_stop(self, server_id):
1826 self.nova_drv.server_stop(server_id)
1827
1828 def nova_server_delete(self, server_id):
1829 self.nova_drv.server_delete(server_id)
1830
1831 def nova_server_reboot(self, server_id):
1832 self.nova_drv.server_reboot(server_id, reboot_type='HARD')
1833
1834 def nova_server_rebuild(self, server_id, image_id):
1835 self.nova_drv.server_rebuild(server_id, image_id)
1836
1837 def nova_floating_ip_list(self):
1838 return self.nova_drv.floating_ip_list()
1839
1840 def nova_floating_ip_create(self, pool = None):
1841 return self.nova_drv.floating_ip_create(pool)
1842
1843 def nova_floating_ip_delete(self, floating_ip):
1844 self.nova_drv.floating_ip_delete(floating_ip)
1845
1846 def nova_floating_ip_assign(self, server_id, floating_ip, fixed_ip):
1847 self.nova_drv.floating_ip_assign(server_id, floating_ip, fixed_ip)
1848
1849 def nova_floating_ip_release(self, server_id, floating_ip):
1850 self.nova_drv.floating_ip_release(server_id, floating_ip)
1851
1852 def nova_server_list(self):
1853 return self.nova_drv.server_list()
1854
1855 def nova_server_get(self, server_id):
1856 return self.nova_drv.server_get(server_id)
1857
1858 def nova_server_console(self, server_id):
1859 return self.nova_drv.server_console(server_id)
1860
1861 def nova_server_group_list(self):
1862 return self.nova_drv.group_list()
1863
1864 def nova_volume_list(self, server_id):
1865 return self.nova_drv.volume_list(server_id)
1866
1867 def neutron_network_list(self):
1868 return self.neutron_drv.network_list()
1869
1870 def neutron_network_get(self, network_id):
1871 return self.neutron_drv.network_get(network_id)
1872
1873 def neutron_network_create(self, **kwargs):
1874 return self.neutron_drv.network_create(**kwargs)
1875
1876 def neutron_network_delete(self, network_id):
1877 self.neutron_drv.network_delete(network_id)
1878
1879 def neutron_subnet_list(self):
1880 return self.neutron_drv.subnet_list()
1881
1882 def neutron_subnet_get(self, subnet_id):
1883 return self.neutron_drv.subnet_get(subnet_id)
1884
1885 def neutron_subnet_create(self, **kwargs):
1886 return self.neutron_drv.subnet_create(**kwargs)
1887
1888 def netruon_subnet_delete(self, subnet_id):
1889 self.neutron_drv.subnet_delete(subnet_id)
1890
1891 def neutron_subnetpool_list(self):
1892 return self.neutron_drv.subnetpool_list()
1893
1894 def netruon_subnetpool_by_name(self, pool_name):
1895 pool_list = self.neutron_drv.subnetpool_list(**{'name': pool_name})
1896 if pool_list:
1897 return pool_list[0]
1898 else:
1899 return None
1900
1901 def neutron_port_list(self, **kwargs):
1902 return self.neutron_drv.port_list(**kwargs)
1903
1904 def neutron_port_get(self, port_id):
1905 return self.neutron_drv.port_get(port_id)
1906
1907 def neutron_port_create(self, **kwargs):
1908 subnets = [subnet for subnet in self.neutron_drv.subnet_list() if subnet['network_id'] == kwargs['network_id']]
1909 assert len(subnets) == 1
1910 kwargs['subnet_id'] = subnets[0]['id']
1911 if not 'admin_state_up' in kwargs:
1912 kwargs['admin_state_up'] = True
1913 port_id = self.neutron_drv.port_create(**kwargs)
1914
1915 if 'vm_id' in kwargs:
1916 self.nova_server_add_port(kwargs['vm_id'], port_id)
1917 return port_id
1918
1919 def neutron_security_group_list(self):
1920 return self.neutron_drv.security_group_list()
1921
1922 def neutron_security_group_by_name(self, group_name):
1923 group_list = self.neutron_drv.security_group_list()
1924 groups = [group for group in group_list if group['name'] == group_name]
1925 if groups:
1926 return groups[0]
1927 else:
1928 return None
1929
1930 def neutron_port_delete(self, port_id):
1931 self.neutron_drv.port_delete(port_id)
1932
1933 def ceilo_meter_endpoint(self):
1934 return self.ceilo_drv.endpoint
1935
1936 def ceilo_meter_list(self):
1937 return self.ceilo_drv.meters
1938
1939 def ceilo_nfvi_metrics(self, vim_id):
1940 """Returns a dict of NFVI metrics for a given VM
1941
1942 Arguments:
1943 vim_id - the VIM ID of the VM to retrieve the metrics for
1944
1945 Returns:
1946 A dict of NFVI metrics
1947
1948 """
1949 def query_latest_sample(counter_name):
1950 try:
1951 filter = json.dumps({
1952 "and": [
1953 {"=": {"resource": vim_id}},
1954 {"=": {"counter_name": counter_name}}
1955 ]
1956 })
1957 orderby = json.dumps([{"timestamp": "DESC"}])
1958 result = self.ceilo_drv.client.query_samples.query(
1959 filter=filter,
1960 orderby=orderby,
1961 limit=1,
1962 )
1963 return result[0]
1964
1965 except IndexError:
1966 pass
1967
1968 except Exception as e:
1969 logger.error("Got exception while querying ceilometer, exception details:%s " %str(e))
1970
1971 return None
1972
1973 memory_usage = query_latest_sample("memory.usage")
1974 disk_usage = query_latest_sample("disk.usage")
1975 cpu_util = query_latest_sample("cpu_util")
1976
1977 metrics = dict()
1978
1979 if memory_usage is not None:
1980 memory_usage.volume = 1e6 * memory_usage.volume
1981 metrics["memory_usage"] = memory_usage.to_dict()
1982
1983 if disk_usage is not None:
1984 metrics["disk_usage"] = disk_usage.to_dict()
1985
1986 if cpu_util is not None:
1987 metrics["cpu_util"] = cpu_util.to_dict()
1988
1989 return metrics
1990
1991 def ceilo_alarm_list(self):
1992 """Returns a list of ceilometer alarms"""
1993 return self.ceilo_drv.client.alarms.list()
1994
1995 def ceilo_alarm_create(self,
1996 name,
1997 meter,
1998 statistic,
1999 operation,
2000 threshold,
2001 period,
2002 evaluations,
2003 severity='low',
2004 repeat=True,
2005 enabled=True,
2006 actions=None,
2007 **kwargs):
2008 """Create a new Alarm
2009
2010 Arguments:
2011 name - the name of the alarm
2012 meter - the name of the meter to measure
2013 statistic - the type of statistic used to trigger the alarm
2014 ('avg', 'min', 'max', 'count', 'sum')
2015 operation - the relational operator that, combined with the
2016 threshold value, determines when the alarm is
2017 triggered ('lt', 'le', 'eq', 'ge', 'gt')
2018 threshold - the value of the statistic that will trigger the
2019 alarm
2020 period - the duration (seconds) over which to evaluate the
2021 specified statistic
2022 evaluations - the number of samples of the meter statistic to
2023 collect when evaluating the threshold
2024 severity - a measure of the urgency or importance of the alarm
2025 ('low', 'moderate', 'critical')
2026 repeat - a flag that indicates whether the alarm should be
2027 triggered once (False) or repeatedly while the alarm
2028 condition is true (True)
2029 enabled - a flag that indicates whether the alarm is enabled
2030 (True) or disabled (False)
2031 actions - a dict specifying the URLs for webhooks. The dict can
2032 have up to 3 keys: 'insufficient_data', 'alarm',
2033 'ok'. Each key is associated with a list of URLs to
2034 webhooks that will be invoked when one of the 3
2035 actions is taken.
2036 kwargs - an arbitrary dict of keyword arguments that are
2037 passed to the ceilometer client
2038
2039 """
2040 ok_actions = actions.get('ok') if actions is not None else None
2041 alarm_actions = actions.get('alarm') if actions is not None else None
2042 insufficient_data_actions = actions.get('insufficient_data') if actions is not None else None
2043
2044 return self.ceilo_drv.client.alarms.create(
2045 name=name,
2046 meter_name=meter,
2047 statistic=statistic,
2048 comparison_operator=operation,
2049 threshold=threshold,
2050 period=period,
2051 evaluation_periods=evaluations,
2052 severity=severity,
2053 repeat_actions=repeat,
2054 enabled=enabled,
2055 ok_actions=ok_actions,
2056 alarm_actions=alarm_actions,
2057 insufficient_data_actions=insufficient_data_actions,
2058 **kwargs
2059 )
2060
2061 def ceilo_alarm_update(self, alarm_id, **kwargs):
2062 """Updates an existing alarm
2063
2064 Arguments:
2065 alarm_id - the identifier of the alarm to update
2066 kwargs - a dict of the alarm attributes to update
2067
2068 """
2069 return self.ceilo_drv.client.alarms.update(alarm_id, **kwargs)
2070
2071 def ceilo_alarm_delete(self, alarm_id):
2072 self.ceilo_drv.client.alarms.delete(alarm_id)
2073
2074 def cinder_volume_list(self):
2075 return self.cinder_drv.volume_list()
2076
2077 def cinder_volume_get(self,vol_id):
2078 return self.cinder_drv.volume_get(vol_id)
2079
2080 def cinder_volume_set_metadata(self, volumeid, metadata):
2081 return self.cinder_drv.volume_set_metadata(volumeid, metadata)
2082
2083 def cinder_volume_delete_metadata(self, volumeid, metadata):
2084 return self.cinder_drv.volume_delete_metadata(volumeid, metadata)
2085
2086
2087
2088 class CinderDriver(object):
2089 """
2090 Driver for openstack cinder-client
2091 """
2092 def __init__(self, ks_drv, service_name, version):
2093 """
2094 Constructor for CinderDriver
2095 Arguments: KeystoneDriver class object
2096 """
2097 self.ks_drv = ks_drv
2098 self._service_name = service_name
2099 self._version = version
2100
2101 def _get_cinder_credentials(self):
2102 """
2103 Returns a dictionary of kwargs required to instantiate python-cinderclient class
2104
2105 Arguments: None
2106
2107 Returns:
2108 A dictionary object of arguments
2109 """
2110 creds = {}
2111 creds['version'] = self._version
2112 creds['username'] = self.ks_drv.get_username()
2113 creds['api_key'] = self.ks_drv.get_password()
2114 creds['auth_url'] = self.ks_drv.get_service_endpoint("identity", "publicURL")
2115 creds['project_id'] = self.ks_drv.get_tenant_name()
2116 creds['insecure'] = self.ks_drv.get_security_mode()
2117
2118 return creds
2119
2120 def _get_cinder_connection(self):
2121 """
2122 Returns a object of class python-cinderclient
2123 """
2124 if not hasattr(self, '_cinder_connection'):
2125 self._cinder_connection = cinder_client.Client(**self._get_cinder_credentials())
2126 else:
2127 # Reinitialize if auth_token is no longer valid
2128 if not self.ks_drv.is_auth_token_valid():
2129 self._cinder_connection = cinder_client.Client(**self._get_cinder_credentials())
2130 return self._cinder_connection
2131
2132 def volume_list(self):
2133 """
2134 Returns list of dictionaries. Each dictionary contains attributes associated with
2135 volumes
2136
2137 Arguments: None
2138
2139 Returns: List of dictionaries.
2140 """
2141 cinderconn = self._get_cinder_connection()
2142 volumes = []
2143 try:
2144 volume_info = cinderconn.volumes.list()
2145 except Exception as e:
2146 logger.error("OpenstackDriver: List volumes operation failed. Exception: %s" %(str(e)))
2147 raise
2148 volumes = [ volume for volume in volume_info ]
2149 return volumes
2150
2151 def volume_get(self, volume_id):
2152 """
2153 Get details volume
2154
2155 Arguments: None
2156
2157 Returns: List of dictionaries.
2158 """
2159 cinderconn = self._get_cinder_connection()
2160 try:
2161 vol = cinderconn.volumes.get(volume_id)
2162 except Exception as e:
2163 logger.error("OpenstackDriver: Get volume operation failed. Exception: %s" %(str(e)))
2164 raise
2165 return vol
2166
2167 def volume_set_metadata(self, volume_id, metadata):
2168 """
2169 Set metadata for volume
2170 Metadata is a dictionary of key-value pairs
2171
2172 Arguments: None
2173
2174 Returns: List of dictionaries.
2175 """
2176 cinderconn = self._get_cinder_connection()
2177 try:
2178 cinderconn.volumes.set_metadata(volume_id, metadata)
2179 except Exception as e:
2180 logger.error("OpenstackDriver: Set metadata operation failed. Exception: %s" %(str(e)))
2181 raise
2182
2183 def volume_delete_metadata(self, volume_id, metadata):
2184 """
2185 Delete metadata for volume
2186 Metadata is a dictionary of key-value pairs
2187
2188 Arguments: None
2189
2190 Returns: List of dictionaries.
2191 """
2192 cinderconn = self._get_cinder_connection()
2193 try:
2194 cinderconn.volumes.delete_metadata(volume_id, metadata)
2195 except Exception as e:
2196 logger.error("OpenstackDriver: Delete metadata operation failed. Exception: %s" %(str(e)))
2197 raise
2198
2199 class CinderDriverV2(CinderDriver):
2200 """
2201 Driver for openstack cinder-client V2
2202 """
2203 def __init__(self, ks_drv):
2204 super(CinderDriverV2, self).__init__(ks_drv, 'volumev2', 2)
2205