d0b69cd27fce73e804cd3ae185f69e2c3ce0bb4b
[snaps.git] / snaps / openstack / create_keypairs.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import logging
16
17 import os
18 from neutronclient.common.utils import str2bool
19 from novaclient.exceptions import NotFound
20
21 from snaps import file_utils
22 from snaps.openstack.utils import nova_utils
23
24 __author__ = 'spisarski'
25
26 logger = logging.getLogger('OpenStackKeypair')
27
28
29 class OpenStackKeypair:
30     """
31     Class responsible for creating a keypair in OpenStack
32     """
33
34     def __init__(self, os_creds, keypair_settings):
35         """
36         Constructor - all parameters are required
37         :param os_creds: The credentials to connect with OpenStack
38         :param keypair_settings: The settings used to create a keypair
39         """
40         self.__nova = None
41         self.__os_creds = os_creds
42         self.keypair_settings = keypair_settings
43         self.__nova = nova_utils.nova_client(os_creds)
44         self.__delete_keys_on_clean = True
45
46         # Attributes instantiated on create()
47         self.__keypair = None
48
49     def create(self, cleanup=False):
50         """
51         Responsible for creating the keypair object.
52         :param cleanup: Denotes whether or not this is being called for cleanup
53                         or not
54         """
55         self.__nova = nova_utils.nova_client(self.__os_creds)
56
57         logger.info('Creating keypair %s...' % self.keypair_settings.name)
58
59         try:
60             self.__keypair = nova_utils.get_keypair_by_name(
61                 self.__nova, self.keypair_settings.name)
62         except Exception as e:
63             logger.warn('Cannot load existing keypair - %s', e)
64             return
65
66         if not self.__keypair and not cleanup:
67             if self.keypair_settings.public_filepath and os.path.isfile(
68                     self.keypair_settings.public_filepath):
69                 logger.info("Uploading existing keypair")
70                 self.__keypair = nova_utils.upload_keypair_file(
71                     self.__nova, self.keypair_settings.name,
72                     self.keypair_settings.public_filepath)
73
74                 if self.keypair_settings.delete_on_clean is not None:
75                     delete_on_clean = self.keypair_settings.delete_on_clean
76                     self.__delete_keys_on_clean = delete_on_clean
77                 else:
78                     self.__delete_keys_on_clean = False
79             else:
80                 logger.info("Creating new keypair")
81                 keys = nova_utils.create_keys(self.keypair_settings.key_size)
82                 self.__keypair = nova_utils.upload_keypair(
83                     self.__nova, self.keypair_settings.name,
84                     nova_utils.public_key_openssh(keys))
85                 file_utils.save_keys_to_files(
86                     keys, self.keypair_settings.public_filepath,
87                     self.keypair_settings.private_filepath)
88
89                 if self.keypair_settings.delete_on_clean is not None:
90                     delete_on_clean = self.keypair_settings.delete_on_clean
91                     self.__delete_keys_on_clean = delete_on_clean
92                 else:
93                     self.__delete_keys_on_clean = True
94         elif self.__keypair and not os.path.isfile(
95                 self.keypair_settings.private_filepath):
96             logger.warn("The public key already exist in OpenStack \
97                         but the private key file is not found ..")
98
99         return self.__keypair
100
101     def clean(self):
102         """
103         Removes and deletes the keypair.
104         """
105         if self.__keypair:
106             try:
107                 nova_utils.delete_keypair(self.__nova, self.__keypair)
108             except NotFound:
109                 pass
110             self.__keypair = None
111
112         if self.__delete_keys_on_clean:
113             if (self.keypair_settings.public_filepath and
114                     file_utils.file_exists(
115                         self.keypair_settings.public_filepath)):
116                 expanded_path = os.path.expanduser(
117                     self.keypair_settings.public_filepath)
118                 os.chmod(expanded_path, 0o755)
119                 os.remove(expanded_path)
120             if (self.keypair_settings.private_filepath and
121                     file_utils.file_exists(
122                         self.keypair_settings.private_filepath)):
123                 expanded_path = os.path.expanduser(
124                     self.keypair_settings.private_filepath)
125                 os.chmod(expanded_path, 0o755)
126                 os.remove(expanded_path)
127
128     def get_keypair(self):
129         """
130         Returns the OpenStack keypair object
131         :return:
132         """
133         return self.__keypair
134
135
136 class KeypairSettings:
137     """
138     Class representing a keypair configuration
139     """
140
141     def __init__(self, **kwargs):
142         """
143         Constructor - all parameters are optional
144         :param name: The keypair name.
145         :param public_filepath: The path to/from the filesystem where the
146                                 public key file is or will be stored
147         :param private_filepath: The path where the generated private key file
148                                  will be stored
149         :param key_size: The number of bytes for the key size when it needs to
150                          be generated (Must be >=512 default 1024)
151         :param delete_on_clean: when True, the key files will be deleted when
152                                 OpenStackKeypair#clean() is called
153         :return:
154         """
155
156         self.name = kwargs.get('name')
157         self.public_filepath = kwargs.get('public_filepath')
158         self.private_filepath = kwargs.get('private_filepath')
159         self.key_size = int(kwargs.get('key_size', 1024))
160
161         if kwargs.get('delete_on_clean') is not None:
162             if isinstance(kwargs.get('delete_on_clean'), bool):
163                 self.delete_on_clean = kwargs.get('delete_on_clean')
164             else:
165                 self.delete_on_clean = str2bool(kwargs.get('delete_on_clean'))
166         else:
167             self.delete_on_clean = None
168
169         if not self.name:
170             raise KeypairSettingsError('Name is a required attribute')
171
172         if self.key_size < 512:
173             raise KeypairSettingsError('key_size must be >=512')
174
175
176 class KeypairSettingsError(Exception):
177     """
178     Exception to be thrown when keypair settings are incorrect
179     """