Merge "Restricted dependency of the novaclient."
[snaps.git] / snaps / openstack / utils / nova_utils.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
16 from cryptography.hazmat.primitives import serialization
17 from cryptography.hazmat.primitives.asymmetric import rsa
18 from cryptography.hazmat.backends import default_backend
19
20 import os
21 import logging
22 from snaps.openstack.utils import keystone_utils
23
24 from novaclient.client import Client
25 from novaclient.exceptions import NotFound
26
27 __author__ = 'spisarski'
28
29 logger = logging.getLogger('nova_utils')
30
31 """
32 Utilities for basic OpenStack Nova API calls
33 """
34
35
36 def nova_client(os_creds):
37     """
38     Instantiates and returns a client for communications with OpenStack's Nova server
39     :param os_creds: The connection credentials to the OpenStack API
40     :return: the client object
41     """
42     logger.debug('Retrieving Nova Client')
43     return Client(os_creds.compute_api_version, session=keystone_utils.keystone_session(os_creds))
44
45
46 def get_servers_by_name(nova, name):
47     """
48     Returns a list of servers with a given name
49     :param nova: the Nova client
50     :param name: the server name
51     :return: the list of servers
52     """
53     return nova.servers.list(search_opts={'name': name})
54
55
56 def get_latest_server_object(nova, server):
57     """
58     Returns a server with a given id
59     :param nova: the Nova client
60     :param server: the old server object
61     :return: the list of servers or None if not found
62     """
63     return nova.servers.get(server)
64
65
66 def create_keys(key_size=2048):
67     """
68     Generates public and private keys
69     :param key_size: the number of bytes for the key size
70     :return: the cryptography keys
71     """
72     return rsa.generate_private_key(backend=default_backend(), public_exponent=65537,
73                                     key_size=key_size)
74
75
76 def public_key_openssh(keys):
77     """
78     Returns the public key for OpenSSH
79     :param keys: the keys generated by create_keys() from cryptography
80     :return: the OpenSSH public key
81     """
82     return keys.public_key().public_bytes(serialization.Encoding.OpenSSH, serialization.PublicFormat.OpenSSH)
83
84
85 def save_keys_to_files(keys=None, pub_file_path=None, priv_file_path=None):
86     """
87     Saves the generated RSA generated keys to the filesystem
88     :param keys: the keys to save generated by cryptography
89     :param pub_file_path: the path to the public keys
90     :param priv_file_path: the path to the private keys
91     :return: None
92     """
93     if keys:
94         if pub_file_path:
95             pub_dir = os.path.dirname(pub_file_path)
96             if not os.path.isdir(pub_dir):
97                 os.mkdir(pub_dir)
98             public_handle = open(pub_file_path, 'wb')
99             public_bytes = keys.public_key().public_bytes(
100                 serialization.Encoding.OpenSSH, serialization.PublicFormat.OpenSSH)
101             public_handle.write(public_bytes)
102             public_handle.close()
103             os.chmod(pub_file_path, 0o400)
104             logger.info("Saved public key to - " + pub_file_path)
105         if priv_file_path:
106             priv_dir = os.path.dirname(priv_file_path)
107             if not os.path.isdir(priv_dir):
108                 os.mkdir(priv_dir)
109             private_handle = open(priv_file_path, 'wb')
110             private_handle.write(keys.private_bytes(encoding=serialization.Encoding.PEM,
111                                                     format=serialization.PrivateFormat.TraditionalOpenSSL,
112                                                     encryption_algorithm=serialization.NoEncryption()))
113             private_handle.close()
114             os.chmod(priv_file_path, 0o400)
115             logger.info("Saved private key to - " + priv_file_path)
116
117
118 def upload_keypair_file(nova, name, file_path):
119     """
120     Uploads a public key from a file
121     :param nova: the Nova client
122     :param name: the keypair name
123     :param file_path: the path to the public key file
124     :return: the keypair object
125     """
126     with open(os.path.expanduser(file_path), 'rb') as fpubkey:
127         logger.info('Saving keypair to - ' + file_path)
128         return upload_keypair(nova, name, fpubkey.read())
129
130
131 def upload_keypair(nova, name, key):
132     """
133     Uploads a public key from a file
134     :param nova: the Nova client
135     :param name: the keypair name
136     :param key: the public key object
137     :return: the keypair object
138     """
139     logger.info('Creating keypair with name - ' + name)
140     return nova.keypairs.create(name=name, public_key=key.decode('utf-8'))
141
142
143 def keypair_exists(nova, keypair_obj):
144     """
145     Returns a copy of the keypair object if found
146     :param nova: the Nova client
147     :param keypair_obj: the keypair object
148     :return: the keypair object or None if not found
149     """
150     try:
151         return nova.keypairs.get(keypair_obj)
152     except:
153         return None
154
155
156 def get_keypair_by_name(nova, name):
157     """
158     Returns a list of all available keypairs
159     :param nova: the Nova client
160     :param name: the name of the keypair to lookup
161     :return: the keypair object or None if not found
162     """
163     keypairs = nova.keypairs.list()
164
165     for keypair in keypairs:
166         if keypair.name == name:
167             return keypair
168
169     return None
170
171
172 def delete_keypair(nova, key):
173     """
174     Deletes a keypair object from OpenStack
175     :param nova: the Nova client
176     :param key: the keypair object to delete
177     """
178     logger.debug('Deleting keypair - ' + key.name)
179     nova.keypairs.delete(key)
180
181
182 def get_floating_ip_pools(nova):
183     """
184     Returns all of the available floating IP pools
185     :param nova: the Nova client
186     :return: a list of pools
187     """
188     return nova.floating_ip_pools.list()
189
190
191 def get_floating_ips(nova):
192     """
193     Returns all of the floating IPs
194     :param nova: the Nova client
195     :return: a list of floating IPs
196     """
197     return nova.floating_ips.list()
198
199
200 def create_floating_ip(nova, ext_net_name):
201     """
202     Returns the floating IP object that was created with this call
203     :param nova: the Nova client
204     :param ext_net_name: the name of the external network on which to apply the floating IP address
205     :return: the floating IP object
206     """
207     logger.info('Creating floating ip to external network - ' + ext_net_name)
208     return nova.floating_ips.create(ext_net_name)
209
210
211 def get_floating_ip(nova, floating_ip):
212     """
213     Returns a floating IP object that should be identical to the floating_ip parameter
214     :param nova: the Nova client
215     :param floating_ip: the floating IP object to lookup
216     :return: hopefully the same floating IP object input
217     """
218     logger.debug('Attempting to retrieve existing floating ip with IP - ' + floating_ip.ip)
219     return nova.floating_ips.get(floating_ip)
220
221
222 def delete_floating_ip(nova, floating_ip):
223     """
224     Responsible for deleting a floating IP
225     :param nova: the Nova client
226     :param floating_ip: the floating IP object to delete
227     :return:
228     """
229     logger.debug('Attempting to delete existing floating ip with IP - ' + floating_ip.ip)
230     return nova.floating_ips.delete(floating_ip)
231
232
233 def get_nova_availability_zones(nova):
234     """
235     Returns the names of all nova compute servers
236     :param nova: the Nova client
237     :return: a list of compute server names
238     """
239     out = list()
240     zones = nova.availability_zones.list()
241     for zone in zones:
242         if zone.zoneName == 'nova':
243             for key, host in zone.hosts.items():
244                 out.append(zone.zoneName + ':' + key)
245
246     return out
247
248
249 def delete_vm_instance(nova, vm_inst):
250     """
251     Deletes a VM instance
252     :param nova: the nova client
253     :param vm_inst: the OpenStack instance object to delete
254     """
255     nova.servers.delete(vm_inst)
256
257
258 def get_flavor_by_name(nova, name):
259     """
260     Returns a flavor by name
261     :param nova: the Nova client
262     :param name: the flavor name to return
263     :return: the OpenStack flavor object or None if not exists
264     """
265     try:
266         return nova.flavors.find(name=name)
267     except NotFound:
268         return None
269
270
271 def create_flavor(nova, flavor_settings):
272     """
273     Creates and returns and OpenStack flavor object
274     :param nova: the Nova client
275     :param flavor_settings: the flavor settings
276     :return: the Flavor
277     """
278     return nova.flavors.create(name=flavor_settings.name, flavorid=flavor_settings.flavor_id, ram=flavor_settings.ram,
279                                vcpus=flavor_settings.vcpus, disk=flavor_settings.disk,
280                                ephemeral=flavor_settings.ephemeral, swap=flavor_settings.swap,
281                                rxtx_factor=flavor_settings.rxtx_factor, is_public=flavor_settings.is_public)
282
283
284 def delete_flavor(nova, flavor):
285     """
286     Deletes a flavor
287     :param nova: the Nova client
288     :param flavor: the OpenStack flavor object
289     """
290     nova.flavors.delete(flavor)
291
292
293 def add_security_group(nova, vm, security_group_name):
294     """
295     Adds a security group to an existing VM
296     :param nova: the nova client
297     :param vm: the OpenStack server object (VM) to alter
298     :param security_group_name: the name of the security group to add
299     """
300     nova.servers.add_security_group(str(vm.id), security_group_name)
301
302
303 def remove_security_group(nova, vm, security_group):
304     """
305     Removes a security group from an existing VM
306     :param nova: the nova client
307     :param vm: the OpenStack server object (VM) to alter
308     :param security_group: the OpenStack security group object to add
309     """
310     nova.servers.remove_security_group(str(vm.id), security_group['security_group']['name'])