-
Notifications
You must be signed in to change notification settings - Fork 33
/
service_instance.py
337 lines (301 loc) · 12.1 KB
/
service_instance.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
try:
from heat.common.i18n import _
except ImportError:
pass
from heat.engine import constraints
from heat.engine import clients
from heat.engine import properties
try:
from heat.openstack.common import log as logging
except ImportError:
from oslo_log import log as logging
from heat.engine import scheduler
from heat.openstack.common import log as logging
from vnc_api import vnc_api
from contrail_heat.resources.contrail import ContrailResource
import uuid
LOG = logging.getLogger(__name__)
class HeatServiceInstance(ContrailResource):
PROPERTIES = (
NAME, SERVICE_TEMPLATE, AUTO_POLICY, AVAILABILITY_ZONE,
INTERFACE_LIST, SCALE_OUT, HA_MODE
) = (
'name', 'service_template', 'auto_policy', 'availability_zone',
'interface_list', 'scale_out', 'ha_mode'
)
_INTERFACE_LIST_KEYS = (
VIRTUAL_NETWORK, IP_ADDRESS, STATIC_ROUTES
) = (
'virtual_network', 'ip_address', 'static_routes'
)
_STATIC_ROUTE_KEYS = (
PREFIX, NEXT_HOP, NEXT_HOP_TYPE
) = (
'prefix', 'next_hop', 'next_hop_type'
)
_SCALE_OUT_KEYS = (
MAX_INSTANCES, AUTO_SCALE
) = (
'max_instances', 'auto_scale'
)
properties_schema = {
NAME: properties.Schema(
properties.Schema.STRING,
_('Service Instance name.'),
required=True,
update_allowed=False
),
SERVICE_TEMPLATE: properties.Schema(
properties.Schema.STRING,
_('Service Template name.'),
required=True,
update_allowed=False
),
AUTO_POLICY: properties.Schema(
properties.Schema.BOOLEAN,
_('Auto policy'),
default=False,
update_allowed=False
),
AVAILABILITY_ZONE: properties.Schema(
properties.Schema.STRING,
_('Availability Zone.'),
update_allowed=False
),
INTERFACE_LIST: properties.Schema(
properties.Schema.LIST,
_('An ordered list of interfaces to be added to this '
'service instance'),
schema=properties.Schema(
properties.Schema.MAP,
schema={
VIRTUAL_NETWORK: properties.Schema(
properties.Schema.STRING,
_('Virtual Network for interface'),
),
IP_ADDRESS: properties.Schema(
properties.Schema.STRING,
_('IP for this interface')
),
STATIC_ROUTES: properties.Schema(
properties.Schema.LIST,
_('An ordered list of static routes to be added '
'to this interface'),
schema=properties.Schema(
properties.Schema.MAP,
schema={
PREFIX: properties.Schema(
properties.Schema.STRING,
_('Route prefix'),
),
NEXT_HOP: properties.Schema(
properties.Schema.STRING,
_('Nexthop'),
),
NEXT_HOP_TYPE: properties.Schema(
properties.Schema.STRING,
_('Nexthop Type'),
)
}
)
),
}
),
update_allowed=False
),
SCALE_OUT: properties.Schema(
properties.Schema.MAP,
_('Scale out property'),
update_allowed=True,
schema={
MAX_INSTANCES: properties.Schema(
properties.Schema.INTEGER,
_('Number of instances of service instance'),
default=1,
),
AUTO_SCALE: properties.Schema(
properties.Schema.BOOLEAN,
_('Whether to auto scale the service instance'),
default=False,
)
}
),
HA_MODE: properties.Schema(
properties.Schema.STRING,
_('High availability mode'),
constraints=[
constraints.AllowedValues(['active-active', 'active-standby']),
],
),
}
attributes_schema = {
"name": _("The name of the Service Instance."),
"fq_name": _("The FQ name of the Service Instance."),
"status": _("Status of the Service Instance."),
"service_template": _("Service Template of the Service Instance."),
"virtual_machines": _("Service VMs for the Service Instance."),
"active_service_vms": _("Number of service VMs active for this "
"Service Instance."),
"tenant_id": _("Tenant id of the Service Instance."),
"show": _("All attributes."),
}
update_allowed_keys = ('Properties',)
def handle_create(self):
try:
st_obj = self.vnc_lib().service_template_read(
id=self.properties[self.SERVICE_TEMPLATE])
except vnc_api.NoIdError:
st_obj = self.vnc_lib().service_template_read(
fq_name_str=self.properties[self.SERVICE_TEMPLATE])
tenant_id = self.stack.context.tenant_id
project_obj = self.vnc_lib().project_read(id=str(uuid.UUID(tenant_id)))
si_obj = vnc_api.ServiceInstance(
name=self.properties[self.NAME], parent_obj=project_obj)
svc_tmpl_if_list = st_obj.get_service_template_properties().interface_type
svc_inst_if_list = self.properties[self.INTERFACE_LIST]
if len(svc_tmpl_if_list) != len(svc_inst_if_list):
raise vnc_api.BadRequest
if_index = 0
si_prop = vnc_api.ServiceInstanceType()
for intf in self.properties[self.INTERFACE_LIST]:
virt_net = intf[self.VIRTUAL_NETWORK]
if virt_net == "auto":
vn_name = ""
elif not ":" in virt_net:
fq_name = self.vnc_lib().id_to_fq_name(virt_net)
vn_name = ":".join(fq_name)
else:
vn_name = virt_net
# now check for static routes on this interface
routes_list = {}
if svc_tmpl_if_list[if_index].get_static_route_enable(
) and self.STATIC_ROUTES in intf:
routes_list['route'] = intf[self.STATIC_ROUTES]
if_type = vnc_api.ServiceInstanceInterfaceType(
virtual_network=vn_name,static_routes=routes_list or None)
si_prop.add_interface_list(if_type)
if_index = if_index + 1
if self.properties[self.SCALE_OUT] is None:
max_instances = 1
auto_scale = False
else:
max_instances = self.properties[self.SCALE_OUT][self.MAX_INSTANCES]
auto_scale = self.properties[self.SCALE_OUT][self.AUTO_SCALE]
scale_out = vnc_api.ServiceScaleOutType(max_instances=max_instances,
auto_scale=auto_scale)
si_prop.set_scale_out(scale_out)
si_prop.set_availability_zone(self.properties[self.AVAILABILITY_ZONE])
si_prop.set_ha_mode(self.properties[self.HA_MODE])
si_obj.set_service_instance_properties(si_prop)
st_obj = self.vnc_lib().service_template_read(id=st_obj.uuid)
si_obj.set_service_template(st_obj)
si_uuid = self.vnc_lib().service_instance_create(si_obj)
self.resource_id_set(si_uuid)
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
try:
si_obj = self.vnc_lib().service_instance_read(id=self.resource_id)
except vnc_api.NoIdError:
LOG.warn(_("Service Instance %s not found.") % self.name)
raise
except:
LOG.warn(_("Unknown error."))
raise
si_prop = si_obj.get_service_instance_properties()
scaleprop = prop_diff.get(self.SCALE_OUT)
if scaleprop:
max_instances = scaleprop.get(self.MAX_INSTANCES)
auto_scale = scaleprop.get(self.AUTO_SCALE)
scale_out = vnc_api.ServiceScaleOutType(max_instances=max_instances,
auto_scale=auto_scale)
si_prop.set_scale_out(scale_out)
si_obj.set_service_instance_properties(si_prop)
self.vnc_lib().service_instance_update(si_obj)
def get_vm(self, vm):
'''
Refresh vm's attributes and log warnings for non-critical API errors.
'''
try:
vm.get()
except clients.novaclient.exceptions.OverLimit as exc:
msg = _("Server %(name)s (%(id)s) received an OverLimit "
"response during vm.get(): %(exception)s")
logger.warning(msg % {'name': vm.name,
'id': vm.id,
'exception': str(exc)})
except clients.novaclient.exceptions.ClientException as exc:
if ((getattr(exc, 'http_status', getattr(exc, 'code', None)) in
(500, 503))):
msg = _('Server "%(name)s" (%(id)s) received the following '
'exception during vm.get(): %(exception)s')
logger.warning(msg % {'name': vm.name,
'id': vm.id,
'exception': str(exc)})
else:
raise
def delete_vm(self, vm):
'''
Return a routine that deletes the vm and waits for it to
disappear from Nova.
'''
while True:
yield
try:
self.get_vm(vm)
except clients.novaclient.exceptions.NotFound:
break
def handle_delete(self):
try:
# get the servers list
si_obj = self.vnc_lib().service_instance_read(id=self.resource_id)
vm_uuid_list = list(si_obj.get_virtual_machine_back_refs() or [])
self.vnc_lib().service_instance_delete(id=self.resource_id)
for vm_uuid in vm_uuid_list or []:
try:
vm = self.nova().servers.get(vm_uuid['to'][0])
except clients.novaclient.exceptions.NotFound:
pass
else:
delete = scheduler.TaskRunner(self.delete_vm, vm)
delete(wait_time=1.0)
except vnc_api.NoIdError:
LOG.warn(_("Service Instance %s not found.") % self.name)
except:
LOG.warn(_("Unknown error."))
raise
def _show_resource(self):
si_obj = self.vnc_lib().service_instance_read(id=self.resource_id)
dict = {}
dict['name'] = si_obj.get_display_name()
dict['fq_name'] = si_obj.get_fq_name_str()
dict['tenant_id'] = si_obj.parent_uuid
svms = []
status = 'INACTIVE'
inactive_count = 0
active_count = 0
for vms in si_obj.get_virtual_machine_back_refs() or []:
svm = {}
vm = self.nova().servers.get(vms['to'][0])
svm['vm_id'] = vm.id
svm['name'] = vm.name
svm['status'] = vm.status
svms.append(svm)
if vm.status == 'ACTIVE':
active_count += 1
else:
inactive_count += 1
dict['virtual_machines'] = svms
if inactive_count and active_count:
status = "PARTIALLY ACTIVE"
elif active_count == 0:
status = "INACTIVE"
elif active_count == len(si_obj.get_virtual_machine_back_refs() or []):
status = "ACTIVE"
dict['status'] = status
dict['active_service_vms'] = active_count
dict['service_template'] = ':'.join(
si_obj.get_service_template_refs()[0]['to'])
return dict
def resource_mapping():
return {
'OS::Contrail::ServiceInstance': HeatServiceInstance,
}