Merge from OSM SO master
[osm/SO.git] / rwlaunchpad / plugins / rwnsm / rift / tasklets / rwnsmtasklet / scale_group.py
1
2 #
3 # Copyright 2016-2017 RIFT.IO Inc
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17
18 import time
19
20 from enum import Enum
21
22 from gi.repository import (
23 ProjectNsdYang as NsdYang,
24 NsrYang
25 )
26
27
28 class ScalingGroupIndexExists(Exception):
29 pass
30
31
32 class ScaleGroupTrigger(Enum):
33 """ Trigger for scaling config """
34 PRE_SCALE_IN = 1
35 POST_SCALE_IN = 2
36 PRE_SCALE_OUT = 3
37 POST_SCALE_OUT = 4
38
39
40 class ScaleGroupState(Enum):
41 """ Scaling group state """
42 RUNNING = 1
43 SCALING_IN = 2
44 SCALING_OUT = 3
45
46
47 class ScalingGroup(object):
48 """ This represents a configured NSR scaling group """
49 def __init__(self, log, group_msg):
50 """ Create a ScalingGroup instance
51
52 This class is responsible for representing a configured scaling group
53 which is present within an NSR.
54
55 :param log: A logger instance
56 :param group_msg: A NSD scaling group pb message
57 """
58 self._log = log
59 self._group_msg = group_msg
60
61 self._instances = {}
62
63 def __str__(self):
64 return "ScalingGroup(%s)" % self.name
65
66 @property
67 def name(self):
68 """ Name of the scaling group """
69 return self._group_msg.name
70
71 @property
72 def state(self):
73 """ State of the scaling group """
74 state = ScaleGroupState.RUNNING
75 for instance in self._instances.values():
76 if instance.operational_status in ["init", "vnf_init_phase"]:
77 self._log.debug("Scaling instance %s in scaling-out state: %s",
78 instance, instance.operational_status)
79 state = ScaleGroupState.SCALING_OUT
80
81 elif instance.operational_status in ["terminate", "vnf_terminate_phase"]:
82 self._log.debug("Scaling instance %s in scaling-in state: %s",
83 instance, instance.operational_status)
84 state = ScaleGroupState.SCALING_IN
85
86 return state
87
88 @property
89 def vnf_index_count_map(self):
90 """ The mapping of member_vnf_index_ref to count"""
91 return {mbr.member_vnf_index_ref: mbr.count for mbr in self._group_msg.vnfd_member}
92
93 @property
94 def group_msg(self):
95 """ Return the scale group PB message """
96 return self._group_msg
97
98 @property
99 def min_instance_count(self):
100 """ Minimum (and default) number of instance of the scaling group """
101 return self._group_msg.min_instance_count
102
103 @property
104 def max_instance_count(self):
105 """ Maximum number of instance of the scaling group """
106 return self._group_msg.max_instance_count
107
108 def create_record_msg(self):
109 """ Returns a NSR Scaling group record """
110 msg = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ScalingGroupRecord(
111 scaling_group_name_ref=self.name,
112 )
113
114 for instance in self.instances:
115 msg.instance.append(instance.create_record_msg())
116
117 return msg
118
119 @property
120 def instances(self):
121 return self._instances.values()
122
123 def get_instance(self, instance_id):
124 """ Get a scaling group instance
125
126 :param instance_id: The instance's instance_id
127 """
128 return self._instances[instance_id]
129
130 def create_instance(self, instance_id, is_default=False):
131 """ Create a scaling group instance
132
133 :param instance_id: The new instance's instance_id
134 """
135 self._log.debug("Creating %s instance instance_id %s ", self, instance_id)
136
137 if instance_id in self._instances:
138 raise ScalingGroupIndexExists("%s instance_id %s already exists" % (self, instance_id))
139
140 instance = ScalingGroupInstance(
141 log=self._log,
142 group_name=self.name,
143 instance_id=instance_id,
144 is_default=is_default,
145 )
146
147 self._instances[instance_id] = instance
148
149 return instance
150
151 def delete_instance(self, instance_id):
152 self._log.debug("Deleting %s instance instance_id %s ", self, instance_id)
153 del self._instances[instance_id]
154
155 def trigger_map(self, trigger):
156 trig_map = {
157 NsdYang.ScalingTrigger.PRE_SCALE_IN : 'pre_scale_in',
158 NsdYang.ScalingTrigger.POST_SCALE_IN : 'post_scale_in',
159 NsdYang.ScalingTrigger.PRE_SCALE_OUT : 'pre_scale_out',
160 NsdYang.ScalingTrigger.POST_SCALE_OUT : 'post_scale_out',
161 }
162
163 try:
164 return trig_map[trigger]
165 except Exception as e:
166 self._log.error("Unknown scaling group trigger passed: {}".format(trigger))
167 self._log.exception(e)
168
169 def trigger_config(self, trigger):
170 """ Get the config action for the trigger """
171 self._log.debug("Trigger config {}: {}".format(trigger, self._group_msg))
172 trig = self.trigger_map(trigger)
173 if trig is None:
174 return
175
176 for config in self._group_msg.scaling_config_action:
177 if trig == config.trigger:
178 return config
179
180
181 class ScalingGroupInstance(object):
182 """ This class represents a configured NSR Scaling Group instance"""
183
184 valid_status_list = (
185 "init",
186 "vnf_init_phase",
187 "running",
188 "terminate",
189 "vnf_terminate_phase",
190 "terminated",
191 "failed",
192 )
193
194 valid_config_status_list = (
195 "configuring",
196 "configured",
197 "failed",
198 )
199
200 def __init__(self, log, group_name, instance_id, is_default=False):
201 self._log = log
202 self._group_name = group_name
203 self._instance_id = instance_id
204 self._is_default = is_default
205
206 self._vnfrs = {}
207
208 self._create_time = int(time.time())
209 self._op_status = "init"
210 self._config_status = "configuring"
211 self._config_err_msg = None
212
213 def __str__(self):
214 return "ScalingGroupInstance(%s #%s)" % (self._group_name, self.instance_id)
215
216 @property
217 def operational_status(self):
218 return self._op_status
219
220 @operational_status.setter
221 def operational_status(self, op_status):
222 if op_status not in ScalingGroupInstance.valid_status_list:
223 raise ValueError("Invalid scaling group instance status: %s", op_status)
224
225 self._op_status = op_status
226
227 @property
228 def config_status(self):
229 return self._config_status
230
231 @config_status.setter
232 def config_status(self, status):
233 if status not in ScalingGroupInstance.valid_config_status_list:
234 raise ValueError("%s, invalid status: %s",
235 self, status)
236
237 self._config_status = status
238
239 @property
240 def config_err_msg(self):
241 return self._config_err_msg
242
243 @config_err_msg.setter
244 def config_err_msg(self, msg):
245 if self.config_err_msg is not None:
246 self._log.info("%s, overwriting previous config error msg '%s' with '%s'",
247 self, self.config_err_msg, msg)
248
249 self._config_err_msg = msg
250
251 @property
252 def instance_id(self):
253 return self._instance_id
254
255 @property
256 def is_default(self):
257 return self._is_default
258
259 @property
260 def vnfrs(self):
261 """ Return all VirtualNetworkFunctionRecord's that have been added"""
262 return self._vnfrs.values()
263
264 def create_record_msg(self):
265 msg = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ScalingGroupRecord_Instance(
266 instance_id=self._instance_id,
267 create_time=self._create_time,
268 op_status=self._op_status,
269 config_status=self._config_status,
270 error_msg=self._config_err_msg,
271 is_default=self._is_default
272 )
273
274 for vnfr in self.vnfrs:
275 msg.vnfrs.append(vnfr.id)
276
277 return msg
278
279 def add_vnfr(self, vnfr):
280 """ Add a VirtualNetworkFunctionRecord"""
281 self._log.debug("Added %s to %s", vnfr, self)
282 self._vnfrs[vnfr.id] = vnfr
283