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