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