import click
from osmclient import client
from osmclient.common.exceptions import ClientException, NotFound
+from osmclient.common.utils import validate_uuid4
from prettytable import PrettyTable
import yaml
import json
ctx.obj.ns.update(ns_name, op_data, wait=wait)
-def process_common_heal_params(ctx, param, value):
+def iterator_split(iterator, separators):
+ """
+ Splits a tuple or list into several lists whenever a separator is found
+ For instance, the following tuple will be separated with the separator "--vnf" as follows.
+ From:
+ ("--vnf", "A", "--cause", "cause_A", "--vdu", "vdu_A1", "--vnf", "B", "--cause", "cause_B", ...
+ "--vdu", "vdu_B1", "--count_index", "1", "--run-day1", "--vdu", "vdu_B1", "--count_index", "2")
+ To:
+ [
+ ("--vnf", "A", "--cause", "cause_A", "--vdu", "vdu_A1"),
+ ("--vnf", "B", "--cause", "cause_B", "--vdu", "vdu_B1", "--count_index", "1", "--run-day1", ...
+ "--vdu", "vdu_B1", "--count_index", "2")
+ ]
+
+ Returns as many lists as separators are found
+ """
logger.debug("")
- if not value:
- return
- logger.debug(f"Param name: {param.name}")
- if not ctx.params.get("heal_params", {}).get("healVnfData"):
- raise ClientException(f"Expected option --vnf before {param.name}")
- if param.name == "cause":
- param_dict = ctx.params["heal_params"]["healVnfData"][-1]["cause"] = value
- return
- # If not "vnf" and not "cause", then the param lies on "additionalParams"
- if not ctx.params["heal_params"]["healVnfData"][-1].get("additionalParams"):
- ctx.params["heal_params"]["healVnfData"][-1]["additionalParams"] = {}
- if param.name == "vdu":
- # Check VDU id ?
- if not ctx.params["heal_params"]["healVnfData"][-1]["additionalParams"].get(
- "vdu"
- ):
- ctx.params["heal_params"]["healVnfData"][-1]["additionalParams"][
- "vdu"
- ] = []
- vdu = {"vdu-id": value}
- ctx.params["heal_params"]["healVnfData"][-1]["additionalParams"][
- "vdu"
- ].append(vdu)
- ctx.params["heal_params"]["current_item"] = "vdu"
+ if iterator[0] not in separators:
+ raise ClientException(f"Expected one of {separators}. Received: {iterator[0]}.")
+ list_of_lists = []
+ first = 0
+ for i in range(len(iterator)):
+ if iterator[i] in separators:
+ if i == first:
+ continue
+ if (i - first < 2):
+ raise ClientException(f"Expected at least one argument after separator (possible separators: {separators}).")
+ list_of_lists.append(list(iterator[first:i]))
+ first = i
+ if ((len(iterator) - first) < 2):
+ raise ClientException(f"Expected at least one argument after separator (possible separators: {separators}).")
else:
- current_item = ctx.params["heal_params"]["current_item"]
- if current_item == "vnf":
- param_dict = ctx.params["heal_params"]["healVnfData"][-1][
- "additionalParams"
- ]
- else:
- # if current_item == "vdu":
- param_dict = ctx.params["heal_params"]["healVnfData"][-1][
- "additionalParams"
- ]["vdu"][-1]
- if param.name == "count_index":
- param_name = "count-index"
- elif param.name == "run_day1":
- param_name = "run-day1"
- else:
- param_name = param.name
- param_dict[param_name] = value
+ list_of_lists.append(list(iterator[first:len(iterator)]))
+ # logger.debug(f"List of lists: {list_of_lists}")
+ return list_of_lists
+
+
+def process_common_heal_params(heal_vnf_dict, args):
+ logger.debug("")
+ current_item = "vnf"
+ i = 0
+ while i < len(args):
+ if args[i] == "--cause":
+ if (i+1 >= len(args)) or args[i+1].startswith("--"):
+ raise ClientException("No cause was provided after --cause")
+ heal_vnf_dict["cause"] = args[i+1]
+ i = i + 2
+ continue
+ if args[i] == "--run-day1":
+ if current_item == "vnf":
+ if "additionalParams" not in heal_vnf_dict:
+ heal_vnf_dict["additionalParams"] = {}
+ heal_vnf_dict["additionalParams"]["run-day1"] = True
+ else:
+ # if current_item == "vdu"
+ heal_vnf_dict["additionalParams"]["vdu"][-1]["run-day1"] = True
+ i = i + 1
+ continue
+ if args[i] == "--vdu":
+ if "additionalParams" not in heal_vnf_dict:
+ heal_vnf_dict["additionalParams"] = {}
+ heal_vnf_dict["additionalParams"]["vdu"] = []
+ if (i+1 >= len(args)) or args[i+1].startswith("--"):
+ raise ClientException("No VDU ID was provided after --vdu")
+ heal_vnf_dict["additionalParams"]["vdu"].append({"vdu-id": args[i+1]})
+ current_item = "vdu"
+ i = i + 2
+ continue
+ if args[i] == "--count-index":
+ if current_item == "vnf":
+ raise ClientException("Option --count-index only applies to VDU, not to VNF")
+ if (i+1 >= len(args)) or args[i+1].startswith("--"):
+ raise ClientException("No count index was provided after --count-index")
+ heal_vnf_dict["additionalParams"]["vdu"][-1]["count-index"] = int(args[i+1])
+ i = i + 2
+ continue
+ i = i + 1
return
def process_ns_heal_params(ctx, param, value):
+ """
+ Processes the params in the command ns-heal
+ Click does not allow advanced patterns for positional options like this:
+ --vnf volumes_vnf --cause "Heal several_volumes-VM of several_volumes_vnf"
+ --vdu several_volumes-VM
+ --vnf charm_vnf --cause "Heal two VMs of native_manual_scale_charm_vnf"
+ --vdu mgmtVM --count-index 1 --run-day1
+ --vdu mgmtVM --count-index 2
+
+ It returns the dictionary with all the params stored in ctx.params["heal_params"]
+ """
logger.debug("")
- if not ctx.params.get("heal_params"):
- ctx.params["heal_params"] = {}
- ctx.params["heal_params"]["healVnfData"] = []
- if param.name == "vnf" and value:
- # Check VNF id ?
- logger.debug(f"Param name: {param.name}")
- vnf = {"vnfInstanceId": value}
- ctx.params["heal_params"]["healVnfData"].append(vnf)
- ctx.params["heal_params"]["current_item"] = "vnf"
- else:
- process_common_heal_params(ctx, param, value)
-
-
-def process_vnf_heal_params(ctx, param, value):
- logger.debug("")
- if not ctx.params.get("heal_params"):
- ctx.params["heal_params"] = {}
- ctx.params["heal_params"]["healVnfData"] = []
- vnf = {"vnfInstanceId": "id_to_be_substituted"}
- ctx.params["heal_params"]["healVnfData"].append(vnf)
- ctx.params["heal_params"]["current_item"] = "vnf"
- else:
- process_common_heal_params(ctx, param, value)
+ # logger.debug(f"Args: {value}")
+ if param.name != "args":
+ raise ClientException(f"Unexpected param: {param.name}")
+ # Split the tuple "value" by "--vnf"
+ vnfs = iterator_split(value, ["--vnf"])
+ logger.debug(f"VNFs: {vnfs}")
+ heal_dict = {}
+ heal_dict["healVnfData"] = []
+ for vnf in vnfs:
+ # logger.debug(f"VNF: {vnf}")
+ heal_vnf = {}
+ if vnf[1].startswith("--"):
+ raise ClientException("Expected a VNF_ID after --vnf")
+ heal_vnf["vnfInstanceId"] = vnf[1]
+ process_common_heal_params(heal_vnf, vnf[2:])
+ heal_dict["healVnfData"].append(heal_vnf)
+ ctx.params["heal_params"] = heal_dict
+ return
@cli_osm.command(
- name="ns-heal", short_help="heals (recreates) VNFs or VDUs of a NS instance"
+ name="ns-heal",
+ short_help="heals (recreates) VNFs or VDUs of a NS instance",
+ context_settings=dict(ignore_unknown_options=True,)
)
@click.argument("ns_name")
-@click.option(
- "--vnf",
- required=True,
- default=None,
- callback=process_ns_heal_params,
- help="vnf-id if the target is a vnf instead of a ns)",
-)
-@click.option(
- "--cause",
- default=None,
- callback=process_ns_heal_params,
- help="human readable cause of the healing",
-)
-@click.option(
- "--run-day1",
- is_flag=True,
- default=False,
+@click.argument(
+ 'args',
+ nargs=-1,
+ type=click.UNPROCESSED,
callback=process_ns_heal_params,
- help="indicates whether or not to run day1 primitives for the VNF/VDU",
-)
-@click.option(
- "--vdu",
- default=None,
- callback=process_ns_heal_params,
- help="vdu-id",
-)
-@click.option(
- "--count-index",
- type=int,
- default=None,
- callback=process_ns_heal_params,
- help="count-index",
)
@click.option(
"--timeout",
)
@click.option(
"--wait",
- is_flag=True,
default=False,
+ is_flag=True,
help="do not return the control immediately, but keep it until the operation is completed, or timeout",
)
@click.pass_context
def ns_heal(
ctx,
ns_name,
+ args,
heal_params,
- cause,
- vnf,
- run_day1,
- vdu,
- count_index,
- wait,
timeout,
+ wait
):
"""heals (recreates) VNFs or VDUs of a NS instance
NS_NAME: name or ID of the NS instance
+
+ \b
+ Options:
+ --vnf TEXT VNF instance ID or VNF id in the NS [required]
+ --cause TEXT human readable cause of the healing
+ --run-day1 indicates whether or not to run day1 primitives for the VNF/VDU
+ --vdu TEXT vdu-id
+ --count-index INTEGER count-index
+
+ \b
+ Example:
+ osm ns-heal NS_NAME|NS_ID --vnf volumes_vnf --cause "Heal several_volumes-VM of several_volumes_vnf"
+ --vdu several_volumes-VM
+ --vnf charm_vnf --cause "Heal two VMs of native_manual_scale_charm_vnf"
+ --vdu mgmtVM --count-index 1 --run-day1
+ --vdu mgmtVM --count-index 2
"""
logger.debug("")
heal_dict = ctx.params["heal_params"]
- heal_dict.pop("current_item")
- if cause:
- heal_dict["cause"] = cause
- logger.debug(f"Heal dict: {heal_dict}")
+ logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
+ # replace VNF id in the NS by the VNF instance ID
+ for vnf in heal_dict["healVnfData"]:
+ vnf_id = vnf["vnfInstanceId"]
+ if not validate_uuid4(vnf_id):
+ vnf_filter = f"member-vnf-index-ref={vnf_id}"
+ vnf_list = ctx.obj.vnf.list(ns=ns_name, filter=vnf_filter)
+ if len(vnf_list) == 0:
+ raise ClientException(f"No VNF found in NS {ns_name} with filter {vnf_filter}")
+ elif len(vnf_list) == 1:
+ vnf["vnfInstanceId"] = vnf_list[0]["_id"]
+ else:
+ raise ClientException(f"More than 1 VNF found in NS {ns_name} with filter {vnf_filter}")
+ logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
check_client_version(ctx.obj, ctx.command.name)
ctx.obj.ns.heal(ns_name, heal_dict, wait, timeout)
+ exit(0)
+
+
+def process_vnf_heal_params(ctx, param, value):
+ """
+ Processes the params in the command vnf-heal
+ Click does not allow advanced patterns for positional options like this:
+ --vdu mgmtVM --count-index 1 --run-day1 --vdu mgmtVM --count-index 2
+
+ It returns the dictionary with all the params stored in ctx.params["heal_params"]
+ """
+ logger.debug("")
+ # logger.debug(f"Args: {value}")
+ if param.name != "args":
+ raise ClientException(f"Unexpected param: {param.name}")
+ # Split the tuple "value" by "--vnf"
+ vnf = value
+ heal_dict = {}
+ heal_dict["healVnfData"] = []
+ logger.debug(f"VNF: {vnf}")
+ heal_vnf = {"vnfInstanceId": "id_to_be_substituted"}
+ process_common_heal_params(heal_vnf, vnf)
+ heal_dict["healVnfData"].append(heal_vnf)
+ ctx.params["heal_params"] = heal_dict
+ return
@cli_osm.command(
name="vnf-heal",
short_help="heals (recreates) a VNF instance or the VDUs of a VNF instance",
+ context_settings=dict(ignore_unknown_options=True,)
)
@click.argument("vnf_name")
-@click.option(
- "--cause",
- default=None,
- callback=process_vnf_heal_params,
- help="human readable cause of the healing",
-)
-@click.option(
- "--run-day1",
- is_flag=True,
- default=False,
+@click.argument(
+ 'args',
+ nargs=-1,
+ type=click.UNPROCESSED,
callback=process_vnf_heal_params,
- help="indicates whether or not to run day1 primitives for the VNF/VDU",
-)
-@click.option(
- "--vdu",
- default=None,
- callback=process_vnf_heal_params,
- help="vdu-id",
-)
-@click.option(
- "--count-index",
- type=int,
- default=None,
- callback=process_vnf_heal_params,
- help="count-index",
)
@click.option(
"--timeout",
help="do not return the control immediately, but keep it until the operation is completed, or timeout",
)
@click.pass_context
-def vnf_heal(
+def vnf_heal2(
ctx,
vnf_name,
+ args,
heal_params,
- cause,
- run_day1,
- vdu,
- count_index,
- wait,
timeout,
+ wait,
):
"""heals (recreates) a VNF instance or the VDUs of a VNF instance
VNF_NAME: name or ID of the VNF instance
+
+ \b
+ Options:
+ --cause TEXT human readable cause of the healing of the VNF
+ --run-day1 indicates whether or not to run day1 primitives for the VNF/VDU
+ --vdu TEXT vdu-id
+ --count-index INTEGER count-index
+
+ \b
+ Example:
+ osm vnf-heal VNF_INSTANCE_ID --vdu mgmtVM --count-index 1 --run-day1
+ --vdu mgmtVM --count-index 2
"""
logger.debug("")
heal_dict = ctx.params["heal_params"]
- heal_dict.pop("current_item")
heal_dict["healVnfData"][-1]["vnfInstanceId"] = vnf_name
- logger.debug(f"Heal dict: {heal_dict}")
+ logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
check_client_version(ctx.obj, ctx.command.name)
- ctx.obj.vnf.heal(vnf_name, heal_dict, wait, timeout)
+ vnfr = ctx.obj.vnf.get(vnf_name)
+ ns_id = vnfr["nsr-id-ref"]
+ ctx.obj.ns.heal(ns_id, heal_dict, wait, timeout)
+ exit(0)
@cli_osm.command(name="alarm-show", short_help="show alarm details")