accessible via L3 routing, e.g. "{(k8s_net1:vim_network1) [,(k8s_net2:vim_network2) ...]}"''',
)
@click.option(
- "--init-helm2/--skip-helm2",
- required=False,
- default=True,
- help="Initialize helm v2",
+ "--init-helm2/--skip-helm2", required=False, default=True, help="Initialize helm v2"
)
@click.option(
- "--init-helm3/--skip-helm3",
- required=False,
- default=True,
- help="Initialize helm v3",
+ "--init-helm3/--skip-helm3", required=False, default=True, help="Initialize helm v3"
)
@click.option(
"--init-jujubundle/--skip-jujubundle",
prompt=True,
help="Name of the cloud credentialsto be used for the K8s cloud",
)
-@click.option(
- "--model-config",
- default={},
- help="Configuration options for the models",
-)
+@click.option("--model-config", default={}, help="Configuration options for the models")
@click.option("--description", default=None, help="human readable description")
@click.pass_context
def vca_add(
"--k8s-credentials",
help="Name of the cloud credentialsto be used for the K8s cloud",
)
-@click.option(
- "--model-config",
- help="Configuration options for the models",
-)
+@click.option("--model-config", help="Configuration options for the models")
@click.option("--description", default=None, help="human readable description")
@click.pass_context
def vca_update(
print(table)
+###########################
+# PaaS operations
+###########################
+
+
+@cli_osm.command(name="paas-add", short_help="adds a PaaS to OSM.")
+@click.argument("name")
+@click.option(
+ "--paas_type",
+ type=click.Choice(["juju"]),
+ default="juju",
+ prompt=True,
+ help="Type of PaaS that can be used. (For the moment, only juju is supported).",
+)
+@click.option(
+ "--endpoints",
+ prompt=True,
+ help="Comma-separated list of IP or hostnames of the PaaS.",
+)
+@click.option("--user", prompt=True, help="Username with admin priviledges.")
+@click.option("--secret", prompt=True, help="Password of the specified username.")
+@click.option(
+ "--config", default={}, help="Extra configuration needed by PaaS service."
+)
+@click.option("--description", default=None, help="Human readable description.")
+@click.pass_context
+def paas_add(ctx, name, paas_type, endpoints, user, secret, config, description):
+ """adds a PaaS to OSM.
+ Args:
+ name (str): Name of the new PaaS.
+ """
+ check_client_version(ctx.obj, ctx.command.name)
+ paas = {
+ "name": name,
+ "paas_type": paas_type,
+ "endpoints": endpoints.split(","),
+ "user": user,
+ "secret": secret,
+ }
+ if description:
+ paas["description"] = description
+ if config:
+ config = load(config)
+ paas["config"] = config
+ ctx.obj.paas.create(paas)
+
+
+@cli_osm.command(name="paas-update", short_help="updates a PaaS")
+@click.argument("name")
+@click.option("--newname", help="New name for the PaaS")
+@click.option(
+ "--paas_type",
+ type=click.Choice(["juju"]),
+ help="Type of PaaS that can be used. (For the moment, only juju is supported)",
+)
+@click.option(
+ "--endpoints", help="Comma-separated list of IP or hostnames of the Juju controller"
+)
+@click.option("--user", help="Username with admin priviledges")
+@click.option("--secret", help="Password of the specified username")
+@click.option("--config", help="Extra configuration needed by PaaS service")
+@click.option("--description", default=None, help="Human readable description")
+@click.pass_context
+def paas_update(
+ ctx, name, newname, paas_type, endpoints, user, secret, config, description
+):
+ """updates a PaaS.
+ Args:
+ name (str): Name or ID of the PaaS to update.
+ """
+ check_client_version(ctx.obj, ctx.command.name)
+ paas = {}
+ if newname:
+ paas["name"] = newname
+ if paas_type:
+ paas["paas_type"] = paas_type
+ if endpoints:
+ paas["endpoints"] = endpoints.split(",")
+ if user:
+ paas["user"] = user
+ if secret:
+ paas["secret"] = secret
+ if description:
+ paas["description"] = description
+ if config:
+ config = load(config)
+ paas["config"] = config
+ ctx.obj.paas.update(name, paas)
+
+
+@cli_osm.command(name="paas-delete", short_help="deletes a PaaS")
+@click.argument("name")
+@click.option(
+ "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
+)
+@click.pass_context
+def paas_delete(ctx, name, force):
+ """deletes a PaaS.
+
+ Args:
+ name (str): Name or ID of the PaaS to delete.
+ """
+ check_client_version(ctx.obj, ctx.command.name)
+ ctx.obj.paas.delete(name, force=force)
+
+
+@cli_osm.command(name="paas-list")
+@click.option(
+ "--filter",
+ default=None,
+ multiple=True,
+ help="Restricts the list to the PaaS matching the filter",
+)
+@click.option("--literal", is_flag=True, help="Print literally, no pretty table")
+@click.option("--long", is_flag=True, help="get more details")
+@click.pass_context
+def paas_list(ctx, filter, literal, long):
+ """List PaaSs"""
+ check_client_version(ctx.obj, ctx.command.name)
+ if filter:
+ filter = "&".join(filter)
+ resp = ctx.obj.paas.list(filter)
+ if literal:
+ print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
+ return
+
+ table = _get_paas_table_header(long)
+ project_list = ctx.obj.project.list()
+ for paas in resp:
+ logger.debug("PaaS details: {}".format(yaml.safe_dump(paas)))
+ if long:
+ _add_paas_long_row(table, paas, project_list)
+ else:
+ _add_paas_row(table, paas)
+ table.align = "l"
+ print(table)
+
+
+def _get_paas_table_header(long):
+ if long:
+ return PrettyTable(
+ ["Name", "Id", "Project", "Operational State", "Detailed Status"]
+ )
+ return PrettyTable(["Name", "Id", "Operational State"])
+
+
+def _add_paas_long_row(table, paas, project_list):
+ _, project_name = get_project(project_list, paas)
+ detailed_status = paas.get("_admin", {}).get("detailed-status", "-")
+ table.add_row(
+ [
+ paas["name"],
+ paas["_id"],
+ project_name,
+ paas.get("_admin", {}).get("operationalState", "-"),
+ wrap_text(text=detailed_status, width=40),
+ ]
+ )
+
+
+def _add_paas_row(table, paas):
+ table.add_row(
+ [paas["name"], paas["_id"], paas.get("_admin", {}).get("operationalState", "-")]
+ )
+
+
+@cli_osm.command(name="paas-show", short_help="Shows the details of a PaaS")
+@click.argument("name")
+@click.option("--literal", is_flag=True, help="Print literally, no pretty table")
+@click.pass_context
+def paas_show(ctx, name, literal):
+ """Shows the details of a PaaS.
+
+ Args:
+ name (str): Name or ID of the PaaS to show.
+ """
+ resp = ctx.obj.paas.get(name)
+ if literal:
+ print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
+ return
+ table = PrettyTable(["key", "attribute"])
+ for k, v in list(resp.items()):
+ table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
+ table.align = "l"
+ print(table)
+
+
###########################
# Repo operations
###########################
NS_NAME: Network service instance name or ID.
"""
- op_data = {
- "timeout": timeout,
- "updateType": updatetype,
- }
+ op_data = {"timeout": timeout, "updateType": updatetype}
if config:
op_data["config"] = yaml.safe_load(config)
@cli_osm.command(
name="ns-heal",
short_help="heals (recreates) VNFs or VDUs of a NS instance",
- context_settings=dict(
- ignore_unknown_options=True,
- ),
+ context_settings=dict(ignore_unknown_options=True),
)
@click.argument("ns_name")
@click.argument(
- "args",
- nargs=-1,
- type=click.UNPROCESSED,
- callback=process_ns_heal_params,
+ "args", nargs=-1, type=click.UNPROCESSED, callback=process_ns_heal_params
)
@click.option("--timeout", type=int, default=None, help="timeout in seconds")
@click.option(
@cli_osm.command(
name="vnf-heal",
short_help="heals (recreates) a VNF instance or the VDUs of a VNF instance",
- context_settings=dict(
- ignore_unknown_options=True,
- ),
+ context_settings=dict(ignore_unknown_options=True),
)
@click.argument("vnf_name")
@click.argument(
- "args",
- nargs=-1,
- type=click.UNPROCESSED,
- callback=process_vnf_heal_params,
+ "args", nargs=-1, type=click.UNPROCESSED, callback=process_vnf_heal_params
)
@click.option("--timeout", type=int, default=None, help="timeout in seconds")
@click.option(
help="do not return the control immediately, but keep it until the operation is completed, or timeout",
)
@click.pass_context
-def vnf_heal2(
- ctx,
- vnf_name,
- args,
- heal_params,
- timeout,
- wait,
-):
+def vnf_heal2(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
print(
'Maybe "--hostname" option or OSM_HOSTNAME environment variable needs to be specified'
)
- except ClientException as exc:
+ except (ClientException, NotFound) as exc:
print("ERROR: {}".format(exc))
except (FileNotFoundError, PermissionError) as exc:
print("Cannot open file: {}".format(exc))
--- /dev/null
+# Copyright 2021 Canonical Ltd.
+#
+# 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 json
+import unittest
+from unittest.mock import Mock, patch
+import yaml
+
+from click.testing import CliRunner
+from osmclient.common.exceptions import NotFound
+from osmclient.scripts import osm
+
+
+@patch("builtins.print")
+@patch("osmclient.scripts.osm.PrettyTable")
+@patch("osmclient.scripts.osm.client.Client")
+@patch("osmclient.scripts.osm.check_client_version")
+@patch("osmclient.scripts.osm.get_project")
+class TestPaaS(unittest.TestCase):
+ def setUp(self):
+ self.runner = CliRunner()
+ self.ctx_obj = Mock()
+ self.table = Mock()
+ self.paas_data = {
+ "name": "name",
+ "_id": "1234",
+ "_admin": {"detailed-status": "status", "operationalState": "state"},
+ }
+
+ def test_paas_add(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ self.runner.invoke(
+ osm.cli_osm,
+ [
+ "paas-add",
+ "name",
+ "--paas_type",
+ "juju",
+ "--endpoints",
+ "1.2.3.4:17070",
+ "--user",
+ "user",
+ "--secret",
+ "secret",
+ "--description",
+ "description",
+ "--config",
+ json.dumps({"juju-https-proxy": "http://squid:3128"}),
+ ],
+ )
+ mock_check_client_version.assert_called()
+ self.ctx_obj.paas.create.assert_called_with(
+ {
+ "name": "name",
+ "paas_type": "juju",
+ "endpoints": ["1.2.3.4:17070"],
+ "user": "user",
+ "secret": "secret",
+ "description": "description",
+ "config": {"juju-https-proxy": "http://squid:3128"},
+ }
+ )
+ mock_pretty_table.assert_not_called()
+ self.table.add_row.assert_not_called()
+ mock_print.assert_not_called()
+
+ def test_paas_update(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ self.runner.invoke(osm.cli_osm, ["paas-update", "name"])
+ mock_check_client_version.assert_called()
+ self.ctx_obj.paas.update.assert_called_with("name", {})
+ mock_pretty_table.assert_not_called()
+ self.table.add_row.assert_not_called()
+ mock_print.assert_not_called()
+
+ def test_paas_update_with_args(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ self.runner.invoke(
+ osm.cli_osm,
+ [
+ "paas-update",
+ "name",
+ "--newname",
+ "paas_new_name",
+ "--paas_type",
+ "juju",
+ "--endpoints",
+ "1.2.3.4:17070",
+ "--user",
+ "user",
+ "--secret",
+ "secret",
+ "--description",
+ "description",
+ "--config",
+ json.dumps({"juju-https-proxy": "http://squid:3128"}),
+ ],
+ )
+ mock_check_client_version.assert_called()
+ self.ctx_obj.paas.update.assert_called_with(
+ "name",
+ {
+ "name": "paas_new_name",
+ "paas_type": "juju",
+ "endpoints": ["1.2.3.4:17070"],
+ "user": "user",
+ "secret": "secret",
+ "description": "description",
+ "config": {"juju-https-proxy": "http://squid:3128"},
+ },
+ )
+ mock_pretty_table.assert_not_called()
+ self.table.add_row.assert_not_called()
+ mock_print.assert_not_called()
+
+ def test_paas_delete(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ self.runner.invoke(osm.cli_osm, ["paas-delete", "name"])
+ mock_check_client_version.assert_called()
+ self.ctx_obj.paas.delete.assert_called_with("name", force=False)
+ mock_pretty_table.assert_not_called()
+ self.table.add_row.assert_not_called()
+ mock_print.assert_not_called()
+
+ def test_paas_delete_force(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ self.runner.invoke(osm.cli_osm, ["paas-delete", "name", "--force"])
+ mock_check_client_version.assert_called()
+ self.ctx_obj.paas.delete.assert_called_with("name", force=True)
+ mock_pretty_table.assert_not_called()
+ self.table.add_row.assert_not_called()
+ mock_print.assert_not_called()
+
+ def test_paas_list(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ mock_pretty_table.return_value = self.table
+
+ self.ctx_obj.paas.list.return_value = [self.paas_data]
+ self.runner.invoke(osm.cli_osm, ["paas-list", "--filter", "somefilter"])
+ mock_check_client_version.assert_called()
+ self.ctx_obj.paas.list.assert_called_with("somefilter")
+ mock_pretty_table.assert_called_with(["Name", "Id", "Operational State"])
+ mock_get_project.assert_not_called()
+ self.table.add_row.assert_called_with(["name", "1234", "state"])
+ mock_print.assert_called_with(self.table)
+
+ def test_paas_list_long(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ mock_pretty_table.return_value = self.table
+ mock_get_project.return_value = ("5678", "project_name")
+ self.ctx_obj.paas.list.return_value = [self.paas_data]
+ self.runner.invoke(
+ osm.cli_osm, ["paas-list", "--filter", "somefilter", "--long"]
+ )
+ mock_check_client_version.assert_called()
+ self.ctx_obj.paas.list.assert_called_with("somefilter")
+ mock_pretty_table.assert_called_with(
+ ["Name", "Id", "Project", "Operational State", "Detailed Status"]
+ )
+ self.table.add_row.assert_called_with(
+ ["name", "1234", "project_name", "state", "status"]
+ )
+ mock_print.assert_called_with(self.table)
+
+ def test_paas_list_literal(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ self.ctx_obj.paas.list.return_value = [self.paas_data]
+ self.runner.invoke(osm.cli_osm, ["paas-list", "--literal"])
+ mock_check_client_version.assert_called()
+ self.ctx_obj.paas.list.assert_called()
+ mock_pretty_table.assert_not_called()
+ self.table.add_row.assert_not_called()
+ mock_print.assert_called_with(
+ yaml.safe_dump([self.paas_data], indent=4, default_flow_style=False)
+ )
+
+ def test_paas_list_empty(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ mock_pretty_table.return_value = self.table
+ self.ctx_obj.paas.list.return_value = []
+ self.runner.invoke(osm.cli_osm, ["paas-list", "--filter", "somefilter"])
+ mock_check_client_version.assert_called()
+ self.ctx_obj.paas.list.assert_called_with("somefilter")
+ mock_get_project.assert_not_called()
+ mock_pretty_table.assert_called_with(["Name", "Id", "Operational State"])
+ mock_print.assert_called_with(self.table)
+
+ def test_paas_show(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ mock_pretty_table.return_value = self.table
+ self.ctx_obj.paas.get.return_value = self.paas_data
+ self.runner.invoke(osm.cli_osm, ["paas-show", "name"])
+ self.ctx_obj.paas.get.assert_called_with("name")
+ mock_pretty_table.assert_called_with(["key", "attribute"])
+ self.assertEqual(self.table.add_row.call_count, len(self.paas_data))
+ mock_print.assert_called_with(self.table)
+
+ def test_paas_show_literal(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ self.ctx_obj.paas.get.return_value = self.paas_data
+ self.runner.invoke(osm.cli_osm, ["paas-show", "name", "--literal"])
+ self.ctx_obj.paas.get.assert_called_with("name")
+ mock_pretty_table.assert_not_called()
+ self.table.add_row.assert_not_called()
+ mock_print.assert_called_with(
+ yaml.safe_dump(self.paas_data, indent=4, default_flow_style=False)
+ )
+
+ def test_paas_show_literal_throws_exception(
+ self,
+ mock_get_project,
+ mock_check_client_version,
+ mock_client,
+ mock_pretty_table,
+ mock_print,
+ ):
+ mock_client.return_value = self.ctx_obj
+ self.ctx_obj.paas.get.side_effect = NotFound()
+ self.runner.invoke(osm.cli_osm, ["paas-show", "name", "--literal"])
+ self.ctx_obj.paas.get.assert_called_with("name")
+ mock_pretty_table.assert_not_called()
+ self.table.add_row.assert_not_called()
+ mock_print.assert_not_called()
from osmclient.sol005 import pdud
from osmclient.sol005 import k8scluster
from osmclient.sol005 import vca
+from osmclient.sol005 import paas
from osmclient.sol005 import repo
from osmclient.sol005 import osmrepo
from osmclient.sol005 import subscription
self.pdu = pdud.Pdu(self._http_client, client=self)
self.k8scluster = k8scluster.K8scluster(self._http_client, client=self)
self.vca = vca.VCA(self._http_client, client=self)
+ self.paas = paas.PAAS(self._http_client, client=self)
self.repo = repo.Repo(self._http_client, client=self)
self.osmrepo = osmrepo.OSMRepo(self._http_client, client=self)
self.package_tool = package_tool.PackageTool(client=self)
--- /dev/null
+# Copyright 2021 Canonical Ltd.
+#
+# 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 PaaS API handling
+"""
+
+from osmclient.common import utils
+from osmclient.common.exceptions import ClientException, NotFound
+import json
+
+
+class PAAS(object):
+ def __init__(self, http=None, client=None):
+ self._http = http
+ self._client = client
+ self._apiName = "/admin"
+ self._apiVersion = "/v1"
+ self._apiResource = "/paas"
+ self._apiBase = "{}{}{}".format(
+ self._apiName, self._apiVersion, self._apiResource
+ )
+
+ def _is_id(self, name):
+ return utils.validate_uuid4(name)
+
+ def create(self, paas):
+ """Create PaaS.
+ Args:
+ paas (dict): includes the PaaS information.
+
+ Raises:
+ ClientException
+ """
+ self._client.get_token()
+ http_code, resp = self._http.post_cmd(
+ endpoint=self._apiBase, postfields_dict=paas
+ )
+ resp = json.loads(resp) if resp else {}
+ if "id" not in resp:
+ raise ClientException("unexpected response from server - {}".format(resp))
+ print("PaaS {} created with ID {}".format(paas["name"], resp["id"]))
+
+ def update(self, name, paas):
+ """Updates a PaaS based on name or ID.
+ Args:
+ name (str): PaaS name or ID.
+ paas (dict): includes the new PaaS information to update.
+
+ Raises:
+ NotFound: if PaaS does not exists in DB.
+ """
+ paas_id = name
+ self._client.get_token()
+ try:
+ if not self._is_id(name):
+ paas_id = self.get(name)["_id"]
+ self._http.patch_cmd(
+ endpoint="{}/{}".format(self._apiBase, paas_id), postfields_dict=paas
+ )
+ except NotFound:
+ raise NotFound("PaaS {} not found".format(name))
+
+ def get_id(self, name):
+ """Returns a PaaS ID from a PaaS name.
+ Args:
+ name (str): PaaS name.
+ Raises:
+ NotFound: if PaaS does not exists in DB.
+ """
+ for paas in self.list():
+ if name == paas["name"]:
+ return paas["_id"]
+ raise NotFound("PaaS {} not found".format(name))
+
+ def delete(self, name, force=False):
+ """Deletes a PaaS based on name or ID.
+ Args:
+ name (str): PaaS name or ID.
+ force (bool): if True, PaaS is deleted without any check.
+ Raises:
+ NotFound: if PaaS does not exists in DB.
+ ClientException: if delete fails.
+ """
+ self._client.get_token()
+ paas_id = name
+ querystring = "?FORCE=True" if force else ""
+ try:
+ if not self._is_id(name):
+ paas_id = self.get_id(name)
+ http_code, resp = self._http.delete_cmd(
+ "{}/{}{}".format(self._apiBase, paas_id, querystring)
+ )
+ except NotFound:
+ raise NotFound("PaaS {} not found".format(name))
+ if http_code == 202:
+ print("Deletion in progress")
+ elif http_code == 204:
+ print("Deleted")
+ else:
+ msg = resp or ""
+ raise ClientException("Failed to delete PaaS {} - {}".format(name, msg))
+
+ def list(self, cmd_filter=None):
+ """Returns a list of PaaS"""
+ self._client.get_token()
+ filter_string = ""
+ if cmd_filter:
+ filter_string = "?{}".format(cmd_filter)
+ _, resp = self._http.get2_cmd("{}{}".format(self._apiBase, filter_string))
+ if resp:
+ return json.loads(resp)
+ return list()
+
+ def get(self, name):
+ """Returns a PaaS based on name or id.
+ Args:
+ name (str): PaaS name or ID.
+
+ Raises:
+ NotFound: if PaaS does not exists in DB.
+ ClientException: if get fails.
+ """
+ self._client.get_token()
+ paas_id = name
+ try:
+ if not self._is_id(name):
+ paas_id = self.get_id(name)
+ _, resp = self._http.get2_cmd("{}/{}".format(self._apiBase, paas_id))
+ resp = json.loads(resp) if resp else {}
+ if "_id" not in resp:
+ raise ClientException("Failed to get PaaS info: {}".format(resp))
+ return resp
+ except NotFound:
+ raise NotFound("PaaS {} not found".format(name))
--- /dev/null
+# Copyright 2021 Canonical Ltd.
+#
+# 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 json
+import unittest
+from unittest.mock import Mock, patch
+
+from osmclient.common.exceptions import ClientException, NotFound
+from osmclient.sol005.paas import PAAS
+
+
+class TestPaaS(unittest.TestCase):
+ def setUp(self):
+ self.paas = PAAS(Mock(), Mock())
+ self.paas_data = {
+ "name": "name",
+ "type": "juju",
+ "endpoints": ["127.0.0.1:17070"],
+ "user": "user",
+ "secret": "secret",
+ "description": "description",
+ "config": {},
+ }
+
+ @patch("builtins.print")
+ def test_create_success(self, mock_print):
+ self.paas._http.post_cmd.return_value = (200, '{"id": "1234"}')
+ self.paas.create(self.paas_data)
+ self.paas._client.get_token.assert_called()
+ self.paas._http.post_cmd.assert_called_with(
+ endpoint="/admin/v1/paas", postfields_dict=self.paas_data
+ )
+ mock_print.assert_called_with("PaaS name created with ID 1234")
+
+ @patch("builtins.print")
+ def test_create_raise_exception(self, mock_print):
+ self.paas._http.post_cmd.return_value = (404, None)
+ with self.assertRaises(ClientException):
+ self.paas.create(self.paas_data)
+ self.paas._client.get_token.assert_called()
+ self.paas._http.post_cmd.assert_called_with(
+ endpoint="/admin/v1/paas", postfields_dict=self.paas_data
+ )
+ mock_print.assert_not_called()
+
+ @patch("osmclient.sol005.paas.utils")
+ def test_update_success(self, mock_utils):
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get = Mock()
+ self.paas.get.return_value = {"_id": "1234"}
+ self.paas.update("paas_name", self.paas_data)
+ self.paas._http.patch_cmd.assert_called_with(
+ endpoint="/admin/v1/paas/1234", postfields_dict=self.paas_data
+ )
+
+ @patch("osmclient.sol005.paas.utils")
+ def test_update_fail_not_found_exception(self, mock_utils):
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get = Mock()
+ self.paas.get.side_effect = NotFound()
+ with self.assertRaises(NotFound):
+ self.paas.update("paas_name", self.paas_data)
+ self.paas._http.patch_cmd.assert_not_called()
+
+ @patch("osmclient.sol005.paas.utils")
+ def test_update_fail_client_exception(self, mock_utils):
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get = Mock()
+ self.paas.get.side_effect = ClientException()
+ with self.assertRaises(ClientException):
+ self.paas.update("paas_name", self.paas_data)
+ self.paas._http.patch_cmd.assert_not_called()
+
+ @patch("osmclient.sol005.paas.utils")
+ def test_update_using_id(self, mock_utils):
+ mock_utils.validate_uuid4.return_value = True
+ self.paas.get = Mock()
+ self.paas.update("1234", self.paas_data)
+ self.paas._http.patch_cmd.assert_called_with(
+ endpoint="/admin/v1/paas/1234", postfields_dict=self.paas_data
+ )
+ self.paas.get.assert_not_called()
+
+ def test_get_id_sucess(self):
+ self.paas_data.update({"_id": "1234"})
+ self.paas.list = Mock()
+ self.paas.list.return_value = [self.paas_data]
+ paas_id = self.paas.get_id("name")
+ self.assertEqual(paas_id, "1234")
+
+ def test_get_id_not_found(self):
+ self.paas.list = Mock()
+ self.paas.list.return_value = []
+ with self.assertRaises(NotFound):
+ self.paas.get_id("name")
+
+ @patch("osmclient.sol005.paas.utils")
+ @patch("builtins.print")
+ def test_delete_success_202(self, mock_print, mock_utils):
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get_id = Mock()
+ self.paas.get_id.return_value = "1234"
+ self.paas._http.delete_cmd.return_value = (202, None)
+ self.paas.delete("paas_name")
+ self.paas._client.get_token.assert_called()
+ self.paas._http.delete_cmd.assert_called_with("/admin/v1/paas/1234")
+ mock_print.assert_called_with("Deletion in progress")
+
+ @patch("osmclient.sol005.paas.utils")
+ @patch("builtins.print")
+ def test_delete_success_204(self, mock_print, mock_utils):
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get_id = Mock()
+ self.paas.get_id.return_value = "1234"
+ self.paas._http.delete_cmd.return_value = (204, None)
+ self.paas.delete("paas_name", force=True)
+ self.paas._client.get_token.assert_called()
+ self.paas._http.delete_cmd.assert_called_with("/admin/v1/paas/1234?FORCE=True")
+ mock_print.assert_called_with("Deleted")
+
+ @patch("osmclient.sol005.paas.utils")
+ @patch("builtins.print")
+ def test_delete_fail_404(self, mock_print, mock_utils):
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get_id = Mock()
+ self.paas.get_id.return_value = "1234"
+ self.paas._http.delete_cmd.return_value = (404, "Not found")
+ with self.assertRaises(ClientException):
+ self.paas.delete("paas_name")
+ self.paas._client.get_token.assert_called()
+ self.paas._http.delete_cmd.assert_called_with("/admin/v1/paas/1234")
+ mock_print.assert_not_called()
+
+ @patch("osmclient.sol005.paas.utils")
+ @patch("builtins.print")
+ def test_delete_failed_id_not_found(self, mock_print, mock_utils):
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get_id = Mock()
+ self.paas.get_id.side_effect = NotFound()
+ self.paas._http.delete_cmd.return_value = (204, None)
+ with self.assertRaises(NotFound):
+ self.paas.delete("paas_name", force=True)
+ self.paas._client.get_token.assert_called()
+ self.paas._http.delete_cmd.assert_not_called()
+ mock_print.assert_not_called()
+
+ @patch("osmclient.sol005.paas.utils")
+ @patch("builtins.print")
+ def test_delete_using_id(self, mock_print, mock_utils):
+ mock_utils.validate_uuid4.return_value = True
+ self.paas.get_id = Mock()
+ self.paas._http.delete_cmd.return_value = (202, None)
+ paas_id = "1234"
+ self.paas.delete(paas_id)
+ self.paas.get_id.assert_not_called()
+ self.paas._client.get_token.assert_called()
+ self.paas._http.delete_cmd.assert_called_with("/admin/v1/paas/1234")
+ mock_print.assert_called_with("Deletion in progress")
+
+ @patch("osmclient.sol005.paas.utils")
+ @patch("builtins.print")
+ def test_delete_using_id_client_exception(self, mock_print, mock_utils):
+ mock_utils.validate_uuid4.return_value = True
+ self.paas.get_id = Mock()
+ self.paas._http.delete_cmd.return_value = (5, None)
+ paas_id = "1234"
+ with self.assertRaises(ClientException):
+ self.paas.delete(paas_id, force=True)
+ self.paas._client.get_token.assert_called()
+ self.paas.get_id.assert_not_called()
+ self.paas._http.delete_cmd.assert_called_with("/admin/v1/paas/1234?FORCE=True")
+ mock_print.assert_not_called()
+
+ @patch("osmclient.sol005.paas.utils")
+ @patch("builtins.print")
+ def test_delete_using_id_not_found(self, mock_print, mock_utils):
+ mock_utils.validate_uuid4.return_value = True
+ self.paas.get_id = Mock()
+ self.paas._http.delete_cmd.side_effect = NotFound()
+ paas_id = "1234"
+ with self.assertRaises(NotFound):
+ self.paas.delete(paas_id, force=True)
+ self.paas._client.get_token.assert_called()
+ self.paas.get_id.assert_not_called()
+ self.paas._http.delete_cmd.assert_called_with("/admin/v1/paas/1234?FORCE=True")
+ mock_print.assert_not_called()
+
+ def test_list_success(self):
+ self.paas._http.get2_cmd.return_value = (None, '[{"_id": "1234"}]')
+ paas_list = self.paas.list("my_filter")
+ self.paas._client.get_token.assert_called()
+ self.paas._http.get2_cmd.assert_called_with("/admin/v1/paas?my_filter")
+ self.assertEqual(paas_list, [{"_id": "1234"}])
+
+ def test_list_no_response(self):
+ self.paas._http.get2_cmd.return_value = (None, None)
+ paas_list = self.paas.list()
+ self.paas._client.get_token.assert_called()
+ self.paas._http.get2_cmd.assert_called_with("/admin/v1/paas")
+ self.assertEqual(paas_list, [])
+
+ @patch("osmclient.sol005.paas.utils")
+ def test_get_success(self, mock_utils):
+ self.paas_data.update({"_id": "1234"})
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get_id = Mock()
+ self.paas.get_id.return_value = "1234"
+ self.paas._http.get2_cmd.return_value = (0, json.dumps(self.paas_data))
+ paas = self.paas.get("paas_name")
+ self.paas._client.get_token.assert_called()
+ self.paas._http.get2_cmd.assert_called_with("/admin/v1/paas/1234")
+ self.assertEqual(paas, self.paas_data)
+
+ @patch("osmclient.sol005.paas.utils")
+ def test_get_client_exception(self, mock_utils):
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get_id = Mock()
+ self.paas.get_id.return_value = "1234"
+ self.paas._http.get2_cmd.return_value = (404, json.dumps({}))
+ with self.assertRaises(ClientException):
+ self.paas.get("paas_name")
+ self.paas._client.get_token.assert_called()
+ self.paas._http.get2_cmd.assert_called_with("/admin/v1/paas/1234")
+
+ @patch("osmclient.sol005.paas.utils")
+ def test_get_not_found_exception(self, mock_utils):
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get_id = Mock()
+ self.paas.get_id.return_value = "1234"
+ self.paas._http.get2_cmd.side_effect = NotFound()
+ with self.assertRaises(NotFound):
+ self.paas.get("paas_name")
+ self.paas._client.get_token.assert_called()
+ self.paas._http.get2_cmd.assert_called_with("/admin/v1/paas/1234")
+
+ @patch("osmclient.sol005.paas.utils")
+ def test_get_success_use_id(self, mock_utils):
+ self.paas_data.update({"_id": "1234"})
+ mock_utils.validate_uuid4.return_value = True
+ self.paas._http.get2_cmd.return_value = (0, json.dumps(self.paas_data))
+ paas = self.paas.get("1234")
+ self.paas._client.get_token.assert_called()
+ self.paas._http.get2_cmd.assert_called_with("/admin/v1/paas/1234")
+ self.assertEqual(paas, self.paas_data)
+
+ @patch("osmclient.sol005.paas.utils")
+ def test_get_with_get_id_exception(self, mock_utils):
+ self.paas_data.update({"_id": "1234"})
+ mock_utils.validate_uuid4.return_value = False
+ self.paas.get_id = Mock()
+ self.paas.get_id.side_effect = NotFound()
+ self.paas._http.get2_cmd.return_value = (404, json.dumps(self.paas_data))
+ with self.assertRaises(NotFound):
+ self.paas.get("paas_name")
+ self.paas._client.get_token.assert_called()
+ self.paas._http.get2_cmd.assert_not_called()