blob: d206db281814b4f3266d8329ed402d448c219abd [file] [log] [blame]
# Copyright ETSI Contributors and Others.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import click
from osmclient.common.exceptions import ClientException
from osmclient.common.utils import validate_uuid4
from osmclient.cli_commands import utils, common
import yaml
import logging
logger = logging.getLogger("osmclient")
@click.command(
name="ns-action", short_help="executes an action/primitive over a NS instance"
)
@click.argument("ns_name")
@click.option(
"--vnf_name",
default=None,
help="member-vnf-index if the target is a vnf instead of a ns)",
)
@click.option("--kdu_name", default=None, help="kdu-name if the target is a kdu)")
@click.option("--vdu_id", default=None, help="vdu-id if the target is a vdu")
@click.option(
"--vdu_count", default=None, type=int, help="number of vdu instance of this vdu_id"
)
@click.option("--action_name", prompt=True, help="action name")
@click.option("--params", default=None, help="action params in YAML/JSON inline string")
@click.option("--params_file", default=None, help="YAML/JSON file with action params")
@click.option(
"--timeout", required=False, default=None, type=int, help="timeout in seconds"
)
@click.option(
"--wait",
required=False,
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_action(
ctx,
ns_name,
vnf_name,
kdu_name,
vdu_id,
vdu_count,
action_name,
params,
params_file,
timeout,
wait,
):
"""executes an action/primitive over a NS instance
NS_NAME: name or ID of the NS instance
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
op_data = {}
if vnf_name:
op_data["member_vnf_index"] = vnf_name
if kdu_name:
op_data["kdu_name"] = kdu_name
if vdu_id:
op_data["vdu_id"] = vdu_id
if vdu_count is not None:
op_data["vdu_count_index"] = vdu_count
if timeout:
op_data["timeout_ns_action"] = timeout
op_data["primitive"] = action_name
if params_file:
with open(params_file, "r") as pf:
params = pf.read()
if params:
op_data["primitive_params"] = yaml.safe_load(params)
else:
op_data["primitive_params"] = {}
print(ctx.obj.ns.exec_op(ns_name, op_name="action", op_data=op_data, wait=wait))
@click.command(
name="vnf-scale", short_help="executes a VNF scale (adding/removing VDUs)"
)
@click.argument("ns_name")
@click.argument("vnf_name")
@click.option(
"--scaling-group", prompt=True, help="scaling-group-descriptor name to use"
)
@click.option(
"--scale-in", default=False, is_flag=True, help="performs a scale in operation"
)
@click.option(
"--scale-out",
default=False,
is_flag=True,
help="performs a scale out operation (by default)",
)
@click.option(
"--timeout", required=False, default=None, type=int, help="timeout in seconds"
)
@click.option(
"--wait",
required=False,
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 vnf_scale(
ctx, ns_name, vnf_name, scaling_group, scale_in, scale_out, timeout, wait
):
"""
Executes a VNF scale (adding/removing VDUs)
\b
NS_NAME: name or ID of the NS instance.
VNF_NAME: member-vnf-index in the NS to be scaled.
"""
logger.debug("")
utils.check_client_version(ctx.obj, ctx.command.name)
if not scale_in and not scale_out:
scale_out = True
ctx.obj.ns.scale_vnf(
ns_name, vnf_name, scaling_group, scale_in, scale_out, wait, timeout
)
@click.command(name="ns-update", short_help="executes an update of a Network Service.")
@click.argument("ns_name")
@click.option(
"--updatetype", required=True, type=str, help="available types: CHANGE_VNFPKG"
)
@click.option(
"--config",
required=True,
type=str,
help="extra information for update operation as YAML/JSON inline string as --config"
" '{changeVnfPackageData:[{vnfInstanceId: xxx, vnfdId: yyy}]}'",
)
@click.option(
"--timeout", required=False, default=None, type=int, help="timeout in seconds"
)
@click.option(
"--wait",
required=False,
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_update(ctx, ns_name, updatetype, config, timeout, wait):
"""Executes an update of a Network Service.
The update will check new revisions of the Network Functions that are part of the
Network Service, and it will update them if needed.
Sample update command: osm ns-update ns_instance_id --updatetype CHANGE_VNFPKG
--config '{changeVnfPackageData: [{vnfInstanceId: id_x,vnfdId: id_y}]}' --timeout 300 --wait
NS_NAME: Network service instance name or ID.
"""
op_data = {
"timeout": timeout,
"updateType": updatetype,
}
if config:
op_data["config"] = yaml.safe_load(config)
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.ns.update(ns_name, op_data, wait=wait)
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("")
# logger.debug(f"Args: {value}")
if param.name != "args":
raise ClientException(f"Unexpected param: {param.name}")
# Split the tuple "value" by "--vnf"
vnfs = common.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
@click.command(
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.argument(
"args",
nargs=-1,
type=click.UNPROCESSED,
callback=process_ns_heal_params,
)
@click.option("--timeout", type=int, default=None, help="timeout in seconds")
@click.option(
"--wait",
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, 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"]
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)}")
utils.check_client_version(ctx.obj, ctx.command.name)
ctx.obj.ns.heal(ns_name, heal_dict, wait, timeout)
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
@click.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.argument(
"args",
nargs=-1,
type=click.UNPROCESSED,
callback=process_vnf_heal_params,
)
@click.option("--timeout", type=int, default=None, help="timeout in seconds")
@click.option(
"--wait",
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 vnf_heal(
ctx,
vnf_name,
args,
heal_params,
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["healVnfData"][-1]["vnfInstanceId"] = vnf_name
logger.debug(f"Heal dict:\n{yaml.safe_dump(heal_dict)}")
utils.check_client_version(ctx.obj, ctx.command.name)
vnfr = ctx.obj.vnf.get(vnf_name)
ns_id = vnfr["nsr-id-ref"]
ctx.obj.ns.heal(ns_id, heal_dict, wait, timeout)