Added the ability to add in configurable metadata to flavor creation.
[snaps.git] / snaps / openstack / create_flavor.py
1 # Copyright (c) 2016 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 from novaclient.exceptions import NotFound
18
19 from snaps.openstack.utils import nova_utils
20
21 __author__ = 'spisarski'
22
23 logger = logging.getLogger('create_image')
24
25 DEFAULT_METADATA = {'hw:mem_page_size': 'any'}
26
27
28 class OpenStackFlavor:
29     """
30     Class responsible for creating a user in OpenStack
31     """
32
33     def __init__(self, os_creds, flavor_settings):
34         """
35         Constructor
36         :param os_creds: The OpenStack connection credentials
37         :param flavor_settings: The flavor settings
38         :return:
39         """
40         self.__os_creds = os_creds
41         self.flavor_settings = flavor_settings
42         self.__flavor = None
43         self.__nova = nova_utils.nova_client(self.__os_creds)
44
45     def create(self, cleanup=False):
46         """
47         Creates the image in OpenStack if it does not already exist
48         :param cleanup: Denotes whether or not this is being called for cleanup or not
49         :return: The OpenStack flavor object
50         """
51         self.__flavor = nova_utils.get_flavor_by_name(self.__nova, self.flavor_settings.name)
52         if self.__flavor:
53             logger.info('Found flavor with name - ' + self.flavor_settings.name)
54         elif not cleanup:
55             self.__flavor = nova_utils.create_flavor(self.__nova, self.flavor_settings)
56             if self.flavor_settings.metadata:
57                 self.__flavor.set_keys(self.flavor_settings.metadata)
58             self.__flavor = nova_utils.get_flavor_by_name(self.__nova, self.flavor_settings.name)
59         else:
60             logger.info('Did not create flavor due to cleanup mode')
61
62         return self.__flavor
63
64     def clean(self):
65         """
66         Cleanse environment of all artifacts
67         :return: void
68         """
69         if self.__flavor:
70             try:
71                 nova_utils.delete_flavor(self.__nova, self.__flavor)
72             except NotFound:
73                 pass
74
75             self.__flavor = None
76
77     def get_flavor(self):
78         """
79         Returns the OpenStack flavor object
80         :return:
81         """
82         return self.__flavor
83
84
85 class FlavorSettings:
86     """
87     Configuration settings for OpenStack flavor creation
88     """
89
90     def __init__(self, config=None, name=None, flavor_id='auto', ram=None, disk=None, vcpus=None, ephemeral=0, swap=0,
91                  rxtx_factor=1.0, is_public=True, metadata=DEFAULT_METADATA):
92         """
93         Constructor
94         :param config: dict() object containing the configuration settings using the attribute names below as each
95                        member's the key and overrides any of the other parameters.
96         :param name: the flavor's name (required)
97         :param flavor_id: the string ID (default 'auto')
98         :param ram: the required RAM in MB (required)
99         :param disk: the size of the root disk in GB (required)
100         :param vcpus: the number of virtual CPUs (required)
101         :param ephemeral: the size of the ephemeral disk in GB (default 0)
102         :param swap: the size of the dedicated swap disk in GB (default 0)
103         :param rxtx_factor: the receive/transmit factor to be set on ports if backend supports
104                             QoS extension (default 1.0)
105         :param is_public: denotes whether or not the flavor is public (default True)
106         :param metadata: freeform dict() for special metadata (default hw:mem_page_size=any)
107         """
108
109         if config:
110             self.name = config.get('name')
111
112             if config.get('flavor_id'):
113                 self.flavor_id = config['flavor_id']
114             else:
115                 self.flavor_id = flavor_id
116
117             self.ram = config.get('ram')
118             self.disk = config.get('disk')
119             self.vcpus = config.get('vcpus')
120
121             if config.get('ephemeral'):
122                 self.ephemeral = config['ephemeral']
123             else:
124                 self.ephemeral = ephemeral
125
126             if config.get('swap'):
127                 self.swap = config['swap']
128             else:
129                 self.swap = swap
130
131             if config.get('rxtx_factor'):
132                 self.rxtx_factor = config['rxtx_factor']
133             else:
134                 self.rxtx_factor = rxtx_factor
135
136             if config.get('is_public') is not None:
137                 self.is_public = config['is_public']
138             else:
139                 self.is_public = is_public
140
141             if config.get('metadata'):
142                 self.metadata = config['metadata']
143             else:
144                 self.metadata = metadata
145         else:
146             self.name = name
147             self.flavor_id = flavor_id
148             self.ram = ram
149             self.disk = disk
150             self.vcpus = vcpus
151             self.ephemeral = ephemeral
152             self.swap = swap
153             self.rxtx_factor = rxtx_factor
154             self.is_public = is_public
155             self.metadata = metadata
156
157         if not self.name or not self.ram or not self.disk or not self.vcpus:
158             raise Exception('The attributes name, ram, disk, and vcpus are required for FlavorSettings')
159
160         if not isinstance(self.ram, int):
161             raise Exception('The ram attribute must be a integer')
162
163         if not isinstance(self.disk, int):
164             raise Exception('The ram attribute must be a integer')
165
166         if not isinstance(self.vcpus, int):
167             raise Exception('The vcpus attribute must be a integer')
168
169         if self.ephemeral and not isinstance(self.ephemeral, int):
170             raise Exception('The ephemeral attribute must be an integer')
171
172         if self.swap and not isinstance(self.swap, int):
173             raise Exception('The swap attribute must be an integer')
174
175         if self.rxtx_factor and not isinstance(self.rxtx_factor, (int, float)):
176             raise Exception('The is_public attribute must be an integer or float')
177
178         if self.is_public and not isinstance(self.is_public, bool):
179             raise Exception('The is_public attribute must be a boolean')