--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.cli_commands import common
+import logging
+
+logger = logging.getLogger("osmclient")
+
+
+@click.command(name="app-profile-create", short_help="creates an App Profile")
+@click.argument("name")
+@click.option("--description", default="", help="human readable description")
+@click.pass_context
+def app_profile_create(ctx, name, description, **kwargs):
+ """creates an App Profile
+
+ NAME: name of the App Profile
+ """
+ logger.debug("")
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
+ app_profile = kwargs
+ app_profile["name"] = name
+ if description:
+ app_profile["description"] = description
+ ctx.obj.app_profile.create(name, content_dict=app_profile)
+
+
+@click.command(name="app-profile-delete", short_help="deletes an App Profile")
+@click.argument("name")
+@click.option(
+ "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
+)
+@click.pass_context
+def app_profile_delete(ctx, name, force):
+ """deletes an App Profile
+
+ NAME: name or ID of the App Profile to be deleted
+ """
+ logger.debug("")
+ ctx.obj.app_profile.delete(name, force=force)
+
+
+@click.command(name="app-profile-list")
+@click.option(
+ "--filter",
+ help="restricts the list to the items matching the filter",
+)
+@click.pass_context
+def app_profile_list(ctx, filter):
+ """list all App Profiles"""
+ logger.debug("")
+ common.generic_list(callback=ctx.obj.app_profile.list, filter=filter)
+
+
+@click.command(
+ name="app-profile-show",
+ short_help="shows the details of an App Profile",
+)
+@click.argument("name")
+@click.pass_context
+def app_profile_show(ctx, name):
+ """shows the details of an App Profile
+
+ NAME: name or ID of the App Profile
+ """
+ logger.debug("")
+ common.generic_show(callback=ctx.obj.app_profile.get, name=name)
+
+
+@click.command(name="app-profile-update", short_help="updates an App Profile")
+@click.argument("name")
+@click.option("--newname", help="New name for the App Profile")
+@click.option("--description", help="human readable description")
+@click.pass_context
+def app_profile_update(ctx, name, newname, description, **kwargs):
+ """updates an App Profile
+
+ NAME: name or ID of the App Profile
+ """
+ logger.debug("")
+ profile_changes = common.generic_update(newname, description, kwargs)
+ ctx.obj.app_profile.update(name, changes_dict=profile_changes)
--- /dev/null
+# 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.cli_commands import common
+import logging
+
+logger = logging.getLogger("osmclient")
+
+
+@click.command(name="cluster-create", short_help="creates a K8s cluster")
+@click.argument("name")
+@click.option("--node-count", "-n", prompt=True, type=int, help="number of nodes")
+@click.option("--node-size", prompt=True, help="size of the worker nodes")
+@click.option("--version", prompt=True, help="Kubernetes version")
+@click.option(
+ "--vim-account",
+ prompt=True,
+ help="VIM target or cloud account where the cluster will be created",
+)
+@click.option("--description", default="", help="human readable description")
+@click.option(
+ "--region-name",
+ default=None,
+ help="region name in VIM account where the cluster will be created",
+)
+@click.option(
+ "--resource-group",
+ default=None,
+ help="resource group in VIM account where the cluster will be created",
+)
+@click.pass_context
+def cluster_create(
+ ctx,
+ name,
+ node_count,
+ node_size,
+ version,
+ vim_account,
+ description,
+ region_name,
+ resource_group,
+ **kwargs
+):
+ """creates a K8s cluster
+
+ NAME: name of the K8s cluster
+ """
+ logger.debug("")
+ # kwargs = {k: v for k, v in kwargs.items() if v is not None}
+ cluster = {}
+ cluster["name"] = name
+ cluster["node_count"] = node_count
+ cluster["node_size"] = node_size
+ cluster["k8s_version"] = version
+ cluster["vim_account"] = vim_account
+ if description:
+ cluster["description"] = description
+ if region_name:
+ cluster["region_name"] = region_name
+ if resource_group:
+ cluster["resource_group"] = resource_group
+ ctx.obj.cluster.create(name, content_dict=cluster)
+
+
+@click.command(name="cluster-delete", short_help="deletes a K8s cluster")
+@click.argument("name")
+@click.option(
+ "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
+)
+@click.pass_context
+def cluster_delete(ctx, name, force):
+ """deletes a K8s cluster
+
+ NAME: name or ID of the K8s cluster to be deleted
+ """
+ logger.debug("")
+ ctx.obj.cluster.delete(name, force=force)
+
+
+@click.command(name="cluster-list")
+@click.option(
+ "--filter",
+ help="restricts the list to the items matching the filter",
+)
+@click.pass_context
+def cluster_list(ctx, filter):
+ """list all K8s clusters"""
+ logger.debug("")
+ extras = {"Created": "created"}
+ common.generic_list(callback=ctx.obj.cluster.list, filter=filter, extras=extras)
+
+
+@click.command(
+ name="cluster-show",
+ short_help="shows the details of a K8s cluster",
+)
+@click.argument("name")
+@click.pass_context
+def cluster_show(ctx, name):
+ """shows the details of a K8s cluster
+
+ NAME: name or ID of the K8s cluster
+ """
+ logger.debug("")
+ common.generic_show(callback=ctx.obj.cluster.get, name=name)
+
+
+@click.command(name="cluster-update", short_help="updates a K8s cluster")
+@click.argument("name")
+@click.option("--newname", help="New name for the K8s cluster")
+@click.option("--description", help="human readable description")
+@click.pass_context
+def cluster_update(ctx, name, newname, description, **kwargs):
+ """updates a K8s cluster
+
+ NAME: name or ID of the K8s cluster
+ """
+ logger.debug("")
+ cluster_changes = common.generic_update(newname, description, kwargs)
+ ctx.obj.cluster.update(name, changes_dict=cluster_changes)
+
+
+@click.command(
+ name="cluster-get-credentials", short_help="get kubeconfig of a K8s cluster"
+)
+@click.argument("name")
+@click.pass_context
+def cluster_get_credentials(ctx, name, **kwargs):
+ """updates a K8s cluster
+
+ NAME: name or ID of the K8s cluster
+ """
+ logger.debug("")
+ ctx.obj.cluster.get_credentials(name)
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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 logging
+import json
+from osmclient.common import print_output
+from osmclient.common.exceptions import ClientException
+
+
+logger = logging.getLogger("osmclient")
+
+
+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 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:
+ list_of_lists.append(list(iterator[first : len(iterator)]))
+ # logger.debug(f"List of lists: {list_of_lists}")
+ return list_of_lists
+
+
+def generic_update(newname, description, extras):
+ changes_dict = {k: v for k, v in extras.items() if v is not None}
+ if newname:
+ changes_dict["name"] = newname
+ if description:
+ changes_dict["description"] = description
+ return changes_dict
+
+
+def generic_show(callback, name, format="table"):
+ logger.debug("")
+ resp = callback(name)
+ headers = ["field", "value"]
+ rows = []
+ if format == "table" or format == "csv":
+ if resp:
+ for k, v in list(resp.items()):
+ rows.append([k, json.dumps(v, indent=2)])
+ print_output.print_output(format, headers, rows, resp)
+
+
+def generic_list(callback, filter, format="table", extras={}):
+ logger.debug("")
+ if filter:
+ filter = "&".join(filter)
+ resp = callback(filter)
+ headers = ["Name", "Id", "State", "Operating State", "Resource State"]
+ headers.extend(extras.keys())
+ rows = []
+ if format == "table" or format == "csv":
+ for item in resp:
+ row_item = [
+ item["name"],
+ item["_id"],
+ item["state"],
+ item["operatingState"],
+ item["resourceState"],
+ ]
+ for v in extras.values():
+ row_item.append(item.get(v, "-"))
+ rows.append(row_item)
+ print_output.print_output(format, headers, rows, resp)
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.cli_commands import common
+import logging
+
+logger = logging.getLogger("osmclient")
+
+
+@click.command(
+ name="infra-config-profile-create", short_help="creates an Infra Config Profile"
+)
+@click.argument("name")
+@click.option("--description", default="", help="human readable description")
+@click.pass_context
+def infra_config_profile_create(ctx, name, description, **kwargs):
+ """creates an Infra Config Profile
+
+ NAME: name of the Infra Config Profile
+ """
+ logger.debug("")
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
+ infra_config_profile = kwargs
+ infra_config_profile["name"] = name
+ if description:
+ infra_config_profile["description"] = description
+ ctx.obj.infra_config_profile.create(name, content_dict=infra_config_profile)
+
+
+@click.command(
+ name="infra-config-profile-delete", short_help="deletes an Infra Config Profile"
+)
+@click.argument("name")
+@click.option(
+ "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
+)
+@click.pass_context
+def infra_config_profile_delete(ctx, name, force):
+ """deletes an Infra Config Profile
+
+ NAME: name or ID of the Infra Config Profile to be deleted
+ """
+ logger.debug("")
+ ctx.obj.infra_config_profile.delete(name, force=force)
+
+
+@click.command(name="infra-config-profile-list")
+@click.option(
+ "--filter",
+ help="restricts the list to the items matching the filter",
+)
+@click.pass_context
+def infra_config_profile_list(ctx, filter):
+ """list all Infra Config Profiles"""
+ logger.debug("")
+ common.generic_list(callback=ctx.obj.infra_config_profile.list, filter=filter)
+
+
+@click.command(
+ name="infra-config-profile-show",
+ short_help="shows the details of an Infra Config Profile",
+)
+@click.argument("name")
+@click.pass_context
+def infra_config_profile_show(ctx, name):
+ """shows the details of an Infra Config Profile
+
+ NAME: name or ID of the Infra Config Profile
+ """
+ logger.debug("")
+ common.generic_show(callback=ctx.obj.infra_config_profile.get, name=name)
+
+
+@click.command(
+ name="infra-config-profile-update", short_help="updates an Infra Config Profile"
+)
+@click.argument("name")
+@click.option("--newname", help="New name for the Infra Config Profile")
+@click.option("--description", help="human readable description")
+@click.pass_context
+def infra_config_profile_update(ctx, name, newname, description, **kwargs):
+ """updates an Infra Config Profile
+
+ NAME: name or ID of the Infra Config Profile
+ """
+ logger.debug("")
+ profile_changes = common.generic_update(newname, description, kwargs)
+ ctx.obj.infra_config_profile.update(name, changes_dict=profile_changes)
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.cli_commands import common
+import logging
+
+logger = logging.getLogger("osmclient")
+
+
+@click.command(
+ name="infra-controller-profile-create",
+ short_help="creates an Infra Controller Profile",
+)
+@click.argument("name")
+@click.option("--description", default="", help="human readable description")
+@click.pass_context
+def infra_controller_profile_create(ctx, name, description, **kwargs):
+ """creates an Infra Controller Profile
+
+ NAME: name of the Infra Controller Profile
+ """
+ logger.debug("")
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
+ infra_controller_profile = kwargs
+ infra_controller_profile["name"] = name
+ if description:
+ infra_controller_profile["description"] = description
+ ctx.obj.infra_controller_profile.create(name, content_dict=infra_controller_profile)
+
+
+@click.command(
+ name="infra-controller-profile-delete",
+ short_help="deletes an Infra Controller Profile",
+)
+@click.argument("name")
+@click.option(
+ "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
+)
+@click.pass_context
+def infra_controller_profile_delete(ctx, name, force):
+ """deletes an Infra Controller Profile
+
+ NAME: name or ID of the Infra Controller Profile to be deleted
+ """
+ logger.debug("")
+ ctx.obj.infra_controller_profile.delete(name, force=force)
+
+
+@click.command(name="infra-controller-profile-list")
+@click.option(
+ "--filter",
+ help="restricts the list to the items matching the filter",
+)
+@click.pass_context
+def infra_controller_profile_list(ctx, filter):
+ """list all Infra Controller Profiles"""
+ logger.debug("")
+ common.generic_list(callback=ctx.obj.infra_controller_profile.list, filter=filter)
+
+
+@click.command(
+ name="infra-controller-profile-show",
+ short_help="shows the details of an Infra Controller Profile",
+)
+@click.argument("name")
+@click.pass_context
+def infra_controller_profile_show(ctx, name):
+ """shows the details of an Infra Controller Profile
+
+ NAME: name or ID of the Infra Controller Profile
+ """
+ logger.debug("")
+ common.generic_show(callback=ctx.obj.infra_controller_profile.get, name=name)
+
+
+@click.command(
+ name="infra-controller-profile-update",
+ short_help="updates an Infra Controller Profile",
+)
+@click.argument("name")
+@click.option("--newname", help="New name for the Infra Controller Profile")
+@click.option("--description", help="human readable description")
+@click.pass_context
+def infra_controller_profile_update(ctx, name, newname, description, **kwargs):
+ """updates an Infra Controller Profile
+
+ NAME: name or ID of the Infra Controller Profile
+ """
+ logger.debug("")
+ profile_changes = common.generic_update(newname, description, kwargs)
+ ctx.obj.infra_controller_profile.update(name, changes_dict=profile_changes)
cluster["namespace"] = namespace
if cni:
cluster["cni"] = yaml.safe_load(cni)
- ctx.obj.k8scluster.create(name, cluster, wait)
+ ctx.obj.k8scluster.add(name, cluster, wait)
@click.command(name="k8scluster-update", short_help="updates a K8s cluster")
NAME: name or ID of the K8s cluster to be deleted
"""
utils.check_client_version(ctx.obj, ctx.command.name)
- ctx.obj.k8scluster.delete(name, force, wait)
+ ctx.obj.k8scluster.remove(name, force, wait)
@click.command(name="k8scluster-list")
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.cli_commands import common
+from osmclient.common.exceptions import ClientException
+import logging
+import yaml
+
+logger = logging.getLogger("osmclient")
+
+
+def verify_and_update_ksu(ctx, ksu):
+ def get_oka_id(ctx, oka_name):
+ logger.debug("")
+ resp = ctx.obj.oka.get(oka_name)
+ logger.debug(f"OKA obtained: {resp}")
+ if "_id" in resp:
+ return resp["_id"]
+ else:
+ raise ClientException("Unexpected failure when reading the OKA")
+
+ def get_profile_id(ctx, profile, profile_type):
+ logger.debug("")
+ get_function = {
+ "infra-controller-profile": ctx.obj.infra_controller_profile.get,
+ "infra-config-profile": ctx.obj.infra_config_profile.get,
+ "app-profile": ctx.obj.app_profile.get,
+ "resource-profile": ctx.obj.resource_profile.get,
+ }
+ resp = get_function[profile_type](profile)
+ logger.debug(f"Profile obtained: {resp}")
+ if "_id" in resp:
+ return resp["_id"]
+ else:
+ raise ClientException("Unexpected failure when reading the profile")
+
+ logger.debug("")
+ if "name" not in ksu:
+ raise ClientException("A name must be provided for each KSU")
+ else:
+ # TODO if ctx.command.name == "ksu-update", update ksu if needed
+ pass
+ if "profile" in ksu:
+ ksu_profile = ksu["profile"]
+ ksu_profile_type = ksu_profile.get("profile_type")
+ if "_id" in ksu_profile:
+ ksu_profile["_id"] = get_profile_id(
+ ctx, ksu_profile["_id"], ksu_profile_type
+ )
+ else:
+ raise ClientException("A profile id or name must be provided for each KSU")
+ else:
+ raise ClientException("A profile must be provided for each KSU")
+ if "oka" in ksu:
+ for oka in ksu["oka"]:
+ if "_id" in oka:
+ oka["_id"] = get_oka_id(ctx, oka["_id"])
+ elif "sw_catalog_path" not in oka:
+ raise ClientException(
+ "An OKA id or name, or a SW catalog path must be provided for each OKA"
+ )
+ else:
+ raise ClientException(
+ "At least one OKA or SW catalog path must be provided for each KSU"
+ )
+
+
+def process_common_ksu_params(ctx, ksu_dict, ksu_args):
+ def process_common_oka_params(ctx, oka_dict, oka_args):
+ logger.debug("")
+ i = 0
+ while i < len(oka_args):
+ if oka_args[i] == "--oka":
+ if (i + 1 >= len(oka_args)) or oka_args[i + 1].startswith("--"):
+ raise ClientException("No OKA was provided after --oka")
+ oka_dict["_id"] = oka_args[i + 1]
+ i = i + 2
+ continue
+ if oka_args[i] == "--sw-catalog-path":
+ if (i + 1 >= len(oka_args)) or oka_args[i + 1].startswith("--"):
+ raise ClientException(
+ "No path was provided after --sw-catalog-path"
+ )
+ oka_dict["sw_catalog_path"] = oka_args[i + 1]
+ i = i + 2
+ continue
+ elif oka_args[i] == "--params":
+ if (i + 1 >= len(oka_args)) or oka_args[i + 1].startswith("--"):
+ raise ClientException("No params file was provided after --params")
+ with open(oka_args[i + 1], "r") as pf:
+ oka_dict["transformation"] = yaml.safe_load(pf.read())
+ i = i + 2
+ continue
+ else:
+ raise ClientException(f"Unknown option for OKA: {oka_args[i]}")
+
+ logger.debug("")
+ i = 0
+ while i < len(ksu_args):
+ if ksu_args[i] == "--description":
+ if (i + 1 >= len(ksu_args)) or ksu_args[i + 1].startswith("--"):
+ raise ClientException("No description was provided after --description")
+ ksu_dict["description"] = ksu_args[i + 1]
+ i = i + 2
+ continue
+ elif ksu_args[i] == "--profile":
+ if (i + 1 >= len(ksu_args)) or ksu_args[i + 1].startswith("--"):
+ raise ClientException(
+ "No profile name or ID was provided after --profile"
+ )
+ if "profile" not in ksu_dict:
+ ksu_dict["profile"] = {}
+ ksu_dict["profile"]["_id"] = ksu_args[i + 1]
+ i = i + 2
+ continue
+ elif ksu_args[i] == "--profile-type":
+ if (i + 1 >= len(ksu_args)) or ksu_args[i + 1].startswith("--"):
+ raise ClientException(
+ "No profile type was provided after --profile-type"
+ )
+ if "profile" not in ksu_dict:
+ ksu_dict["profile"] = {}
+ profile_type = ksu_args[i + 1]
+ profile_set = (
+ "infra-controller-profile",
+ "infra-config-profile",
+ "app-profile",
+ "resource-profile",
+ )
+ if profile_type not in profile_set:
+ raise ClientException(f"Profile type must be one of: {profile_set}")
+ ksu_dict["profile"]["profile_type"] = ksu_args[i + 1]
+ i = i + 2
+ continue
+ elif ksu_args[i] == "--oka" or ksu_args[i] == "--sw-catalog-path":
+ # Split the tuple by "--oka"
+ logger.debug(ksu_args[i:])
+ okas = common.iterator_split(ksu_args[i:], ("--oka", "--sw-catalog-path"))
+ logger.debug(f"OKAs: {okas}")
+ oka_list = []
+ for oka in okas:
+ oka_dict = {}
+ process_common_oka_params(ctx, oka_dict, oka)
+ oka_list.append(oka_dict)
+ ksu_dict["oka"] = oka_list
+ break
+ else:
+ if ksu_args[i] == "--params":
+ raise ClientException(
+ "Option --params must be specified for an OKA or sw-catalog-path"
+ )
+ else:
+ raise ClientException(f"Unknown option for KSU: {ksu_args[i]}")
+ return
+
+
+def process_ksu_params(ctx, param, value):
+ """
+ Processes the params in the commands ksu-create and ksu-update
+ Click does not allow advanced patterns for positional options like this:
+ --ksu jenkins --description "Jenkins KSU"
+ --profile profile1 --profile-type infra-controller-profile
+ --oka jenkins-controller --params jenkins-controller.yaml
+ --oka jenkins-config --params jenkins-config.yaml
+ --ksu prometheus --description "Prometheus KSU"
+ --profile profile2 --profile-type infra-controller-profile
+ --sw-catalog-path infra-controllers/prometheus --params prometheus-controller.yaml
+
+ It returns the dictionary with all the params stored in ctx.params["ksu_params"]
+ """
+
+ logger.debug("")
+ logger.debug(f"Args: {value}")
+ if param.name != "args":
+ raise ClientException(f"Unexpected param: {param.name}")
+ # Split the tuple "value" by "--ksu"
+ ksus = common.iterator_split(value, ["--ksu"])
+ logger.debug(f"KSUs: {ksus}")
+ ksu_list = []
+ for ksu in ksus:
+ ksu_dict = {}
+ if ksu[1].startswith("--"):
+ raise ClientException("Expected a KSU after --ksu")
+ ksu_dict["name"] = ksu[1]
+ process_common_ksu_params(ctx, ksu_dict, ksu[2:])
+ ksu_list.append(ksu_dict)
+ ctx.params["ksu_params"] = ksu_list
+ logger.debug(f"KSU params: {ksu_list}")
+ return
+
+
+@click.command(
+ name="ksu-create",
+ short_help="creates KSUs in OSM",
+ context_settings=dict(
+ ignore_unknown_options=True,
+ ),
+)
+@click.argument(
+ "args",
+ nargs=-1,
+ type=click.UNPROCESSED,
+ callback=process_ksu_params,
+)
+@click.pass_context
+def ksu_create(ctx, args, ksu_params):
+ """creates one or several Kubernetes SW Units (KSU) in OSM
+
+ \b
+ Options:
+ --ksu NAME name of the KSU to be created
+ --profile NAME name or ID of the profile the KSU will belong to
+ --profile_type TYPE type of the profile:
+ [infra-controller-profile|infra-config-profile|app-profile|resource-profile]
+ --oka OKA_ID name or ID of the OKA that will be incorporated to the KSU
+ (either --oka or --sw_catalog must be used)
+ --sw_catalog TEXT folder in the SW catalog (git repo) that will be incorporated to the KSU
+ (either --oka or --sw_catalog must be used)
+ --params FILE file with the values that parametrize the OKA or the sw_catalog
+
+ \b
+ Example:
+ osm ksu-create --ksu jenkins --description "Jenkins KSU"
+ --profile profile1 --profile-type infra-controller-profile
+ --oka jenkins-controller --params jenkins-controller.yaml
+ --oka jenkins-config --params jenkins-config.yaml
+ --ksu prometheus --description "Prometheus KSU"
+ --profile profile2 --profile-type infra-controller-profile
+ --sw-catalog-path infra-controllers/prometheus --params prometheus-controller.yaml
+ """
+ logger.debug("")
+ logger.debug(f"ksu_params:\n{yaml.safe_dump(ksu_params)}")
+ for ksu in ksu_params:
+ verify_and_update_ksu(ctx, ksu)
+ logger.debug(f"ksu_params:\n{yaml.safe_dump(ksu_params)}")
+ ctx.obj.ksu.multi_create_update(ksu_params, "create")
+
+
+@click.command(name="ksu-delete", short_help="deletes one or several KSU")
+@click.argument("ksus", type=str, nargs=-1, metavar="<KSU> [<KSU>...]")
+@click.option(
+ "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
+)
+@click.pass_context
+def ksu_delete(ctx, ksus, force):
+ """deletes one or several KSUs
+
+ KSU: name or ID of the KSU to be deleted
+ """
+ logger.debug("")
+ ctx.obj.ksu.multi_delete(ksus, "ksus", force=force)
+
+
+@click.command(name="ksu-list")
+@click.option(
+ "--filter",
+ help="restricts the list to the items matching the filter",
+)
+@click.pass_context
+def ksu_list(ctx, filter):
+ """list all Kubernetes SW Units (KSU)"""
+ logger.debug("")
+ common.generic_list(callback=ctx.obj.ksu.list, filter=filter)
+
+
+@click.command(name="ksu-show", short_help="shows the details of a KSU")
+@click.argument("name")
+@click.pass_context
+def ksu_show(ctx, name):
+ """shows the details of a KSU
+
+ NAME: name or ID of the KSU
+ """
+ logger.debug("")
+ common.generic_show(callback=ctx.obj.ksu.get, name=name)
+
+
+@click.command(
+ name="ksu-update",
+ short_help="updates KSUs in OSM",
+ context_settings=dict(
+ ignore_unknown_options=True,
+ ),
+)
+@click.argument(
+ "args",
+ nargs=-1,
+ type=click.UNPROCESSED,
+ callback=process_ksu_params,
+)
+@click.pass_context
+def ksu_update(ctx, args, ksu_params):
+ """updates one or several Kubernetes SW Units (KSU) in OSM
+
+ \b
+ Options:
+ --ksu NAME name of the KSU to be udpated
+ --profile NAME name or ID of the profile the KSU will belong to
+ --profile_type TYPE type of the profile:
+ [infra-controller-profile|infra-config-profile|app-profile|resource-profile]
+ --oka OKA_ID name or ID of the OKA that will be incorporated to the KSU
+ (either --oka or --sw_catalog must be used)
+ --sw_catalog TEXT folder in the SW catalog (git repo) that will be incorporated to the KSU
+ (either --oka or --sw_catalog must be used)
+ --params FILE file with the values that parametrize the OKA or the sw_catalog
+
+ \b
+ Example:
+ osm ksu-update --ksu jenkins --description "Jenkins KSU"
+ --profile profile1 --profile-type infra-controller-profile
+ --oka jenkins-controller --params jenkins-controller.yaml
+ --oka jenkins-config --params jenkins-config.yaml
+ --ksu prometheus --description "Prometheus KSU"
+ --profile profile2 --profile-type infra-controller-profile
+ --sw-catalog-path infra-controllers/prometheus --params prometheus-controller.yaml
+ """
+ logger.debug("")
+ logger.debug(f"ksu_params:\n{yaml.safe_dump(ksu_params)}")
+ for ksu in ksu_params:
+ verify_and_update_ksu(ctx, ksu)
+ logger.debug(f"ksu_params:\n{yaml.safe_dump(ksu_params)}")
+ ctx.obj.ksu.multi_create_update(ksu_params, "create")
import click
from osmclient.common.exceptions import ClientException
from osmclient.common.utils import validate_uuid4
-from osmclient.cli_commands import utils
+from osmclient.cli_commands import utils, common
import yaml
import logging
ctx.obj.ns.update(ns_name, op_data, wait=wait)
-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 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:
- 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"
if param.name != "args":
raise ClientException(f"Unexpected param: {param.name}")
# Split the tuple "value" by "--vnf"
- vnfs = iterator_split(value, ["--vnf"])
+ vnfs = common.iterator_split(value, ["--vnf"])
logger.debug(f"VNFs: {vnfs}")
heal_dict = {}
heal_dict["healVnfData"] = []
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.cli_commands import common
+import logging
+
+logger = logging.getLogger("osmclient")
+
+
+@click.command(name="oka-add", short_help="adds an OSM Kubernetes Application (OKA)")
+@click.argument("name")
+@click.argument("path")
+@click.option("--description", default="", help="human readable description")
+@click.pass_context
+def oka_add(ctx, name, path, description):
+ """adds an OKA to OSM
+
+ \b
+ NAME: name of the OKA
+ PATH: path to a folder with the OKA or to a tar.gz file with the OKA
+ """
+ logger.debug("")
+ oka = {}
+ oka["name"] = name
+ if description:
+ oka["description"] = description
+ ctx.obj.oka.create(name, content_dict=oka, filename=path)
+
+
+@click.command(
+ name="oka-delete", short_help="deletes an OSM Kubernetes Application (OKA)"
+)
+@click.argument("name")
+@click.option(
+ "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
+)
+@click.pass_context
+def oka_delete(ctx, name, force):
+ """deletes an OkA from OSM
+
+ NAME: name or ID of the OKA to be deleted
+ """
+ logger.debug("")
+ ctx.obj.oka.delete(name, force=force)
+
+
+@click.command(name="oka-list")
+@click.option(
+ "--filter",
+ help="restricts the list to the items matching the filter",
+)
+@click.pass_context
+def oka_list(ctx, filter):
+ """list all OSM Kubernetes Application (OKA)"""
+ logger.debug("")
+ common.generic_list(callback=ctx.obj.oka.list, filter=filter)
+
+
+@click.command(
+ name="oka-show",
+ short_help="shows the details of an OKA",
+)
+@click.argument("name")
+@click.pass_context
+def oka_show(ctx, name):
+ """shows the details of an OKA
+
+ NAME: name or ID of the OKA
+ """
+ logger.debug("")
+ common.generic_show(callback=ctx.obj.oka.get, name=name)
+
+
+@click.command(
+ name="oka-update", short_help="updates an OSM Kubernetes Application (OKA)"
+)
+@click.argument("name")
+@click.option("--newname", help="New name for the OSM Kubernetes Application (OKA)")
+@click.option("--description", help="human readable description")
+@click.pass_context
+def oka_update(ctx, name, newname, description, **kwargs):
+ """updates an OSM Kubernetes Application (OKA)
+
+ NAME: name or ID of the OSM Kubernetes Application (OKA)
+ """
+ logger.debug("")
+ oka_changes = common.generic_update(newname, description, kwargs)
+ ctx.obj.oka.update(name, changes_dict=oka_changes, force_multipart=True)
+
+
+@click.command(
+ name="oka-update-content",
+ short_help="updates the content of an OKA",
+)
+@click.argument("name")
+@click.argument("path")
+@click.pass_context
+def oka_update_content(ctx, name, path):
+ """updates the content of an OSM Kubernetes Application (OKA)
+
+ \b
+ NAME: name or ID of the OSM Kubernetes Application (OKA)
+ PATH: path to a folder with the OKA or to a tar.gz file with the OKA
+ """
+ logger.debug("")
+ ctx.obj.oka.update_content(name, path)
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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
+import logging
+from osmclient.common import print_output
+
+logger = logging.getLogger("osmclient")
+
+
+@click.command(name="profile-list")
+@click.option(
+ "--filter",
+ help="restricts the list to the items matching the filter",
+)
+@click.pass_context
+def profile_list(ctx, filter):
+ """list all Profiles"""
+ if filter:
+ filter = "&".join(filter)
+
+ resp_infra_controller_profile = ctx.obj.infra_controller_profile.list(filter)
+ resp_infra_config_profile = ctx.obj.infra_config_profile.list(filter)
+ resp_app_profile = ctx.obj.app_profile.list(filter)
+ resp_resource_profile = ctx.obj.resource_profile.list(filter)
+
+ headers = ["Name", "Id", "Profile Type", "Default"]
+ rows = []
+
+ profile_list = [
+ {"type": "Infra Controller Profile", "list": resp_infra_controller_profile},
+ {"type": "Infra Config Profile", "list": resp_infra_config_profile},
+ {"type": "App Profile", "list": resp_app_profile},
+ {"type": "Resource Profile", "list": resp_resource_profile},
+ ]
+ for profile in profile_list:
+ profile_type = profile["type"]
+ profile_list_item = profile["list"]
+ for item in profile_list_item:
+ rows.append(
+ [
+ item["name"],
+ item["_id"],
+ profile_type,
+ item["default"],
+ ]
+ )
+ print_output.print_output("table", headers, rows)
+
+
+def patch_cluster_profile(ctx, profile, cluster, profile_type, patch_string):
+ logger.debug("")
+ get_function = {
+ "infra-controller-profile": ctx.obj.infra_controller_profile.get,
+ "infra-config-profile": ctx.obj.infra_config_profile.get,
+ "app-profile": ctx.obj.app_profile.get,
+ "resource-profile": ctx.obj.resource_profile.get,
+ }
+ resp = get_function[profile_type](profile)
+ profile_id = resp["_id"]
+ resp = ctx.obj.cluster.get(cluster)
+ cluster_id = resp["_id"]
+ update_dict = {patch_string: [{"id": profile_id}]}
+ logger.debug(
+ f"Cluster_id: {cluster_id}. Profile_id: {profile_id}. Update_dict = {update_dict}"
+ )
+ return cluster_id, update_dict
+
+
+@click.command(name="attach-profile")
+@click.argument("profile")
+@click.argument("cluster")
+@click.option(
+ "--profile_type",
+ type=click.Choice(
+ [
+ "infra-controller-profile",
+ "infra-config-profile",
+ "app-profile",
+ "resource-profile",
+ ]
+ ),
+ prompt=True,
+ help="type of profile",
+)
+@click.pass_context
+def attach_profile(ctx, profile, cluster, profile_type):
+ """attaches profile to cluster
+
+ \b
+ PROFILE: name or ID of the profile
+ CLUSTER: name or ID of the Kubernetes cluster
+ """
+ logger.debug("")
+ cluster_id, update_dict = patch_cluster_profile(
+ ctx, profile, cluster, profile_type, "add_profile"
+ )
+ ctx.obj.cluster.update_profiles(cluster_id, profile_type, update_dict)
+
+
+@click.command(name="detach-profile")
+@click.argument("profile")
+@click.argument("cluster")
+@click.option(
+ "--profile_type",
+ type=click.Choice(
+ [
+ "infra-controller-profile",
+ "infra-config-profile",
+ "app-profile",
+ "resource-profile",
+ ]
+ ),
+ prompt=True,
+ help="type of profile",
+)
+@click.pass_context
+def detach_profile(ctx, profile, cluster, profile_type):
+ """detaches profile from cluster
+
+ \b
+ PROFILE: name or ID of the profile
+ CLUSTER: name or ID of the Kubernetes cluster
+ """
+ logger.debug("")
+ cluster_id, update_dict = patch_cluster_profile(
+ ctx, profile, cluster, profile_type, "remove_profile"
+ )
+ ctx.obj.cluster.update_profiles(cluster_id, profile_type, update_dict)
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.cli_commands import common
+import logging
+
+logger = logging.getLogger("osmclient")
+
+
+@click.command(
+ name="resource-profile-create", short_help="creates an Resource Profile to OSM"
+)
+@click.argument("name")
+@click.option("--description", default=None, help="human readable description")
+@click.pass_context
+def resource_profile_create(ctx, name, description, **kwargs):
+ """creates an Resource Profile to OSM
+
+ NAME: name of the Resource Profile
+ """
+ logger.debug("")
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
+ resource_profile = kwargs
+ resource_profile["name"] = name
+ if description:
+ resource_profile["description"] = description
+ ctx.obj.resource_profile.create(name, content_dict=resource_profile)
+
+
+@click.command(name="resource-profile-delete", short_help="deletes an Resource Profile")
+@click.argument("name")
+@click.option(
+ "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
+)
+@click.pass_context
+def resource_profile_delete(ctx, name, force):
+ """deletes an Resource Profile
+
+ NAME: name or ID of the Resource Profile to be deleted
+ """
+ logger.debug("")
+ ctx.obj.resource_profile.delete(name, force=force)
+
+
+@click.command(name="resource-profile-list")
+@click.option(
+ "--filter",
+ help="restricts the list to the items matching the filter",
+)
+@click.pass_context
+def resource_profile_list(ctx, filter):
+ """list all Resource Profiles"""
+ logger.debug("")
+ common.generic_list(callback=ctx.obj.resource_profile.list, filter=filter)
+
+
+@click.command(
+ name="resource-profile-show",
+ short_help="shows the details of an Resource Profile",
+)
+@click.argument("name")
+@click.pass_context
+def resource_profile_show(ctx, name):
+ """shows the details of an Resource Profile
+
+ NAME: name or ID of the Resource Profile
+ """
+ logger.debug("")
+ common.generic_show(callback=ctx.obj.resource_profile.get, name=name)
+
+
+@click.command(name="resource-profile-update", short_help="updates an Resource Profile")
+@click.argument("name")
+@click.option("--newname", help="New name for the Resource Profile")
+@click.option("--description", help="human readable description")
+@click.pass_context
+def resource_profile_update(ctx, name, newname, description, **kwargs):
+ """updates an Resource Profile
+
+ NAME: name or ID of the Resource Profile
+ """
+ logger.debug("")
+ profile_changes = common.generic_update(newname, description, kwargs)
+ ctx.obj.resource_profile.update(name, changes_dict=profile_changes)
"until the operation is completed, or timeout",
)
@click.option("--vca", default=None, help="VCA to be used in this VIM account")
-@click.option(
- "--creds", default=None, help="credentials file (only applicable for GCP VIM type)"
-)
+@click.option("--creds", default=None, help="credentials file")
@click.option(
"--prometheus_url",
default=None,
from osmclient.common.exceptions import ClientException
from osmclient.cli_commands import (
alarms,
+ app_profile,
+ cluster,
+ infra_config_profile,
+ infra_controller_profile,
k8scluster,
+ ksu,
metrics,
netslice_instance,
netslice_ops,
nslcm_ops,
nslcm,
nspkg,
+ oka,
other,
packages,
pdus,
+ profiles,
rbac,
repo,
+ resource_profile,
sdnc,
subscriptions,
vca,
cli_osm.add_command(k8scluster.k8scluster_show)
cli_osm.add_command(k8scluster.k8scluster_update)
+ cli_osm.add_command(infra_controller_profile.infra_controller_profile_create)
+ cli_osm.add_command(infra_controller_profile.infra_controller_profile_delete)
+ cli_osm.add_command(infra_controller_profile.infra_controller_profile_list)
+ cli_osm.add_command(infra_controller_profile.infra_controller_profile_show)
+ cli_osm.add_command(infra_controller_profile.infra_controller_profile_update)
+
+ cli_osm.add_command(infra_config_profile.infra_config_profile_create)
+ cli_osm.add_command(infra_config_profile.infra_config_profile_delete)
+ cli_osm.add_command(infra_config_profile.infra_config_profile_list)
+ cli_osm.add_command(infra_config_profile.infra_config_profile_show)
+ cli_osm.add_command(infra_config_profile.infra_config_profile_update)
+
+ cli_osm.add_command(app_profile.app_profile_create)
+ cli_osm.add_command(app_profile.app_profile_delete)
+ cli_osm.add_command(app_profile.app_profile_list)
+ cli_osm.add_command(app_profile.app_profile_show)
+ cli_osm.add_command(app_profile.app_profile_update)
+
+ cli_osm.add_command(resource_profile.resource_profile_create)
+ cli_osm.add_command(resource_profile.resource_profile_delete)
+ cli_osm.add_command(resource_profile.resource_profile_list)
+ cli_osm.add_command(resource_profile.resource_profile_show)
+ cli_osm.add_command(resource_profile.resource_profile_update)
+
+ cli_osm.add_command(profiles.profile_list)
+ cli_osm.add_command(profiles.attach_profile)
+ cli_osm.add_command(profiles.detach_profile)
+
+ cli_osm.add_command(oka.oka_add)
+ cli_osm.add_command(oka.oka_delete)
+ cli_osm.add_command(oka.oka_list)
+ cli_osm.add_command(oka.oka_show)
+ cli_osm.add_command(oka.oka_update)
+ cli_osm.add_command(oka.oka_update_content)
+
+ cli_osm.add_command(ksu.ksu_create)
+ cli_osm.add_command(ksu.ksu_delete)
+ cli_osm.add_command(ksu.ksu_list)
+ cli_osm.add_command(ksu.ksu_show)
+ cli_osm.add_command(ksu.ksu_update)
+
+ cli_osm.add_command(cluster.cluster_create)
+ cli_osm.add_command(cluster.cluster_delete)
+ cli_osm.add_command(cluster.cluster_list)
+ cli_osm.add_command(cluster.cluster_show)
+ cli_osm.add_command(cluster.cluster_update)
+
cli_osm.add_command(netslice_instance.nsi_create1)
cli_osm.add_command(netslice_instance.nsi_create2)
cli_osm.add_command(netslice_instance.nsi_delete1)
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+
+"""
+OSM App Profile API handling
+"""
+
+from osmclient.sol005.osm_api_object import GenericOSMAPIObject
+
+
+class AppProfile(GenericOSMAPIObject):
+ def __init__(self, http=None, client=None):
+ super().__init__(http, client)
+ self._apiName = "/k8scluster"
+ self._apiVersion = "/v1"
+ self._apiResource = "/app_profiles"
+ self._logObjectName = "app_profile"
+ self._apiBase = "{}{}{}".format(
+ self._apiName, self._apiVersion, self._apiResource
+ )
from osmclient.sol005 import osmrepo
from osmclient.sol005 import subscription
from osmclient.common import package_tool
+from osmclient.sol005 import oka
+from osmclient.sol005 import ksu
+from osmclient.sol005 import infra_controller_profile
+from osmclient.sol005 import infra_config_profile
+from osmclient.sol005 import app_profile
+from osmclient.sol005 import resource_profile
+from osmclient.sol005 import cluster
from osmclient.common.exceptions import ClientException
import json
import logging
self.osmrepo = osmrepo.OSMRepo(self._http_client, client=self)
self.package_tool = package_tool.PackageTool(client=self)
self.subscription = subscription.Subscription(self._http_client, client=self)
+ self.ksu = ksu.KSU(self._http_client, client=self)
+ self.oka = oka.OKA(self._http_client, client=self)
+ self.infra_controller_profile = infra_controller_profile.InfraControllerProfile(
+ self._http_client, client=self
+ )
+ self.infra_config_profile = infra_config_profile.InfraConfigProfile(
+ self._http_client, client=self
+ )
+ self.app_profile = app_profile.AppProfile(self._http_client, client=self)
+ self.resource_profile = resource_profile.ResourceProfile(
+ self._http_client, client=self
+ )
+ self.cluster = cluster.Cluster(self._http_client, client=self)
"""
self.vca = vca.Vca(http_client, client=self, **kwargs)
self.utils = utils.Utils(http_client, **kwargs)
postfields_dict["project_domain_name"] = self._project_domain_name
if self._user_domain_name:
postfields_dict["user_domain_name"] = self._user_domain_name
- http_code, resp = self._http_client.post_cmd(
+ _, resp = self._http_client.post_cmd(
endpoint=self._auth_endpoint,
postfields_dict=postfields_dict,
skip_query_admin=True,
)
- # if http_code not in (200, 201, 202, 204):
- # message ='Authentication error: not possible to get auth token\nresp:\n{}'.format(resp)
- # raise ClientException(message)
token = json.loads(resp) if resp else None
if token.get("message") == "change_password" and not pwd_change:
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+
+"""
+OSM Cluster API handling
+"""
+
+from osmclient.sol005.osm_api_object import GenericOSMAPIObject
+from osmclient.common.exceptions import ClientException
+import json
+
+
+class Cluster(GenericOSMAPIObject):
+ def __init__(self, http=None, client=None):
+ super().__init__(http, client)
+ self._apiName = "/k8scluster"
+ self._apiVersion = "/v1"
+ self._apiResource = "/clusters"
+ self._logObjectName = "cluster"
+ self._apiBase = "{}{}{}".format(
+ self._apiName, self._apiVersion, self._apiResource
+ )
+
+ def update_profiles(self, cluster_id, profile_type, update_dict):
+ """Updates the profiles in a cluster"""
+ self._logger.debug("")
+ self._client.get_token()
+ profile_endpoint = {
+ "infra-controller-profile": "infra_controller_profiles",
+ "infra-config-profile": "infra_config_profiles",
+ "app-profile": "app_profiles",
+ "resource-profile": "resource_profiles",
+ }
+ http_code, resp = self._http.patch_cmd(
+ endpoint="{}/{}/{}".format(
+ self._apiBase, cluster_id, profile_endpoint[profile_type]
+ ),
+ postfields_dict=update_dict,
+ skip_query_admin=True,
+ )
+ if http_code in (200, 201, 202):
+ if resp:
+ resp = json.loads(resp)
+ if not resp or "id" not in resp:
+ raise ClientException(f"unexpected response from server - {resp}")
+ print(resp["id"])
+ elif http_code == 204:
+ print("Updated")
+
+ # def get_credentials(self, cluster_id):
+ # # TODO:
self,
endpoint="",
postfields_dict=None,
+ postdata=None,
formfile=None,
filename=None,
put_method=False,
jsondata_log = jsondata
requests_cmd.json = postfields_dict
self._logger.verbose("Request POSTFIELDS: {}".format(jsondata_log))
- elif formfile is not None:
- requests_cmd.files = {formfile[0]: formfile[1]}
- elif filename is not None:
+ if postdata is not None:
+ self._logger.verbose("Request DATA: {}".format(postdata))
+ requests_cmd.data = postdata
+ if formfile is not None:
+ self._logger.verbose("Request FILES: {}".format(formfile))
+ requests_cmd.files = formfile
+ if filename is not None:
with open(filename, "rb") as stream:
- postdata = stream.read()
+ filedata = stream.read()
self._logger.verbose("Request POSTFIELDS: Binary content")
- requests_cmd.data = postdata
+ requests_cmd.data = filedata
if put_method:
self._logger.info(
self,
endpoint="",
postfields_dict=None,
+ postdata=None,
formfile=None,
filename=None,
skip_query_admin=False,
return self.send_cmd(
endpoint=endpoint,
postfields_dict=postfields_dict,
+ postdata=postdata,
formfile=formfile,
filename=filename,
put_method=False,
self,
endpoint="",
postfields_dict=None,
+ postdata=None,
formfile=None,
filename=None,
skip_query_admin=False,
return self.send_cmd(
endpoint=endpoint,
postfields_dict=postfields_dict,
+ postdata=postdata,
formfile=formfile,
filename=filename,
put_method=True,
self,
endpoint="",
postfields_dict=None,
+ postdata=None,
formfile=None,
filename=None,
skip_query_admin=False,
return self.send_cmd(
endpoint=endpoint,
postfields_dict=postfields_dict,
+ postdata=postdata,
formfile=formfile,
filename=filename,
put_method=False,
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+
+"""
+OSM Infra Config Profile API handling
+"""
+
+from osmclient.sol005.osm_api_object import GenericOSMAPIObject
+
+
+class InfraConfigProfile(GenericOSMAPIObject):
+ def __init__(self, http=None, client=None):
+ super().__init__(http, client)
+ self._apiName = "/k8scluster"
+ self._apiVersion = "/v1"
+ self._apiResource = "/infra_config_profiles"
+ self._logObjectName = "infra_config_profile"
+ self._apiBase = "{}{}{}".format(
+ self._apiName, self._apiVersion, self._apiResource
+ )
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+
+"""
+OSM Infra Controller Profile API handling
+"""
+
+from osmclient.sol005.osm_api_object import GenericOSMAPIObject
+
+
+class InfraControllerProfile(GenericOSMAPIObject):
+ def __init__(self, http=None, client=None):
+ super().__init__(http, client)
+ self._apiName = "/k8scluster"
+ self._apiVersion = "/v1"
+ self._apiResource = "/infra_controller_profiles"
+ self._logObjectName = "infra_controller_profile"
+ self._apiBase = "{}{}{}".format(
+ self._apiName, self._apiVersion, self._apiResource
+ )
deleteFlag=deleteFlag,
)
- def create(self, name, k8s_cluster, wait=False):
+ def add(self, name, k8s_cluster, wait=False):
self._client.get_token()
vim_account = self._get_vim_account(k8s_cluster["vim_account"])
k8s_cluster["vim_account"] = vim_account["_id"]
return cluster["_id"]
raise NotFound("K8s cluster {} not found".format(name))
- def delete(self, name, force=False, wait=False):
+ def remove(self, name, force=False, wait=False):
self._client.get_token()
cluster_id = name
if not utils.validate_uuid4(name):
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+
+"""
+OSM KSU API handling
+"""
+
+from osmclient.sol005.osm_api_object import GenericOSMAPIObject
+
+
+class KSU(GenericOSMAPIObject):
+ def __init__(self, http=None, client=None):
+ super().__init__(http, client)
+ self._apiName = "/ksu"
+ self._apiVersion = "/v1"
+ self._apiResource = "/ksus"
+ self._logObjectName = "ksu"
+ self._apiBase = "{}{}{}".format(
+ self._apiName, self._apiVersion, self._apiResource
+ )
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+
+"""
+OSM Kubernetes Application (OKA) API handling
+"""
+
+from osmclient.sol005.osm_api_object import GenericOSMAPIObject
+
+
+class OKA(GenericOSMAPIObject):
+ def __init__(self, http=None, client=None):
+ super().__init__(http, client)
+ self._apiName = "/oka"
+ self._apiVersion = "/v1"
+ self._apiResource = "/oka_packages"
+ self._logObjectName = "oka_package"
+ self._apiBase = "{}{}{}".format(
+ self._apiName, self._apiVersion, self._apiResource
+ )
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+
+"""
+OSM Generic OSM API Object
+Abstract base class used by other objects
+"""
+
+from abc import ABC, abstractmethod
+from osmclient.common.exceptions import NotFound
+from osmclient.common.exceptions import ClientException
+from osmclient.common import utils
+import json
+import logging
+import magic
+import os
+import shutil
+import tempfile
+
+
+class GenericOSMAPIObject(ABC):
+ @abstractmethod
+ def __init__(self, http=None, client=None):
+ self._http = http
+ self._client = client
+ self._apiName = "/generic"
+ self._apiVersion = "/v1"
+ self._apiResource = "/objects"
+ self._logObjectName = "object"
+ self._apiBase = "{}{}{}".format(
+ self._apiName, self._apiVersion, self._apiResource
+ )
+ self._logger = logging.getLogger("osmclient")
+
+ def set_http_headers(self, filename, multipart=False):
+ headers = self._client._headers
+ if multipart:
+ # requests library should know which Content-Type to set
+ # headers["Content-Type"] = "multipart/form-data"
+ headers.pop("Content-Type")
+ return
+ mime_type = magic.from_file(filename, mime=True)
+ if mime_type is None:
+ raise ClientException(
+ "Unexpected MIME type for file {}: MIME type {}".format(
+ filename, mime_type
+ )
+ )
+ headers["Content-Filename"] = os.path.basename(filename)
+ if mime_type in ["application/yaml", "text/plain", "application/json"]:
+ headers["Content-Type"] = "text/plain"
+ elif mime_type in ["application/gzip", "application/x-gzip"]:
+ headers["Content-Type"] = "application/gzip"
+ elif mime_type in ["application/zip"]:
+ headers["Content-Type"] = "application/zip"
+ else:
+ raise ClientException(
+ "Unexpected MIME type for file {}: MIME type {}".format(
+ filename, mime_type
+ )
+ )
+ headers["Content-File-MD5"] = utils.md5(filename)
+
+ def generate_package(self, name, path, basedir):
+ """Generate a package (tar.gz) from path"""
+ self._logger.debug("")
+ if not os.path.exists(path):
+ raise ClientException(f"The specified {path} does not exist")
+ try:
+ self._logger.debug(f"basename: {os.path.join(basedir, name)}")
+ self._logger.debug(f"root_dir: {path}")
+ zip_package = shutil.make_archive(
+ base_name=os.path.join(basedir, name),
+ format="gztar",
+ root_dir=path,
+ )
+ self._logger.info(f"Package created: {zip_package}")
+ return zip_package
+ except Exception as exc:
+ raise ClientException("failure during build of zip file: {}".format(exc))
+
+ def list(self, filter=None):
+ """List all Generic OSM API Object"""
+ self._logger.debug("")
+ self._client.get_token()
+ filter_string = ""
+ if filter:
+ filter_string = "?{}".format(filter)
+ _, resp = self._http.get2_cmd("{}{}".format(self._apiBase, filter_string))
+
+ if resp:
+ return json.loads(resp)
+ return list()
+
+ def get(self, name):
+ """
+ Gets and shows an individual Generic OSM API Object
+ from the list of all objects of the same kind
+ """
+ self._logger.debug("")
+ self._client.get_token()
+ if utils.validate_uuid4(name):
+ for item in self.list():
+ if name == item["_id"]:
+ return item
+ else:
+ for item in self.list():
+ if "_id" in item and name == item["_id"]:
+ return item
+ elif "name" in item and name == item["name"]:
+ return item
+ raise NotFound(f"{self._logObjectName} {name} not found")
+
+ def get_individual(self, name):
+ """Gets and shows an individual Generic OSM API Object"""
+ # It is redundant, since the method getalready gets the whole item
+ # The only difference is that a different primitive is exercised
+ self._logger.debug("")
+ item = self.get(name)
+ try:
+ _, resp = self._http.get2_cmd("{}/{}".format(self._apiBase, item["_id"]))
+ if resp:
+ return json.loads(resp)
+ except NotFound:
+ raise NotFound(f"{self._logObjectName} '{name}' not found")
+ raise NotFound(f"{self._logObjectName} '{name}' not found")
+
+ def delete(self, name, force=False):
+ """Delete the Generic OSM API Object specified by name"""
+ self._logger.debug("")
+ self._client.get_token()
+ item = self.get(name)
+ querystring = ""
+ if force:
+ querystring = "?FORCE=True"
+ http_code, resp = self._http.delete_cmd(
+ "{}/{}{}".format(self._apiBase, item["_id"], querystring)
+ )
+
+ if http_code == 202:
+ print("Deletion in progress")
+ elif http_code == 204:
+ print("Deleted")
+ else:
+ msg = resp or ""
+ raise ClientException(
+ f"failed to delete {self._logObjectName} {name} - {msg}"
+ )
+
+ def generic_post(self, endpoint, content):
+ return self._http.post_cmd(
+ endpoint=endpoint,
+ postfields_dict=content,
+ skip_query_admin=True,
+ )
+
+ def multi_create_update(self, content, endpoint_suffix):
+ """Create or update a bundle of Generic OSM API Object specified by content"""
+ self._logger.debug("")
+ self._client.get_token()
+ endpoint = f"{self._apiBase}/{endpoint_suffix}"
+ http_code, resp = self.generic_post(endpoint=endpoint, content=content)
+ if http_code in (200, 201, 202):
+ if not resp:
+ raise ClientException(f"unexpected response from server - {resp}")
+ resp = json.loads(resp)
+ print(resp)
+ elif http_code == 204:
+ print("Received")
+
+ def multi_delete(self, name_list, prefix="", force=False):
+ """Delete the list of Generic OSM API Object specified by name_list"""
+ self._logger.debug("")
+ self._client.get_token()
+
+ # Get the id for each element
+ item_list = []
+ for name in name_list:
+ item = self.get(name)
+ item_list.append(item["_id"])
+
+ # Prepare delete_content
+ if not prefix:
+ delete_content = item_list
+ else:
+ delete_content = {prefix: []}
+ for i in item_list:
+ delete_content[prefix].append({"_id": i})
+
+ querystring = ""
+ if force:
+ querystring = "?FORCE=True"
+ http_code, resp = self.generic_post(
+ endpoint=f"{self._apiBase}/delete{querystring}", content=delete_content
+ )
+ if http_code in (201, 202):
+ print("Deletion in progress")
+ elif http_code in (200, 204):
+ print("Deleted")
+ else:
+ msg = resp or ""
+ raise ClientException(
+ f"failed to create {self._logObjectName}. Http code: {http_code}. Message: {msg}"
+ )
+
+ def create(self, name, content_dict=None, filename=None):
+ """Creates a new Generic OSM API Object"""
+ self._logger.debug(f"Creating Generic OSM API Object {name}")
+ self._client.get_token()
+ # If filename is dir, generate a tar.gz file
+ tempdir = None
+ if filename:
+ if os.path.isdir(filename):
+ tempdir = tempfile.TemporaryDirectory()
+ basedir = tempdir.name
+ filename = filename.rstrip("/")
+ filename = self.generate_package(name, filename, basedir)
+ if content_dict and not filename:
+ # Typical case. Only a dict
+ self._logger.debug("Sending only a dict")
+ _, resp = self._http.post_cmd(
+ endpoint=self._apiBase,
+ postfields_dict=content_dict,
+ skip_query_admin=True,
+ )
+ elif not content_dict and not filename:
+ # No content at all. Raise error
+ raise ClientException(
+ f"failed to create {self._logObjectName} {name} - No content to be sent"
+ )
+ elif content_dict and filename:
+ self._logger.debug("Sending a multipart file: a dict and a file")
+ # It's a form and data
+ formfile = {
+ "package": (
+ os.path.basename(filename),
+ open(filename, "rb"),
+ "application/gzip",
+ ),
+ }
+ self._logger.debug(f"formfile: {formfile}")
+ self.set_http_headers(filename, multipart=True)
+ _, resp = self._http.post_cmd(
+ endpoint=self._apiBase, postdata=content_dict, formfile=formfile
+ )
+ elif not content_dict and filename:
+ self._logger.debug("Sending only a file")
+ # Only a file to be sent
+ self.set_http_headers(filename)
+ _, resp = self._http.post_cmd(endpoint=self._apiBase, filename=filename)
+ if resp:
+ resp = json.loads(resp)
+ self._logger.debug(f"Resp: {resp}")
+ if not resp or "_id" not in resp:
+ raise ClientException("Unexpected response from server - {}".format(resp))
+ print(resp["_id"])
+ if tempdir:
+ tempdir.cleanup()
+
+ def update(self, name, changes_dict=None, filename=None, force_multipart=False):
+ """Updates a Generic OSM API Object"""
+ self._logger.debug("")
+ self._client.get_token()
+ item = self.get(name)
+
+ formfile = None
+ tempdir = None
+ if filename:
+ # If filename is dir, generate a tar.gz file
+ if os.path.isdir(filename):
+ tempdir = tempfile.TemporaryDirectory()
+ basedir = tempdir.name
+ filename = filename.rstrip("/")
+ filename = self.generate_package(name, filename, basedir)
+ self._logger.debug("Sending a multipart file: a dict and a file")
+ # It's a form and data
+ formfile = {
+ "package": (filename, open(filename, "rb"), "application/gzip"),
+ }
+ self.set_http_headers(filename, multipart=True)
+
+ if not filename and force_multipart:
+ self.set_http_headers(filename, multipart=True)
+
+ http_code, resp = self._http.patch_cmd(
+ endpoint="{}/{}".format(self._apiBase, item["_id"]),
+ postfields_dict=changes_dict,
+ formfile=formfile,
+ skip_query_admin=True,
+ )
+ if http_code in (200, 201, 202):
+ if resp:
+ resp = json.loads(resp)
+ if not resp or "id" not in resp:
+ raise ClientException(f"unexpected response from server - {resp}")
+ print(resp["id"])
+ elif http_code == 204:
+ print("Updated")
+
+ def update_content(self, name, filename):
+ """Updates the content of a Generic OSM API Object"""
+ self._logger.debug("")
+ self._client.get_token()
+ item = self.get(name)
+
+ tempdir = None
+ if filename:
+ if os.path.isdir(filename):
+ tempdir = tempfile.TemporaryDirectory()
+ basedir = tempdir.name
+ filename = filename.rstrip("/")
+ filename = self.generate_package(name, filename, basedir)
+ self._logger.debug("Sending only a file")
+ # Only a file to be sent
+ self.set_http_headers(filename)
+ http_code, resp = self._http.put_cmd(
+ endpoint="{}/{}".format(self._apiBase, item["_id"]),
+ filename=filename,
+ skip_query_admin=True,
+ )
+ if http_code in (200, 201, 202):
+ if resp:
+ resp = json.loads(resp)
+ if not resp or "id" not in resp:
+ raise ClientException(f"unexpected response from server - {resp}")
+ print(resp["id"])
+ elif http_code == 204:
+ print("Updated")
+ if tempdir:
+ tempdir.cleanup()
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+
+"""
+OSM Resource Profile API handling
+"""
+
+from osmclient.sol005.osm_api_object import GenericOSMAPIObject
+
+
+class ResourceProfile(GenericOSMAPIObject):
+ def __init__(self, http=None, client=None):
+ super().__init__(http, client)
+ self._apiName = "/k8scluster"
+ self._apiVersion = "/v1"
+ self._apiResource = "/resource_profiles"
+ self._logObjectName = "resource_profile"
+ self._apiBase = "{}{}{}".format(
+ self._apiName, self._apiVersion, self._apiResource
+ )