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