| ####################################################################################### |
| # 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") |