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