Skip to content

Commit e583e31

Browse files
authored
Merge pull request #1439 from grycap/faas_tosca_delete
Faas tosca delete #1438
2 parents c26b8aa + 7c526ab commit e583e31

File tree

4 files changed

+174
-39
lines changed

4 files changed

+174
-39
lines changed

IM/REST.py

+4
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,10 @@ def RESTAddResource(infid=None):
773773

774774
vm_ids = InfrastructureManager.AddResource(infid, radl_data, auth, context)
775775

776+
# If there are no changes in the infra, launch a reconfigure
777+
if not remove_list and not vm_ids and context:
778+
InfrastructureManager.Reconfigure(infid, "", auth)
779+
776780
# Replace the TOSCA document
777781
if tosca_data:
778782
sel_inf = InfrastructureManager.get_infrastructure(infid, auth)

IM/tosca/Tosca.py

+75-39
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ def to_radl(self, inf_info=None):
136136
Tosca.logger.warn("Unknown tyoe of token %s. Ignoring." % token_type)
137137
radl.ansible_hosts = [ansible_host]
138138
elif root_type == "tosca.nodes.aisprint.FaaS.Function":
139+
min_instances, _, default_instances, count, removal_list = self._get_scalable_properties(node)
140+
num_instances = self._get_num_instances(count, default_instances, min_instances)
141+
if num_instances not in [0, 1]:
142+
raise Exception("Scalability values of %s only allows 0 or 1." % root_type)
143+
139144
oscar_host = self._find_host_compute(node, self.tosca.nodetemplates,
140145
"tosca.nodes.SoftwareComponent")
141146
oscar_compute = self._find_host_compute(node, self.tosca.nodetemplates)
@@ -150,13 +155,22 @@ def to_radl(self, inf_info=None):
150155
service_password = self._final_function_result(oscar_host.get_property_value('password'),
151156
oscar_host)
152157

153-
recipe = ' - tasks:\n'
154-
recipe += ' - include_tasks: utils/tasks/oscar_function.yml\n'
155-
recipe += ' vars:\n'
156-
recipe += ' oscar_endpoint: "%s"\n' % service_endpoint
157-
recipe += ' oscar_username: "oscar"\n'
158-
recipe += ' oscar_password: "%s"\n' % service_password
159-
recipe += " oscar_service_json: '%s'\n" % service_json
158+
if num_instances <= 0:
159+
recipe = ' - tasks:\n'
160+
recipe += ' - include_tasks: utils/tasks/del_oscar_function.yml\n'
161+
recipe += ' vars:\n'
162+
recipe += ' oscar_endpoint: "%s"\n' % service_endpoint
163+
recipe += ' oscar_username: "oscar"\n'
164+
recipe += ' oscar_password: "%s"\n' % service_password
165+
recipe += " oscar_service_name: '%s'\n" % service_json["name"]
166+
else:
167+
recipe = ' - tasks:\n'
168+
recipe += ' - include_tasks: utils/tasks/oscar_function.yml\n'
169+
recipe += ' vars:\n'
170+
recipe += ' oscar_endpoint: "%s"\n' % service_endpoint
171+
recipe += ' oscar_username: "oscar"\n'
172+
recipe += ' oscar_password: "%s"\n' % service_password
173+
recipe += " oscar_service_json: '%s'\n" % json.dumps(service_json)
160174

161175
conf = configure("oscar_%s" % node.name, recipe)
162176
radl.configures.append(conf)
@@ -165,14 +179,26 @@ def to_radl(self, inf_info=None):
165179
else:
166180
# if not create a system
167181
oscar_sys = self._gen_oscar_system(node)
168-
radl.systems.append(oscar_sys)
169-
conf = configure(node.name, None)
170-
radl.configures.append(conf)
171-
level = Tosca._get_dependency_level(node)
172-
cont_items.append(contextualize_item(node.name, conf.name, level))
173-
cloud_id = self._get_placement_property(oscar_sys.name, "cloud_id")
174-
dep = deploy(oscar_sys.name, 1, cloud_id)
175-
radl.deploys.append(dep)
182+
183+
current_num_instances = self._get_current_num_instances(oscar_sys.name, inf_info)
184+
num_instances = num_instances - current_num_instances
185+
Tosca.logger.debug("User requested %d instances of type %s and there"
186+
" are %s" % (num_instances, oscar_sys.name, current_num_instances))
187+
188+
if num_instances < 0:
189+
vm_ids = self._get_vm_ids_to_remove(removal_list, num_instances, inf_info, oscar_sys)
190+
if vm_ids:
191+
all_removal_list.extend(vm_ids)
192+
Tosca.logger.debug("List of FaaS to delete: %s" % vm_ids)
193+
else:
194+
radl.systems.append(oscar_sys)
195+
conf = configure(node.name, None)
196+
radl.configures.append(conf)
197+
level = Tosca._get_dependency_level(node)
198+
cont_items.append(contextualize_item(node.name, conf.name, level))
199+
cloud_id = self._get_placement_property(oscar_sys.name, "cloud_id")
200+
dep = deploy(oscar_sys.name, 1, cloud_id)
201+
radl.deploys.append(dep)
176202
else:
177203
if root_type == "tosca.nodes.Compute":
178204
# Add the system RADL element
@@ -184,33 +210,17 @@ def to_radl(self, inf_info=None):
184210
# Add the deploy element for this system
185211
min_instances, _, default_instances, count, removal_list = self._get_scalable_properties(
186212
node)
187-
if count is not None:
188-
# we must check the correct number of instances to
189-
# deploy
190-
num_instances = count
191-
elif default_instances is not None:
192-
num_instances = default_instances
193-
elif min_instances is not None:
194-
num_instances = min_instances
195-
else:
196-
num_instances = 1
197-
198-
current_num_instances = self._get_num_instances(sys.name, inf_info)
213+
num_instances = self._get_num_instances(count, default_instances, min_instances)
214+
current_num_instances = self._get_current_num_instances(sys.name, inf_info)
199215
num_instances = num_instances - current_num_instances
200216
Tosca.logger.debug("User requested %d instances of type %s and there"
201217
" are %s" % (num_instances, sys.name, current_num_instances))
202218

203219
if num_instances < 0:
204-
if removal_list:
205-
# TODO: Think about to check the IDs of the VMs
206-
all_removal_list.extend([int(vm_id) for vm_id in removal_list[0:-num_instances]])
207-
# if no removal list is set, delete the oldest VMs of this type
208-
elif inf_info:
209-
vm_list = inf_info.get_vm_list_by_system_name()
210-
if sys.name in vm_list:
211-
vms = sorted(vm_list[sys.name], key=lambda vm: vm.creation_date)
212-
all_removal_list.extend([vm.im_id for vm in vms[0:-num_instances]])
213-
Tosca.logger.debug("List of VMs to delete: %s" % all_removal_list)
220+
vm_ids = self._get_vm_ids_to_remove(removal_list, num_instances, inf_info, sys)
221+
if vm_ids:
222+
all_removal_list.extend(vm_ids)
223+
Tosca.logger.debug("List of VMs to delete: %s" % vm_ids)
214224

215225
if num_instances > 0:
216226
cloud_id = self._get_placement_property(sys.name, "cloud_id")
@@ -246,6 +256,32 @@ def to_radl(self, inf_info=None):
246256

247257
return all_removal_list, self._complete_radl_networks(radl)
248258

259+
@staticmethod
260+
def _get_num_instances(count, default_instances, min_instances):
261+
# we must check the correct number of instances to deploy
262+
if count is not None:
263+
num_instances = count
264+
elif default_instances is not None:
265+
num_instances = default_instances
266+
elif min_instances is not None:
267+
num_instances = min_instances
268+
else:
269+
num_instances = 1
270+
return num_instances
271+
272+
@staticmethod
273+
def _get_vm_ids_to_remove(removal_list, num_instances, inf_info, sys):
274+
if removal_list:
275+
# TODO: Think about to check the IDs of the VMs
276+
return [int(vm_id) for vm_id in removal_list[0:-num_instances]]
277+
# if no removal list is set, delete the oldest VMs of this type
278+
elif inf_info:
279+
vm_list = inf_info.get_vm_list_by_system_name()
280+
if sys.name in vm_list:
281+
vms = sorted(vm_list[sys.name], key=lambda vm: vm.creation_date)
282+
return [vm.im_id for vm in vms[0:-num_instances]]
283+
return None
284+
249285
@staticmethod
250286
def _check_private_networks(radl, inf_info):
251287
"""
@@ -336,7 +372,7 @@ def _order_deploys(radl):
336372
radl.deploys = fe + wn + priv
337373

338374
@staticmethod
339-
def _get_num_instances(sys_name, inf_info):
375+
def _get_current_num_instances(sys_name, inf_info):
340376
"""
341377
Get the current number of instances of system type name sys_name
342378
"""
@@ -2003,4 +2039,4 @@ def _get_oscar_service_json(self, node):
20032039
# this should never happen
20042040
Tosca.logger.warn("Property %s not expected. Ignoring." % prop.name)
20052041

2006-
return json.dumps(res)
2042+
return res
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# IM - Infrastructure Manager
2+
# Copyright (C) 2011 - GRyCAP - Universitat Politecnica de Valencia
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
# Task to delete an OSCAR function
18+
- name: Delete OSCAR service {{ oscar_service_name }} at {{ oscar_endpoint }}
19+
uri:
20+
url: "{{ oscar_endpoint }}/system/services/{{ oscar_service_name }}"
21+
user: "{{ oscar_username }}"
22+
password: "{{ oscar_password }}"
23+
method: DELETE
24+
force_basic_auth: yes
25+
status_code: [204, 409, 404]
26+
validate_certs: false
27+
timeout: 30
28+
register: endpoint_result
29+
changed_when: "endpoint_result.status == 204"
30+
until: endpoint_result.status == 204 or endpoint_result.status == 409 or endpoint_result.status == 404
31+
retries: 10
32+
delay: 5
33+
34+
- name: Delete OSCAR service {{ oscar_service_name }} at localhost
35+
uri:
36+
url: "http://localhost/system/services/{{ oscar_service_name }}"
37+
user: "{{ oscar_username }}"
38+
password: "{{ oscar_password }}"
39+
method: DELETE
40+
force_basic_auth: yes
41+
status_code: [204, 409, 404]
42+
validate_certs: false
43+
timeout: 60
44+
register: _result
45+
changed_when: "_result.status == 204"
46+
until: _result.status == 204 or _result.status == 409 or _result.status == 404
47+
retries: 10
48+
delay: 5
49+
when: endpoint_result is failed

test/unit/Tosca.py

+46
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ def test_tosca_oscar(self):
302302
conf = radl.get_configure_by_name('plants')
303303
self.assertEqual(conf.recipes, None)
304304
self.assertEqual(radl.deploys[0].id, "plants")
305+
self.assertEqual(radl.deploys[0].vm_number, 1)
305306

306307
def test_tosca_oscar_get_attribute(self):
307308
"""Test TOSCA OSCAR get_attributes function"""
@@ -344,6 +345,51 @@ def test_tosca_oscar_get_attribute(self):
344345
'token_type': 'password',
345346
'user': 'oscar'}})
346347

348+
def test_tosca_oscar_delete(self):
349+
"""Test TOSCA RADL deletion with OSCAR functions"""
350+
tosca_data = read_file_as_string('../files/tosca_oscar_host.yml')
351+
tosca_yaml = yaml.safe_load(tosca_data)
352+
tosca_yaml["topology_template"]["node_templates"]["plants"]["capabilities"] = \
353+
{"scalable": {"properties": {"count": 0}}}
354+
tosca_data = yaml.safe_dump(tosca_yaml)
355+
356+
tosca = Tosca(tosca_data)
357+
remove_list, radl = tosca.to_radl()
358+
self.assertEqual(remove_list, [])
359+
radl = parse_radl(str(radl))
360+
radl.check()
361+
node = radl.get_configure_by_name('oscar_plants')
362+
epected_res = """
363+
- tasks:
364+
- include_tasks: utils/tasks/del_oscar_function.yml
365+
vars:
366+
oscar_endpoint: "https://cluster.oscar.com"
367+
oscar_username: "oscar"
368+
oscar_password: "oscar_password"
369+
oscar_service_name: 'plants'
370+
"""
371+
self.assertIn(epected_res, node.recipes)
372+
373+
tosca_data = read_file_as_string('../files/tosca_oscar.yml')
374+
tosca_yaml = yaml.safe_load(tosca_data)
375+
tosca_yaml["topology_template"]["node_templates"]["plants"]["capabilities"] = \
376+
{"scalable": {"properties": {"count": 0}}}
377+
tosca_data = yaml.safe_dump(tosca_yaml)
378+
379+
vm1 = MagicMock()
380+
system1 = system("plants", [Feature("disk.0.image.url", "=", "grycap/image")])
381+
vm1.info.systems = [system1]
382+
vm1.creation_date = 1
383+
vm1.im_id = 1
384+
inf_info = MagicMock()
385+
inf_info.get_vm_list_by_system_name.return_value = {"plants": [vm1]}
386+
387+
tosca = Tosca(tosca_data)
388+
remove_list, radl = tosca.to_radl(inf_info)
389+
self.assertEqual(remove_list, [1])
390+
radl = parse_radl(str(radl))
391+
radl.check()
392+
347393
def test_tosca_remove(self):
348394
tosca_data = read_file_as_string('../files/tosca_remove_no_list.yml')
349395
tosca = Tosca(tosca_data)

0 commit comments

Comments
 (0)