Code Coverage

Cobertura Coverage Report > osmclient.cli_commands >

nslcm.py

Trend

File Coverage summary

NameClassesLinesConditionals
nslcm.py
100%
1/1
29%
56/194
100%
0/0

Coverage Breakdown by Class

NameLinesConditionals
nslcm.py
29%
56/194
N/A

Source

osmclient/cli_commands/nslcm.py
1 # Copyright ETSI Contributors and Others.
2 # All Rights Reserved.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    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, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15
16 1 import click
17 1 from osmclient.common.exceptions import ClientException
18 1 from osmclient.common.utils import validate_uuid4
19 1 from osmclient.cli_commands import utils
20 1 import yaml
21 1 import logging
22
23 1 logger = logging.getLogger("osmclient")
24
25
26 1 @click.command(
27     name="ns-action", short_help="executes an action/primitive over a NS instance"
28 )
29 1 @click.argument("ns_name")
30 1 @click.option(
31     "--vnf_name",
32     default=None,
33     help="member-vnf-index if the target is a vnf instead of a ns)",
34 )
35 1 @click.option("--kdu_name", default=None, help="kdu-name if the target is a kdu)")
36 1 @click.option("--vdu_id", default=None, help="vdu-id if the target is a vdu")
37 1 @click.option(
38     "--vdu_count", default=None, type=int, help="number of vdu instance of this vdu_id"
39 )
40 1 @click.option("--action_name", prompt=True, help="action name")
41 1 @click.option("--params", default=None, help="action params in YAML/JSON inline string")
42 1 @click.option("--params_file", default=None, help="YAML/JSON file with action params")
43 1 @click.option(
44     "--timeout", required=False, default=None, type=int, help="timeout in seconds"
45 )
46 1 @click.option(
47     "--wait",
48     required=False,
49     default=False,
50     is_flag=True,
51     help="do not return the control immediately, but keep it until the operation is completed, or timeout",
52 )
53 1 @click.pass_context
54 1 def ns_action(
55     ctx,
56     ns_name,
57     vnf_name,
58     kdu_name,
59     vdu_id,
60     vdu_count,
61     action_name,
62     params,
63     params_file,
64     timeout,
65     wait,
66 ):
67     """executes an action/primitive over a NS instance
68
69     NS_NAME: name or ID of the NS instance
70     """
71 0     logger.debug("")
72 0     utils.check_client_version(ctx.obj, ctx.command.name)
73 0     op_data = {}
74 0     if vnf_name:
75 0         op_data["member_vnf_index"] = vnf_name
76 0     if kdu_name:
77 0         op_data["kdu_name"] = kdu_name
78 0     if vdu_id:
79 0         op_data["vdu_id"] = vdu_id
80 0     if vdu_count is not None:
81 0         op_data["vdu_count_index"] = vdu_count
82 0     if timeout:
83 0         op_data["timeout_ns_action"] = timeout
84 0     op_data["primitive"] = action_name
85 0     if params_file:
86 0         with open(params_file, "r") as pf:
87 0             params = pf.read()
88 0     if params:
89 0         op_data["primitive_params"] = yaml.safe_load(params)
90     else:
91 0         op_data["primitive_params"] = {}
92 0     print(ctx.obj.ns.exec_op(ns_name, op_name="action", op_data=op_data, wait=wait))
93
94
95 1 @click.command(
96     name="vnf-scale", short_help="executes a VNF scale (adding/removing VDUs)"
97 )
98 1 @click.argument("ns_name")
99 1 @click.argument("vnf_name")
100 1 @click.option(
101     "--scaling-group", prompt=True, help="scaling-group-descriptor name to use"
102 )
103 1 @click.option(
104     "--scale-in", default=False, is_flag=True, help="performs a scale in operation"
105 )
106 1 @click.option(
107     "--scale-out",
108     default=False,
109     is_flag=True,
110     help="performs a scale out operation (by default)",
111 )
112 1 @click.option(
113     "--timeout", required=False, default=None, type=int, help="timeout in seconds"
114 )
115 1 @click.option(
116     "--wait",
117     required=False,
118     default=False,
119     is_flag=True,
120     help="do not return the control immediately, but keep it until the operation is completed, or timeout",
121 )
122 1 @click.pass_context
123 1 def vnf_scale(
124     ctx, ns_name, vnf_name, scaling_group, scale_in, scale_out, timeout, wait
125 ):
126     """
127     Executes a VNF scale (adding/removing VDUs)
128
129     \b
130     NS_NAME: name or ID of the NS instance.
131     VNF_NAME: member-vnf-index in the NS to be scaled.
132     """
133 0     logger.debug("")
134 0     utils.check_client_version(ctx.obj, ctx.command.name)
135 0     if not scale_in and not scale_out:
136 0         scale_out = True
137 0     ctx.obj.ns.scale_vnf(
138         ns_name, vnf_name, scaling_group, scale_in, scale_out, wait, timeout
139     )
140
141
142 1 @click.command(name="ns-update", short_help="executes an update of a Network Service.")
143 1 @click.argument("ns_name")
144 1 @click.option(
145     "--updatetype", required=True, type=str, help="available types: CHANGE_VNFPKG"
146 )
147 1 @click.option(
148     "--config",
149     required=True,
150     type=str,
151     help="extra information for update operation as YAML/JSON inline string as --config"
152     " '{changeVnfPackageData:[{vnfInstanceId: xxx, vnfdId: yyy}]}'",
153 )
154 1 @click.option(
155     "--timeout", required=False, default=None, type=int, help="timeout in seconds"
156 )
157 1 @click.option(
158     "--wait",
159     required=False,
160     default=False,
161     is_flag=True,
162     help="do not return the control immediately, but keep it until the operation is completed, or timeout",
163 )
164 1 @click.pass_context
165 1 def ns_update(ctx, ns_name, updatetype, config, timeout, wait):
166     """Executes an update of a Network Service.
167
168     The update will check new revisions of the Network Functions that are part of the
169     Network Service, and it will update them if needed.
170     Sample update command: osm ns-update  ns_instance_id --updatetype CHANGE_VNFPKG
171     --config '{changeVnfPackageData: [{vnfInstanceId: id_x,vnfdId: id_y}]}' --timeout 300 --wait
172
173     NS_NAME: Network service instance name or ID.
174
175     """
176 0     op_data = {
177         "timeout": timeout,
178         "updateType": updatetype,
179     }
180 0     if config:
181 0         op_data["config"] = yaml.safe_load(config)
182
183 0     utils.check_client_version(ctx.obj, ctx.command.name)
184 0     ctx.obj.ns.update(ns_name, op_data, wait=wait)
185
186
187 1 def iterator_split(iterator, separators):
188     """
189     Splits a tuple or list into several lists whenever a separator is found
190     For instance, the following tuple will be separated with the separator "--vnf" as follows.
191     From:
192         ("--vnf", "A", "--cause", "cause_A", "--vdu", "vdu_A1", "--vnf", "B", "--cause", "cause_B", ...
193         "--vdu", "vdu_B1", "--count_index", "1", "--run-day1", "--vdu", "vdu_B1", "--count_index", "2")
194     To:
195         [
196             ("--vnf", "A", "--cause", "cause_A", "--vdu", "vdu_A1"),
197             ("--vnf", "B", "--cause", "cause_B", "--vdu", "vdu_B1", "--count_index", "1", "--run-day1", ...
198              "--vdu", "vdu_B1", "--count_index", "2")
199         ]
200
201     Returns as many lists as separators are found
202     """
203 0     logger.debug("")
204 0     if iterator[0] not in separators:
205 0         raise ClientException(f"Expected one of {separators}. Received: {iterator[0]}.")
206 0     list_of_lists = []
207 0     first = 0
208 0     for i in range(len(iterator)):
209 0         if iterator[i] in separators:
210 0             if i == first:
211 0                 continue
212 0             if i - first < 2:
213 0                 raise ClientException(
214                     f"Expected at least one argument after separator (possible separators: {separators})."
215                 )
216 0             list_of_lists.append(list(iterator[first:i]))
217 0             first = i
218 0     if (len(iterator) - first) < 2:
219 0         raise ClientException(
220             f"Expected at least one argument after separator (possible separators: {separators})."
221         )
222     else:
223 0         list_of_lists.append(list(iterator[first : len(iterator)]))
224     # logger.debug(f"List of lists: {list_of_lists}")
225 0     return list_of_lists
226
227
228 1 def process_common_heal_params(heal_vnf_dict, args):
229 0     logger.debug("")
230 0     current_item = "vnf"
231 0     i = 0
232 0     while i < len(args):
233 0         if args[i] == "--cause":
234 0             if (i + 1 >= len(args)) or args[i + 1].startswith("--"):
235 0                 raise ClientException("No cause was provided after --cause")
236 0             heal_vnf_dict["cause"] = args[i + 1]
237 0             i = i + 2
238 0             continue
239 0         if args[i] == "--run-day1":
240 0             if current_item == "vnf":
241 0                 if "additionalParams" not in heal_vnf_dict:
242 0                     heal_vnf_dict["additionalParams"] = {}
243 0                 heal_vnf_dict["additionalParams"]["run-day1"] = True
244             else:
245                 # if current_item == "vdu"
246 0                 heal_vnf_dict["additionalParams"]["vdu"][-1]["run-day1"] = True
247 0             i = i + 1
248 0             continue
249 0         if args[i] == "--vdu":
250 0             if "additionalParams" not in heal_vnf_dict:
251 0                 heal_vnf_dict["additionalParams"] = {}
252 0                 heal_vnf_dict["additionalParams"]["vdu"] = []
253 0             if (i + 1 >= len(args)) or args[i + 1].startswith("--"):
254 0                 raise ClientException("No VDU ID was provided after --vdu")
255 0             heal_vnf_dict["additionalParams"]["vdu"].append({"vdu-id": args[i + 1]})
256 0             current_item = "vdu"
257 0             i = i + 2
258 0             continue
259 0         if args[i] == "--count-index":
260 0             if current_item == "vnf":
261 0                 raise ClientException(
262                     "Option --count-index only applies to VDU, not to VNF"
263                 )
264 0             if (i + 1 >= len(args)) or args[i + 1].startswith("--"):
265 0                 raise ClientException("No count index was provided after --count-index")
266 0             heal_vnf_dict["additionalParams"]["vdu"][-1]["count-index"] = int(
267                 args[i + 1]
268             )
269 0             i = i + 2
270 0             continue
271 0         i = i + 1
272 0     return
273
274
275 1 def process_ns_heal_params(ctx, param, value):
276     """
277     Processes the params in the command ns-heal
278     Click does not allow advanced patterns for positional options like this:
279     --vnf volumes_vnf --cause "Heal several_volumes-VM of several_volumes_vnf"
280                       --vdu several_volumes-VM
281     --vnf charm_vnf --cause "Heal two VMs of native_manual_scale_charm_vnf"
282                     --vdu mgmtVM --count-index 1 --run-day1
283                     --vdu mgmtVM --count-index 2
284
285     It returns the dictionary with all the params stored in ctx.params["heal_params"]
286     """
287 0     logger.debug("")
288     # logger.debug(f"Args: {value}")
289 0     if param.name != "args":
290 0         raise ClientException(f"Unexpected param: {param.name}")
291     # Split the tuple "value" by "--vnf"
292 0     vnfs = iterator_split(value, ["--vnf"])
293 0     logger.debug(f"VNFs: {vnfs}")
294 0     heal_dict = {}
295 0     heal_dict["healVnfData"] = []
296 0     for vnf in vnfs:
297         # logger.debug(f"VNF: {vnf}")
298 0         heal_vnf = {}
299 0         if vnf[1].startswith("--"):
300 0             raise ClientException("Expected a VNF_ID after --vnf")
301 0         heal_vnf["vnfInstanceId"] = vnf[1]
302 0         process_common_heal_params(heal_vnf, vnf[2:])
303 0         heal_dict["healVnfData"].append(heal_vnf)
304 0     ctx.params["heal_params"] = heal_dict
305 0     return
306
307
308 1 @click.command(
309     name="ns-heal",
310     short_help="heals (recreates) VNFs or VDUs of a NS instance",
311     context_settings=dict(
312         ignore_unknown_options=True,
313     ),
314 )
315 1 @click.argument("ns_name")
316 1 @click.argument(
317     "args",
318     nargs=-1,
319     type=click.UNPROCESSED,
320     callback=process_ns_heal_params,
321 )
322 1 @click.option("--timeout", type=int, default=None, help="timeout in seconds")
323 1 @click.option(
324     "--wait",
325     default=False,
326     is_flag=True,
327     help="do not return the control immediately, but keep it until the operation is completed, or timeout",
328 )
329 1 @click.pass_context
330 1 def ns_heal(ctx, ns_name, args, heal_params, timeout, wait):
331     """heals (recreates) VNFs or VDUs of a NS instance
332
333     NS_NAME: name or ID of the NS instance
334
335     \b
336     Options:
337       --vnf TEXT             VNF instance ID or VNF id in the NS [required]
338       --cause TEXT           human readable cause of the healing
339       --run-day1             indicates whether or not to run day1 primitives for the VNF/VDU
340       --vdu TEXT             vdu-id
341       --count-index INTEGER  count-index
342
343     \b
344     Example:
345     osm ns-heal NS_NAME|NS_ID --vnf volumes_vnf --cause "Heal several_volumes-VM of several_volumes_vnf"
346                                                 --vdu several_volumes-VM
347                               --vnf charm_vnf --cause "Heal two VMs of native_manual_scale_charm_vnf"
348                                               --vdu mgmtVM --count-index 1 --run-day1
349                                               --vdu mgmtVM --count-index 2
350     """
351 0     logger.debug("")
352 0     heal_dict = ctx.params["heal_params"]
353 0     logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
354     # replace VNF id in the NS by the VNF instance ID
355 0     for vnf in heal_dict["healVnfData"]:
356 0         vnf_id = vnf["vnfInstanceId"]
357 0         if not validate_uuid4(vnf_id):
358 0             vnf_filter = f"member-vnf-index-ref={vnf_id}"
359 0             vnf_list = ctx.obj.vnf.list(ns=ns_name, filter=vnf_filter)
360 0             if len(vnf_list) == 0:
361 0                 raise ClientException(
362                     f"No VNF found in NS {ns_name} with filter {vnf_filter}"
363                 )
364 0             elif len(vnf_list) == 1:
365 0                 vnf["vnfInstanceId"] = vnf_list[0]["_id"]
366             else:
367 0                 raise ClientException(
368                     f"More than 1 VNF found in NS {ns_name} with filter {vnf_filter}"
369                 )
370 0     logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
371 0     utils.check_client_version(ctx.obj, ctx.command.name)
372 0     ctx.obj.ns.heal(ns_name, heal_dict, wait, timeout)
373
374
375 1 def process_vnf_heal_params(ctx, param, value):
376     """
377     Processes the params in the command vnf-heal
378     Click does not allow advanced patterns for positional options like this:
379     --vdu mgmtVM --count-index 1 --run-day1 --vdu mgmtVM --count-index 2
380
381     It returns the dictionary with all the params stored in ctx.params["heal_params"]
382     """
383 0     logger.debug("")
384     # logger.debug(f"Args: {value}")
385 0     if param.name != "args":
386 0         raise ClientException(f"Unexpected param: {param.name}")
387     # Split the tuple "value" by "--vnf"
388 0     vnf = value
389 0     heal_dict = {}
390 0     heal_dict["healVnfData"] = []
391 0     logger.debug(f"VNF: {vnf}")
392 0     heal_vnf = {"vnfInstanceId": "id_to_be_substituted"}
393 0     process_common_heal_params(heal_vnf, vnf)
394 0     heal_dict["healVnfData"].append(heal_vnf)
395 0     ctx.params["heal_params"] = heal_dict
396 0     return
397
398
399 1 @click.command(
400     name="vnf-heal",
401     short_help="heals (recreates) a VNF instance or the VDUs of a VNF instance",
402     context_settings=dict(
403         ignore_unknown_options=True,
404     ),
405 )
406 1 @click.argument("vnf_name")
407 1 @click.argument(
408     "args",
409     nargs=-1,
410     type=click.UNPROCESSED,
411     callback=process_vnf_heal_params,
412 )
413 1 @click.option("--timeout", type=int, default=None, help="timeout in seconds")
414 1 @click.option(
415     "--wait",
416     default=False,
417     is_flag=True,
418     help="do not return the control immediately, but keep it until the operation is completed, or timeout",
419 )
420 1 @click.pass_context
421 1 def vnf_heal(
422     ctx,
423     vnf_name,
424     args,
425     heal_params,
426     timeout,
427     wait,
428 ):
429     """heals (recreates) a VNF instance or the VDUs of a VNF instance
430
431     VNF_NAME: name or ID of the VNF instance
432
433     \b
434     Options:
435       --cause TEXT           human readable cause of the healing of the VNF
436       --run-day1             indicates whether or not to run day1 primitives for the VNF/VDU
437       --vdu TEXT             vdu-id
438       --count-index INTEGER  count-index
439
440     \b
441     Example:
442     osm vnf-heal VNF_INSTANCE_ID --vdu mgmtVM --count-index 1 --run-day1
443                                  --vdu mgmtVM --count-index 2
444     """
445 0     logger.debug("")
446 0     heal_dict = ctx.params["heal_params"]
447 0     heal_dict["healVnfData"][-1]["vnfInstanceId"] = vnf_name
448 0     logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
449 0     utils.check_client_version(ctx.obj, ctx.command.name)
450 0     vnfr = ctx.obj.vnf.get(vnf_name)
451 0     ns_id = vnfr["nsr-id-ref"]
452 0     ctx.obj.ns.heal(ns_id, heal_dict, wait, timeout)