From 5432642135a085af59494ffd88c81ab79c4f90dc Mon Sep 17 00:00:00 2001 From: Teodora Mihoc Date: Wed, 18 Dec 2024 16:47:11 +0100 Subject: [PATCH 1/4] docs: move docs from juju.is --- build/lib/juju/__init__.py | 3 + build/lib/juju/access.py | 49 + build/lib/juju/action.py | 24 + build/lib/juju/annotation.py | 12 + build/lib/juju/annotationhelper.py | 38 + build/lib/juju/application.py | 1096 + build/lib/juju/bundle.py | 1197 + build/lib/juju/charm.py | 12 + build/lib/juju/charmhub.py | 181 + build/lib/juju/client/__init__.py | 3 + build/lib/juju/client/_client.py | 236 + build/lib/juju/client/_client1.py | 550 + build/lib/juju/client/_client10.py | 674 + build/lib/juju/client/_client11.py | 481 + build/lib/juju/client/_client12.py | 466 + build/lib/juju/client/_client17.py | 817 + build/lib/juju/client/_client19.py | 845 + build/lib/juju/client/_client2.py | 326 + build/lib/juju/client/_client20.py | 845 + build/lib/juju/client/_client3.py | 605 + build/lib/juju/client/_client4.py | 572 + build/lib/juju/client/_client5.py | 285 + build/lib/juju/client/_client6.py | 781 + build/lib/juju/client/_client7.py | 1014 + build/lib/juju/client/_client8.py | 68 + build/lib/juju/client/_client9.py | 347 + build/lib/juju/client/_definitions.py | 22723 ++++++++++++++++ build/lib/juju/client/client.py | 35 + build/lib/juju/client/codegen.py | 53 + build/lib/juju/client/connection.py | 924 + build/lib/juju/client/connector.py | 232 + build/lib/juju/client/facade.py | 989 + build/lib/juju/client/facade_versions.py | 139 + build/lib/juju/client/flags.py | 17 + build/lib/juju/client/gocookies.py | 108 + build/lib/juju/client/jujudata.py | 190 + build/lib/juju/client/overrides.py | 418 + build/lib/juju/client/proxy/__init__.py | 3 + build/lib/juju/client/proxy/factory.py | 29 + .../juju/client/proxy/kubernetes/__init__.py | 3 + .../lib/juju/client/proxy/kubernetes/proxy.py | 83 + build/lib/juju/client/proxy/proxy.py | 26 + build/lib/juju/client/runner.py | 25 + build/lib/juju/constraints.py | 254 + build/lib/juju/controller.py | 1009 + build/lib/juju/delta.py | 140 + build/lib/juju/errors.py | 156 + build/lib/juju/exceptions.py | 6 + build/lib/juju/jasyncio.py | 174 + build/lib/juju/juju.py | 37 + build/lib/juju/loop.py | 12 + build/lib/juju/machine.py | 310 + build/lib/juju/model.py | 3359 +++ build/lib/juju/names.py | 93 + build/lib/juju/offerendpoints.py | 216 + build/lib/juju/origin.py | 212 + build/lib/juju/placement.py | 60 + build/lib/juju/provisioner.py | 344 + build/lib/juju/py.typed | 0 build/lib/juju/relation.py | 147 + build/lib/juju/remoteapplication.py | 38 + build/lib/juju/secrets.py | 124 + build/lib/juju/status.py | 252 + build/lib/juju/tag.py | 64 + build/lib/juju/unit.py | 446 + build/lib/juju/url.py | 188 + build/lib/juju/user.py | 183 + build/lib/juju/utils.py | 602 + build/lib/juju/version.py | 9 + docs/conf.py | 1 + docs/howto/#manage-machines.rst# | 161 + docs/howto/index.rst | 27 + docs/howto/manage-actions.rst | 43 + docs/howto/manage-applications.rst | 241 + docs/howto/manage-charm-resources.rst | 52 + docs/howto/manage-charms.rst | 108 + docs/howto/manage-clouds.rst | 78 + docs/howto/manage-controllers.rst | 52 + docs/howto/manage-credentials.rst | 22 + docs/howto/manage-machines.rst | 161 + docs/howto/manage-models.rst | 89 + docs/howto/manage-offers.rst | 90 + docs/howto/manage-python-libjuju.rst | 73 + docs/howto/manage-relations.rst | 59 + docs/howto/manage-secret-backends.rst | 62 + docs/howto/manage-secrets.rst | 80 + docs/howto/manage-spaces.rst | 36 + docs/howto/manage-ssh-keys.rst | 46 + docs/howto/manage-storage-pools.rst | 56 + docs/howto/manage-storage.rst | 83 + docs/howto/manage-units.rst | 103 + docs/howto/manage-users.rst | 135 + docs/index.rst | 8 + 93 files changed, 47825 insertions(+) create mode 100644 build/lib/juju/__init__.py create mode 100644 build/lib/juju/access.py create mode 100644 build/lib/juju/action.py create mode 100644 build/lib/juju/annotation.py create mode 100644 build/lib/juju/annotationhelper.py create mode 100644 build/lib/juju/application.py create mode 100644 build/lib/juju/bundle.py create mode 100644 build/lib/juju/charm.py create mode 100644 build/lib/juju/charmhub.py create mode 100644 build/lib/juju/client/__init__.py create mode 100644 build/lib/juju/client/_client.py create mode 100644 build/lib/juju/client/_client1.py create mode 100644 build/lib/juju/client/_client10.py create mode 100644 build/lib/juju/client/_client11.py create mode 100644 build/lib/juju/client/_client12.py create mode 100644 build/lib/juju/client/_client17.py create mode 100644 build/lib/juju/client/_client19.py create mode 100644 build/lib/juju/client/_client2.py create mode 100644 build/lib/juju/client/_client20.py create mode 100644 build/lib/juju/client/_client3.py create mode 100644 build/lib/juju/client/_client4.py create mode 100644 build/lib/juju/client/_client5.py create mode 100644 build/lib/juju/client/_client6.py create mode 100644 build/lib/juju/client/_client7.py create mode 100644 build/lib/juju/client/_client8.py create mode 100644 build/lib/juju/client/_client9.py create mode 100644 build/lib/juju/client/_definitions.py create mode 100644 build/lib/juju/client/client.py create mode 100644 build/lib/juju/client/codegen.py create mode 100644 build/lib/juju/client/connection.py create mode 100644 build/lib/juju/client/connector.py create mode 100644 build/lib/juju/client/facade.py create mode 100644 build/lib/juju/client/facade_versions.py create mode 100644 build/lib/juju/client/flags.py create mode 100644 build/lib/juju/client/gocookies.py create mode 100644 build/lib/juju/client/jujudata.py create mode 100644 build/lib/juju/client/overrides.py create mode 100644 build/lib/juju/client/proxy/__init__.py create mode 100644 build/lib/juju/client/proxy/factory.py create mode 100644 build/lib/juju/client/proxy/kubernetes/__init__.py create mode 100644 build/lib/juju/client/proxy/kubernetes/proxy.py create mode 100644 build/lib/juju/client/proxy/proxy.py create mode 100644 build/lib/juju/client/runner.py create mode 100644 build/lib/juju/constraints.py create mode 100644 build/lib/juju/controller.py create mode 100644 build/lib/juju/delta.py create mode 100644 build/lib/juju/errors.py create mode 100644 build/lib/juju/exceptions.py create mode 100644 build/lib/juju/jasyncio.py create mode 100644 build/lib/juju/juju.py create mode 100644 build/lib/juju/loop.py create mode 100644 build/lib/juju/machine.py create mode 100644 build/lib/juju/model.py create mode 100644 build/lib/juju/names.py create mode 100644 build/lib/juju/offerendpoints.py create mode 100644 build/lib/juju/origin.py create mode 100644 build/lib/juju/placement.py create mode 100644 build/lib/juju/provisioner.py create mode 100644 build/lib/juju/py.typed create mode 100644 build/lib/juju/relation.py create mode 100644 build/lib/juju/remoteapplication.py create mode 100644 build/lib/juju/secrets.py create mode 100644 build/lib/juju/status.py create mode 100644 build/lib/juju/tag.py create mode 100644 build/lib/juju/unit.py create mode 100644 build/lib/juju/url.py create mode 100644 build/lib/juju/user.py create mode 100644 build/lib/juju/utils.py create mode 100644 build/lib/juju/version.py create mode 100644 docs/howto/#manage-machines.rst# create mode 100644 docs/howto/index.rst create mode 100644 docs/howto/manage-actions.rst create mode 100644 docs/howto/manage-applications.rst create mode 100644 docs/howto/manage-charm-resources.rst create mode 100644 docs/howto/manage-charms.rst create mode 100644 docs/howto/manage-clouds.rst create mode 100644 docs/howto/manage-controllers.rst create mode 100644 docs/howto/manage-credentials.rst create mode 100644 docs/howto/manage-machines.rst create mode 100644 docs/howto/manage-models.rst create mode 100644 docs/howto/manage-offers.rst create mode 100644 docs/howto/manage-python-libjuju.rst create mode 100644 docs/howto/manage-relations.rst create mode 100644 docs/howto/manage-secret-backends.rst create mode 100644 docs/howto/manage-secrets.rst create mode 100644 docs/howto/manage-spaces.rst create mode 100644 docs/howto/manage-ssh-keys.rst create mode 100644 docs/howto/manage-storage-pools.rst create mode 100644 docs/howto/manage-storage.rst create mode 100644 docs/howto/manage-units.rst create mode 100644 docs/howto/manage-users.rst diff --git a/build/lib/juju/__init__.py b/build/lib/juju/__init__.py new file mode 100644 index 00000000..9da2b17a --- /dev/null +++ b/build/lib/juju/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2024 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +"""Python Library for Juju.""" diff --git a/build/lib/juju/access.py b/build/lib/juju/access.py new file mode 100644 index 00000000..de3c8b7a --- /dev/null +++ b/build/lib/juju/access.py @@ -0,0 +1,49 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +from .errors import JujuNotValid + +# No permissions at all +NO_ACCESS = "" + +# Model permissions + +READ_ACCESS = "read" +WRITE_ACCESS = "write" +CONSUME_ACCESS = "consume" +ADMIN_ACCESS = "admin" +MODEL_ACCESS_LEVELS = {READ_ACCESS, WRITE_ACCESS, CONSUME_ACCESS, ADMIN_ACCESS} + +# Controller permissions + +LOGIN_ACCESS = "login" +ADD_MODEL_ACCESS = "add-model" +SUPERUSER_ACCESS = "superuser" +CONTROLLER_ACCESS_LEVELS = {LOGIN_ACCESS, ADD_MODEL_ACCESS, SUPERUSER_ACCESS} + +OFFER_ACCESS_LEVELS = {READ_ACCESS, CONSUME_ACCESS, ADMIN_ACCESS} + +ALL_ACCESS_LEVELS = MODEL_ACCESS_LEVELS.union(CONTROLLER_ACCESS_LEVELS) + + +def validate_access_level(access): + if access not in ALL_ACCESS_LEVELS: + raise JujuNotValid("access level", access) + + +def validate_offer_access(access): + validate_access_level() + if access not in OFFER_ACCESS_LEVELS: + raise JujuNotValid("offer access level", access) + + +def validate_model_access(access): + validate_access_level(access) + if access not in MODEL_ACCESS_LEVELS: + raise JujuNotValid("model access level", access) + + +def validate_controller_access(access): + validate_access_level(access) + if access not in CONTROLLER_ACCESS_LEVELS: + raise JujuNotValid("controller access level", access) diff --git a/build/lib/juju/action.py b/build/lib/juju/action.py new file mode 100644 index 00000000..7aa729d6 --- /dev/null +++ b/build/lib/juju/action.py @@ -0,0 +1,24 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +from . import model + + +class Action(model.ModelEntity): + def __init__(self, entity_id, model, history_index=-1, connected=True): + super().__init__(entity_id, model, history_index, connected) + self.results = {} + self._status = self.data["status"] + + @property + def status(self): + return self._status + + async def fetch_output(self): + completed_action = await self.model._get_completed_action(self.id) + self.results = completed_action.output or {} + self._status = completed_action.status + + async def wait(self): + self.results or await self.fetch_output() + return self diff --git a/build/lib/juju/annotation.py b/build/lib/juju/annotation.py new file mode 100644 index 00000000..2d70eafa --- /dev/null +++ b/build/lib/juju/annotation.py @@ -0,0 +1,12 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import logging + +from . import model + +log = logging.getLogger(__name__) + + +class Annotation(model.ModelEntity): + pass diff --git a/build/lib/juju/annotationhelper.py b/build/lib/juju/annotationhelper.py new file mode 100644 index 00000000..d8369125 --- /dev/null +++ b/build/lib/juju/annotationhelper.py @@ -0,0 +1,38 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import logging + +from .client import client +from .errors import JujuError + +log = logging.getLogger(__name__) + + +async def _get_annotations(entity_tag, connection): + """Get annotations for the specified entity + + :return dict: The annotations for the entity + """ + facade = client.AnnotationsFacade.from_connection(connection) + result = (await facade.Get(entities=[{"tag": entity_tag}])).results[0] + if result.error is not None: + raise JujuError(result.error) + return result.annotations + + +async def _set_annotations(entity_tag, annotations, connection): + """Set annotations on the specified entity. + + :param annotations map[string]string: the annotations as key/value + pairs. + """ + # TODO: ensure annotations is dict with only string keys + # and values. + log.debug("Updating annotations on %s", entity_tag) + facade = client.AnnotationsFacade.from_connection(connection) + args = client.EntityAnnotations( + entity=entity_tag, + annotations=annotations, + ) + return await facade.Set(annotations=[args]) diff --git a/build/lib/juju/application.py b/build/lib/juju/application.py new file mode 100644 index 00000000..a57e336c --- /dev/null +++ b/build/lib/juju/application.py @@ -0,0 +1,1096 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import hashlib +import json +import logging +from pathlib import Path +from typing import Dict, List, Optional, Union + +from typing_extensions import deprecated + +from . import jasyncio, model, tag, utils +from .annotationhelper import _get_annotations, _set_annotations +from .bundle import get_charm_series, is_local_charm +from .client import _definitions, client +from .errors import JujuApplicationConfigError, JujuError +from .origin import Channel +from .placement import parse as parse_placement +from .relation import Relation +from .status import derive_status +from .url import URL +from .utils import block_until +from .version import DEFAULT_ARCHITECTURE + +log = logging.getLogger(__name__) + + +class Application(model.ModelEntity): + """Represents the current state of a deployed application. + + In the current library version, as well entire 2.x and 3.x series, + the data is supplied by Juju AllWatcher notifications, also known as deltas + for the specific application. The fields are declared here: + https://github.com/juju/juju/blob/be8a779/core/multiwatcher/types.go#L191-L209 + + The fields marked deprecated below will be removed in version 4.0 because + a different API must be used against Juju 4. + """ + + @property + def name(self) -> str: + return self.entity_id + + @property + def exposed(self) -> bool: + return self.safe_data["exposed"] + + @property + @deprecated("Application.owner_tag is deprecated and will be removed in v4") + def owner_tag(self) -> str: + return self.safe_data["owner-tag"] + + @property + def life(self) -> str: + return self.safe_data["life"] + + @property + @deprecated("Application.min_units is deprecated and will be removed in v4") + def min_units(self) -> int: + return self.safe_data["min-units"] + + @property + def constraints(self) -> Dict[str, Union[str, int, bool]]: + return self.safe_data["constraints"] + + @property + @deprecated("Application.subordinate is deprecated and will be removed in v4") + def subordinate(self) -> bool: + return self.safe_data["subordinate"] + + @property + @deprecated( + "Application.workload_version is deprecated and will be removed in v4, use Unit.workload_version instead." + ) + def workload_version(self) -> str: + return self.safe_data["workload-version"] + + @property + def _unit_match_pattern(self): + return rf"^{self.entity_id}.*$" + + def _facade(self): + return client.ApplicationFacade.from_connection(self.connection) + + def _facade_version(self): + return client.ApplicationFacade.best_facade_version(self.connection) + + def on_unit_add(self, callable_): + """Add a "unit added" observer to this entity, which will be called + whenever a unit is added to this application. + + """ + self.model.add_observer(callable_, "unit", "add", self._unit_match_pattern) + + def on_unit_remove(self, callable_): + """Add a "unit removed" observer to this entity, which will be called + whenever a unit is removed from this application. + + """ + self.model.add_observer(callable_, "unit", "remove", self._unit_match_pattern) + + @property + def units(self): + return [ + unit for unit in self.model.units.values() if unit.application == self.name + ] + + @property + def subordinate_units(self): + """Returns the subordinate units of this application""" + return [u for u in self.units if u.is_subordinate] + + @property + def relations(self) -> List[Relation]: + return [rel for rel in self.model.relations if rel.matches(self.name)] + + def related_applications(self, endpoint_name=None): + apps = {} + for rel in self.relations: + if rel.is_peer: + local_ep, remote_ep = rel.endpoints + else: + + def is_us(ep): + return ep.application.name == self.name + + local_ep, remote_ep = sorted(rel.endpoints, key=is_us) + if endpoint_name is not None and endpoint_name != local_ep.name: + continue + apps[remote_ep.application.name] = remote_ep.application + return apps + + @property + def status(self): + """Get the application status. + + If the application is unknown it will attempt to derive the unit + workload status and highlight the most relevant (severity). + """ + status = self.safe_data["status"]["current"] + if status == "unset": + known_statuses = [unit.workload_status for unit in self.units] + # If the self.get_status() is called (i.e. the status + # is received by FullStatus from the API) then add + # that into this computation as it might be more up + # to date (and more severe). + known_statuses.append(self._status) + return derive_status(known_statuses) + return status + + @property + def status_message(self): + """Get the application status message, as set by the charm's leader.""" + return self.safe_data["status"]["message"] + + @property + def tag(self): + return tag.application(self.name) + + async def add_relation(self, local_relation, remote_relation): + """.. deprecated:: 2.9.9 + Use ``relate()`` instead + """ + return await self.relate(local_relation, remote_relation) + + async def relate(self, local_relation, remote_relation): + """Add a relation to another application. + + :param str local_relation: Name of relation on this application + :param str remote_relation: Name of relation on the other + application in the form '[:]' + + """ + if ":" not in local_relation: + local_relation = f"{self.name}:{local_relation}" + + return await self.model.relate(local_relation, remote_relation) + + async def add_unit(self, count=1, to=None, attach_storage=[]): + """Add one or more units to this application. + + :param int count: Number of units to add + :param [str] attach_storage: Existing storage to attach to the deployed unit + (not available on k8s models) + :param str to: Placement directive, e.g.:: + '23' - machine 23 + 'lxc:7' - new lxc container on machine 7 + '24/lxc/3' - lxc container 3 or machine 24 + + If None, a new machine is provisioned. + + """ + if self.model.info.type_ == "caas": + log.warning( + "adding units to a container-based model not supported, auto-switching to scale" + ) + return await self.scale(scale_change=count) + + app_facade = self._facade() + + log.debug("Adding %s unit%s to %s", count, "" if count == 1 else "s", self.name) + + result = await app_facade.AddUnits( + application=self.name, + placement=parse_placement(to) if to else None, + num_units=count, + attach_storage=attach_storage, + ) + + return await jasyncio.gather(*[ + jasyncio.ensure_future(self.model._wait_for_new("unit", unit_id)) + for unit_id in result.units + ]) + + add_units = add_unit + + async def scale(self, scale=None, scale_change=None): + """Set or adjust the scale of this (K8s) application. + + One or the other of scale or scale_change must be provided. + + :param int scale: Scale to which to set this application. + :param int scale_change: Amount by which to adjust the scale of this + application (can be positive or negative). + """ + app_facade = self._facade() + + if (scale, scale_change) == (None, None): + raise ValueError("Must provide either scale or scale_change") + + log.debug( + "Scaling application %s %s %s", + self.name, + "to" if scale else "by", + scale or scale_change, + ) + + await app_facade.ScaleApplications( + applications=[ + client.ScaleApplicationParams( + application_tag=self.tag, scale=scale, scale_change=scale_change + ) + ] + ) + + async def destroy_relation( + self, local_relation, remote_relation, block_until_done: bool = False + ): + """Remove a relation to another application. + + :param str local_relation: Name of relation on this application + :param str remote_relation: Name of relation on the other + application in the form '[:]' + :param bool block_until_done: Wait until the relation is completely removed. + + """ + if ":" not in local_relation: + local_relation = f"{self.name}:{local_relation}" + + app_facade = self._facade() + + log.debug("Destroying relation %s <-> %s", local_relation, remote_relation) + + await app_facade.DestroyRelation(endpoints=[local_relation, remote_relation]) + if block_until_done: + await block_until( + lambda: not any( + relation.matches(local_relation, remote_relation) + for relation in self.relations + ) + ) + + remove_relation = destroy_relation + + async def destroy_unit(self, *unit_names): + """Destroy units by name.""" + return await self.model.destroy_units(*unit_names) + + destroy_units = destroy_unit + + async def destroy(self, destroy_storage=False, force=False, no_wait=False): + """Remove this application from the model. + + :param bool destroy_storage: Destroy storage attached to application unit. (=false) + :param bool force: Completely remove an application and all its dependencies. (=false) + :param bool no_wait: Rush through application removal without waiting for each individual step to complete (=false) + :param bool block: Blocks until the application is removed from the model + """ + if no_wait and not force: + raise JujuError("--no-wait without --force is not valid") + + app_facade = self._facade() + + log.debug( + f"Destroying {self.name} with parameters -- destroy-storage : {destroy_storage} -- force : {force} -- no-wait : {no_wait}" + ) + + res = await app_facade.DestroyApplication( + applications=[ + client.DestroyApplicationParams( + application_tag=self.tag, + destroy_storage=destroy_storage, + force=force, + max_wait=0 if no_wait else None, + ) + ] + ) + return res + + remove = destroy + + def supports_granular_expose_parameters(self): + """Returns true if the controller supports granular, per-endpoint + expose parameters. + """ + return self._facade_version() >= 13 + + async def expose(self, exposed_endpoints=None): + """Make a subset of the application endpoints or the entire application + available over the network. + + If the exposed_endpoints argument is not provided, all opened port + ranges for the application will become reachable from 0.0.0.0/0. + + On juju 2.9 and onwards, the exposed_endpoints argument may be used + to specify a list of spaces and or CIDRs that should be able to + reach the port ranges opened for a particular subnet. The + exposed_endpoints parameter is a map where keys are endpoint names + or the empty string ("") which works as a wildcard for all endpoints + and values are ExposedEndpoint instances. + + When targeting an older juju controller, the exposed_endpoints param + is not supported and an error will be raised if it is provided. + """ + app_facade = self._facade() + ctrl_supports_expose_parameters = self.supports_granular_expose_parameters() + + if exposed_endpoints is not None: + if not isinstance(exposed_endpoints, dict): + raise ValueError( + "endpoints must be a dictionary with ExposedEndpoint values" + ) + + # The bundle changes code will pass in raw dicts with the exposed + # endpoint data. We need to convert those into ExposedEndpoints + for k, v in exposed_endpoints.items(): + if not isinstance(v, ExposedEndpoint): + exposed_endpoints[k] = ExposedEndpoint.from_dict(v) + + # Check if the specified exposed_endpoints would cause security + # issues when applied to a pre 2.9 controller. + has_more_than_one_endpoints = len(exposed_endpoints) > 1 + has_non_wildcard_endpoint = ( + len(exposed_endpoints) > 0 and "" not in exposed_endpoints + ) + has_wildcard_endpoint_with_spaces_or_non_wildcard_cidrs = ( + "" in exposed_endpoints + and ( + exposed_endpoints[""].includes_non_wildcard_cidrs() + or exposed_endpoints[""].includes_spaces() + ) + ) + + is_security_risk = not ctrl_supports_expose_parameters and ( + has_more_than_one_endpoints + or has_non_wildcard_endpoint + or has_wildcard_endpoint_with_spaces_or_non_wildcard_cidrs + ) + + if is_security_risk: + raise JujuError( + "controller does not support granular expose parameters; applying this change would make all open application ports accessible from 0.0.0.0/0" + ) + + for endpoint, expose_details in exposed_endpoints.items(): + access_from = "from CIDRs 0.0.0.0/0 and ::/0" + if isinstance(expose_details, ExposedEndpoint): + access_from = str(expose_details) + + if endpoint == "": + log.debug( + "expose all endpoints of %s and allow access %s", + self.name, + access_from, + ) + else: + log.debug( + "override expose settings for endpoint %s of %s and %s", + endpoint, + self.name, + access_from, + ) + + # Map ExposedEndpoint entries to a dict we can pass to the facade. + exposed_endpoints = {k: v.to_dict() for k, v in exposed_endpoints.items()} + else: + log.debug( + "expose all endpoints of %s and allow access from CIDRs 0.0.0.0/0 and ::/0", + self.name, + ) + + if not ctrl_supports_expose_parameters: + return await app_facade.Expose(application=self.name) + + return await app_facade.Expose( + application=self.name, exposed_endpoints=exposed_endpoints + ) + + async def unexpose(self, exposed_endpoints=None): + """Prevent a subset of the application endpoints or the entire + application from being reached over the network. + + If the exposed_endpoints argument is not provided, the entire + application will be unexposed. + + On juju 2.9 and onwards, the exposed_endpoints argument may be used + to specify a list of endpoint names whose port ranges should be + unexposed. + + When targeting an older juju controller, the exposed_endpoints param + is not supported and an error will be raised if it is provided. + """ + app_facade = self._facade() + facade_version = self._facade_version() + + # Check if an endpoint list is provided + if exposed_endpoints is not None and len(exposed_endpoints) > 0: + if facade_version < 13: + raise JujuError( + "controller does not support granular expose parameters; applying this change would unexpose the application" + ) + + log.debug( + "Unexposing endpoints %s of %s", ",".join(exposed_endpoints), self.name + ) + return await app_facade.Unexpose( + application=self.name, exposed_endpoints=exposed_endpoints + ) + + # Just expose the entire application + log.debug("Unexposing %s", self.name) + return await app_facade.Unexpose(application=self.name) + + async def get_series(self): + """Return the series on which the application is deployed + + :return: str series + """ + app_facade = self._facade() + + log.debug("Getting series for %s", self.name) + + results = await app_facade.Get(application=self.name) + if self._facade_version() >= 15: + base_channel = results.base.channel + return utils.base_channel_to_series(base_channel) + return results.series + + async def get_config(self): + """Return the configuration settings dict for this application.""" + app_facade = self._facade() + + log.debug("Getting config for %s", self.name) + + return (await app_facade.Get(application=self.name)).config + + async def get_trusted(self): + """Return the trusted configuration setting for this application.""" + if self.model.info.agent_version < client.Number.from_json("2.4.0"): + raise NotImplementedError( + f"trusted is not supported on model version {self.model.info.agent_version}" + ) + + app_facade = self._facade() + + log.debug("Getting config for %s", self.name) + + config = await app_facade.Get(application=self.name) + if "trust" in config.config: + return config.config["trust"]["value"] is True + + app_config = config.application_config + return app_config["trust"]["value"] is True + + async def set_trusted(self, trust: bool): + """Set the trusted configuration of the application. + + :param bool trust: Trust the application or not + """ + if self.model.info.agent_version < client.Number.from_json("2.4.0"): + raise NotImplementedError( + f"trusted is not supported on model version {self.model.info.agent_version}" + ) + + # clamp trust to exactly the value juju expects, rather than allowing + # anything in the config. + app_facade = self._facade() + + config = {"trust": json.dumps(trust)} + log.debug("Setting config for %s: %s", self.name, config) + + # Unfortunately we have to do this in a lazy fashion, attempting to use + # the method early will cause an error. Attempting to call this + # dynamically causes issues with how the client code is wired up... we + # end up with a missing _toPy attr. + # Using a lambda to only throw it away when it's wrong seems a problem + # as well. + config_method = None + if self._facade_version() < 13: + config_method = app_facade.SetApplicationsConfig + else: + config_method = app_facade.SetConfigs + return await config_method( + args=[ + { + "application": self.name, + "config": config, + } + ] + ) + + async def get_constraints(self): + """Return the machine constraints dict for this application.""" + app_facade = self._facade() + + log.debug("Getting constraints for %s", self.name) + + result = (await app_facade.Get(application=self.name)).constraints + return vars(result) if result else result + + async def get_actions(self, schema=False): + """Get actions defined for this application. + + :param bool schema: Return the full action schema + :return dict: The charms actions, empty dict if none are defined. + """ + actions = {} + entity = {"tag": self.tag} + action_facade = client.ActionFacade.from_connection(self.connection) + results = ( + await action_facade.ApplicationsCharmsActions(entities=[entity]) + ).results + for result in results: + if result.application_tag == self.tag and result.actions: + actions = result.actions + break + if not schema: + actions = {k: v.description for k, v in actions.items()} + return actions + + async def get_status(self): + """Get the application status using info from the FullStatus + as well, because it might be more up to date than our model + + :return: str status + """ + client_facade = client.ClientFacade.from_connection(self.connection) + + full_status = await client_facade.FullStatus(patterns=None) + _app = full_status.applications.get(self.name, None) + if not _app: + raise JujuError(f"application is not in FullStatus : {self.name}") + + self._status = derive_status([self.status, _app.status.status]) + return self._status + + def attach_resource(self, resource_name, file_name, file_obj): + """Updates the resource for an application by uploading file from + local disk to the Juju controller. + + :param str resource_name: Name of the resource to be updated. + :param str file_name: Name of the local file to be uploaded. + :param TextIOWrapper file_obj: Actual object to be read for data. + """ + conn, headers, path_prefix = self.connection.https_connection() + + url = f"{path_prefix}/applications/{self.name}/resources/{resource_name}" + + data = file_obj.read() + + headers["Content-Type"] = "application/octet-stream" + headers["Content-Length"] = len(data) + data_bytes = data if isinstance(data, bytes) else bytes(data, "utf-8") + headers["Content-Sha384"] = hashlib.sha384(data_bytes).hexdigest() + + file_name = str(file_name) + if not file_name.startswith("./"): + file_name = "./" + file_name + + headers["Content-Disposition"] = f'form-data; filename="{file_name}"' + headers["Accept-Encoding"] = "gzip" + headers["Bakery-Protocol-Version"] = 3 + headers["Connection"] = "close" + + conn.request("PUT", url, data, headers) + response = conn.getresponse() + result = response.read().decode() + if not response.status == 200: + raise JujuError(result) + + async def get_resources(self): + """Return resources for this application. + + Returns a dict mapping resource name to + :class:`~juju._definitions.CharmResource` instances. + """ + facade = client.ResourcesFacade.from_connection(self.connection) + response = await facade.ListResources(entities=[client.Entity(self.tag)]) + + resources = dict() + for result in response.results: + for resource in result.charm_store_resources or []: + resources[resource.name] = resource + for resource in result.resources or []: + if resource.charmresource: + resource = resource.charmresource + resources[resource.name] = resource + return resources + + async def run(self, command, timeout=None): + """Run command on all units for this application. + + :param str command: The command to run + :param int timeout: Time to wait before command is considered failed + + """ + action = client.ActionFacade.from_connection(self.connection) + + log.debug("Running `%s` on all units of %s", command, self.name) + + # TODO this should return a list of Actions + return await action.Run( + applications=[self.name], + commands=command, + machines=[], + timeout=timeout, + units=[], + ) + + @property + def charm_name(self): + """Get the charm name of this application + + :return str: The name of the charm + """ + return URL.parse(self.charm_url).name + + @property + def charm_url(self): + """Get the charm url for this application + + :return str: The charm url + """ + return self.safe_data["charm-url"] + + async def get_annotations(self): + """Get annotations on this application. + + :return dict: The annotations for this application + """ + return await _get_annotations(self.tag, self.connection) + + async def set_annotations(self, annotations): + """Set annotations on this application. + + :param annotations map[string]string: the annotations as key/value + pairs. + + """ + return await _set_annotations(self.tag, annotations, self.connection) + + async def set_config(self, config): + """Set configuration options for this application. + + :param config: Dict of configuration to set + """ + app_facade = self._facade() + + log.debug("Setting config for %s: %s", self.name, config) + + str_config = {} + for k, v in config.items(): + if isinstance(v, str): + str_config[k] = v + elif isinstance(v, dict): + # pairs with a value of None are ignored + if v.get("value", False): + str_config[k] = str(v.get("value")) + else: + raise JujuApplicationConfigError(config, [k, v]) + + return await app_facade.SetConfigs( + args=[ + { + "application": self.name, + "config": str_config, + } + ] + ) + + async def reset_config(self, to_default): + """Restore application config to default values. + + :param list to_default: A list of config options to be reset to their + default value. + """ + app_facade = self._facade() + + log.debug("Restoring default config for %s: %s", self.name, to_default) + + return await app_facade.UnsetApplicationsConfig( + args=[ + { + "application": self.name, + "options": to_default, + } + ] + ) + + async def set_constraints(self, constraints): + """Set machine constraints for this application. + + :param dict constraints: Dict of machine constraints + + """ + app_facade = self._facade() + + log.debug("Setting constraints for %s: %s", self.name, constraints) + + return await app_facade.SetConstraints( + application=self.name, constraints=constraints + ) + + async def refresh( + self, + channel: Optional[str] = None, + force: bool = False, + force_series: bool = False, + force_units: bool = False, + path: Optional[Union[Path, str]] = None, + resources: Optional[Dict[str, str]] = None, + revision: Optional[int] = None, + switch: Optional[str] = None, + ): + """Refresh the charm for this application. + + :param str|None channel: Channel to use when getting the charm from the + charm store, e.g. 'development' + :param bool force_series: Refresh even if series of deployed + application is not supported by the new charm + :param bool force_units: Refresh all units immediately, even if in + error state + :param Path|str|None path: Refresh to a charm located at path + :param dict[str,str]|None resources: Dictionary of resource name/filepath pairs + :param int|None revision: Explicit refresh revision + :param str|None switch: URL of a different charm to cross-grade to + + """ + if switch is not None and path is not None: + raise ValueError("switch and path are mutually exclusive") + + if switch is not None and revision is not None: + raise ValueError("switch and revision are mutually exclusive") + + app_facade = self._facade() + charms_facade = client.CharmsFacade.from_connection(self.connection) + + # 1 - Figure out the destination origin and destination charm_url + # 2 - Then take care of the resources + # 3 - Finally execute the upgrade + + # Get the charm URL and charm origin of the given application is running at present. + charm_url_origin_result = await app_facade.GetCharmURLOrigin( + application=self.name + ) + if charm_url_origin_result.error is not None: + err = charm_url_origin_result.error + raise JujuError(f"{err.code} : {err.message}") + + current_origin = charm_url_origin_result.charm_origin + if path is not None or (switch is not None and is_local_charm(switch)): + local_path = path or switch + assert local_path + + await self.local_refresh( + charm_origin=current_origin, + force=force, + force_series=force_series, + force_units=force_units, + path=local_path, + resources=resources, + ) + return + + origin = _refresh_origin(current_origin, channel, revision) + + # If switch is not None at this point, that means it's a switch to a store charm + charm_url = switch or charm_url_origin_result.url + parsed_url = URL.parse(charm_url) + charm_name = parsed_url.name + + if parsed_url.schema is None: + raise JujuError( + f"A ch: or cs: schema is required for application refresh, given : {parsed_url!s}" + ) + + # Resolve the given charm URLs with an optionally specified preferred channel. + # Channel provided via CharmOrigin. + resolved_charm_with_channel_results = await charms_facade.ResolveCharms( + resolve=[ + client.ResolveCharmWithChannel( + charm_origin=origin, + switch_charm=bool(switch), + reference=charm_url, + ) + ] + ) + resolved_charm = resolved_charm_with_channel_results.results[0] + + # Get the destination origin and destination charm_url from the resolved charm + if resolved_charm.error is not None: + err = resolved_charm.error + raise JujuError(f"{err.code} : {err.message}") + dest_origin = resolved_charm.charm_origin + charm_url = resolved_charm.url + + # Add the charm with the destination url and origin + charm_origin_result = await charms_facade.AddCharm( + url=charm_url, force=force, charm_origin=dest_origin + ) + if charm_origin_result.error is not None: + err = charm_origin_result.error + raise JujuError(f"{err.code} : {err.message}") + + # Now take care of the resources: + + # user supplied resources to be used in refresh, + # will override the default values if there's any + arg_resources = resources or {} + + # need to process the given resources, as they can be + # paths or revisions + _arg_res_filenames = {} + _arg_res_revisions = {} + for res, filename_or_rev in arg_resources.items(): + if isinstance(filename_or_rev, int): + _arg_res_revisions[res] = filename_or_rev + else: + _arg_res_filenames[res] = filename_or_rev + + # Get the existing resources from the ResourcesFacade + request_data = [client.Entity(self.tag)] + resources_facade = client.ResourcesFacade.from_connection(self.connection) + response = await resources_facade.ListResources(entities=request_data) + existing_resources = { + resource.name: resource for resource in response.results[0].resources + } + + charmhub = self.model.charmhub + charm_resources = await charmhub.list_resources(charm_name) + + # Compute the difference btw resources needed and the existing resources + resources_to_update = [ + resource + for resource in charm_resources + if utils.should_upgrade_resource( + resource, existing_resources, arg_resources + ) + ] + + # Update the resources + if resources_to_update: + request_data = [] + for resource in resources_to_update: + res_name = resource.get("Name", resource.get("name")) + request_data.append( + client.CharmResource( + description=resource.get( + "Description", resource.get("description") + ), + name=res_name, + path=_arg_res_filenames.get( + res_name, resource.get("Path", resource.get("filename", "")) + ), + revision=_arg_res_revisions.get(res_name, -1), + type_=resource.get("Type", resource.get("type")), + origin="store", + ) + ) + + response = await resources_facade.AddPendingResources( + application_tag=self.tag, + charm_url=charm_url, + resources=request_data, + charm_origin=dest_origin, + ) + pending_ids = response.pending_ids + resource_ids = { + resource.get("Name", resource.get("name")): id_ + for resource, id_ in zip(resources_to_update, pending_ids) + } + else: + resource_ids = None + + set_charm_args = { + "application": self.entity_id, + "charm_url": charm_url, + "charm_origin": dest_origin, + "config_settings": None, + "config_settings_yaml": None, + "force": force, + "force_units": force_units, + "resource_ids": resource_ids, + "storage_constraints": None, + } + if self.connection.is_using_old_client: + set_charm_args["force_series"] = force_series + + # Update the application + await app_facade.SetCharm(**set_charm_args) + + await self.model.block_until(lambda: self.data["charm-url"] == charm_url) + + upgrade_charm = refresh + + async def local_refresh( + self, + *, + charm_origin: _definitions.CharmOrigin, + force: bool, + force_series: bool, + force_units: bool, + path: Union[Path, str], + resources: Optional[Dict[str, str]], + ): + """Refresh the charm for this application with a local charm. + + :param dict charm_origin: The charm origin of the destination charm + we're refreshing to + :param bool force: Refresh even if validation checks fail + :param bool force_series: Refresh even if series of deployed + application is not supported by the new charm + :param bool force_units: Refresh all units immediately, even if in + error state + :param Path|str path: Refresh to a charm located at path + :param dict resources: Dictionary of resource name/filepath pairs + + """ + app_facade = self._facade() + + if isinstance(path, str) and path.startswith("local:"): + path = path[6:] + local_path = Path(path) + charm_dir = local_path.expanduser().resolve() + model_config = await self.get_config() + + series = await self.get_series() or self.model.info.get("default-series", "") + if not series: + metadata = utils.get_local_charm_metadata(charm_dir) + await get_charm_series(metadata, self.model) + + if not series: + default_series = model_config.get("default-series") + if default_series: + series = default_series.value + charm_url = await self.model.add_local_charm_dir(charm_dir, series) + metadata = utils.get_local_charm_metadata(local_path) + if resources is not None: + resources = await self.model.add_local_resources( + self.entity_id, charm_url, metadata, resources=resources + ) + + # We know this charm is a local charm, but this charm origin could be + # the charm origin of a charmhub charm. Ensure that we update/remove + # the appropriate fields. + charm_origin.source = "local" + charm_origin.track = None + charm_origin.risk = None + charm_origin.branch = None + charm_origin.hash_ = None + charm_origin.id_ = None + charm_origin.revision = URL.parse(charm_url).revision + + set_charm_args = { + "application": self.entity_id, + "charm_origin": charm_origin, + "charm_url": charm_url, + "config_settings": None, + "config_settings_yaml": None, + "force": force, + "force_units": force_units, + "resource_ids": resources, + "storage_constraints": None, + } + + if self.connection.is_using_old_client: + set_charm_args["force_series"] = force_series + + # Update application + await app_facade.SetCharm(**set_charm_args) + + await self.model.block_until(lambda: self.data["charm-url"] == charm_url) + + async def get_metrics(self): + """Get metrics for this application's units. + + :return: Dictionary of unit_name:metrics + + """ + return await self.model.get_metrics(self.tag) + + +def _refresh_origin( + current_origin: client.CharmOrigin, + channel: Optional[str] = None, + revision: Optional[int] = None, +) -> client.CharmOrigin: + chan = None if channel is None else Channel.parse(channel).normalize() + + return client.CharmOrigin( + source=current_origin.source, + track=chan.track if chan else current_origin.track, + risk=chan.risk if chan else current_origin.risk, + revision=revision if revision is not None else current_origin.revision, + base=current_origin.base, + architecture=current_origin.get("architecture", DEFAULT_ARCHITECTURE), + ) + + +class ExposedEndpoint: + """ExposedEndpoint stores the list of CIDRs and space names which should be + allowed access to the port ranges that the application has opened for a + particular endpoint. Both lists are optional; if empty, the opened port + ranges will be reachable from any source IP address. + """ + + def __init__(self, to_spaces=None, to_cidrs=None): + if to_spaces is not None and not isinstance(to_spaces, list): + raise ValueError("to_spaces must be a list of space names or None") + if to_cidrs is not None and not isinstance(to_cidrs, list): + raise ValueError("to_cidrs must be a list of CIDRs or None") + + self.to_cidrs = to_cidrs + self.to_spaces = to_spaces + + def includes_spaces(self): + return self.to_spaces is not None and len(self.to_spaces) > 0 + + def includes_non_wildcard_cidrs(self): + to_cidrs = self.to_cidrs or [] + non_wildcard_cidrs = filter(lambda x: x == "0.0.0.0/0" or x == "::/0", to_cidrs) + return len(list(non_wildcard_cidrs)) > 0 + + @classmethod + def from_dict(cls, data): + d = data or {} + if not isinstance(d, dict): + raise ValueError( + "expected a dictionary with fields: expose-to-spaces and expose-to-cidrs" + ) + + to_spaces = None + if "expose-to-spaces" in d and isinstance(d["expose-to-spaces"], list): + to_spaces = d["expose-to-spaces"] + to_cidrs = None + if "expose-to-cidrs" in d and isinstance(d["expose-to-cidrs"], list): + to_cidrs = d["expose-to-cidrs"] + + return cls(to_spaces=to_spaces, to_cidrs=to_cidrs) + + def to_dict(self): + d = {} + if self.to_cidrs is not None: + d["expose-to-cidrs"] = self.to_cidrs + if self.to_spaces is not None: + d["expose-to-spaces"] = self.to_spaces + return d + + def __str__(self): + descr = "" + if self.to_spaces is not None and len(self.to_spaces) > 0: + if len(self.to_spaces) == 1: + descr = f"from space {self.to_spaces[0]}" + elif len(self.to_spaces) > 1: + descr = "from spaces {}".format(",".join(self.to_spaces)) + + if self.to_cidrs is not None and len(self.to_cidrs) > 0: + descr = descr + " and " + + if self.to_cidrs is not None: + if len(self.to_cidrs) == 1: + descr = descr + f"from CIDR {self.to_cidrs[0]}" + elif len(self.to_cidrs) > 1: + descr = descr + "from CIDRs {}".format(",".join(self.to_cidrs)) + + return descr diff --git a/build/lib/juju/bundle.py b/build/lib/juju/bundle.py new file mode 100644 index 00000000..ad6600b3 --- /dev/null +++ b/build/lib/juju/bundle.py @@ -0,0 +1,1197 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +import base64 +import io +import logging +import os +import zipfile +from contextlib import closing +from pathlib import Path +from typing import TYPE_CHECKING, Mapping, cast + +import requests +import yaml +from toposort import toposort_flatten + +from . import jasyncio, utils +from .client import client +from .constraints import parse as parse_constraints +from .errors import JujuError +from .origin import Channel, Source +from .url import URL, Schema +from .utils import get_base_from_origin_or_channel + +if TYPE_CHECKING: + from .constraints import StorageConstraintDict + from .model import Model + +log = logging.getLogger(__name__) + + +class BundleHandler: + """Handle bundles by using the API to translate bundle YAML into a plan of + steps and then dispatching each of those using the API. + """ + + def __init__(self, model, trusted=False, forced=False): + self.model = model + self.trusted = trusted + self.forced = forced + self.bundle = None + self.overlays = [] + self.overlay_removed_charms = set() + + self.plan = [] + self.references = {} + self._units_by_app = {} + self.origins = {} + + for unit_name, unit in model.units.items(): + app_units = self._units_by_app.setdefault(unit.application, []) + app_units.append(unit_name) + + self.bundle_facade = client.BundleFacade.from_connection(model.connection()) + self.client_facade = client.ClientFacade.from_connection(model.connection()) + self.app_facade = client.ApplicationFacade.from_connection(model.connection()) + self.ann_facade = client.AnnotationsFacade.from_connection(model.connection()) + self.machine_manager_facade = client.MachineManagerFacade.from_connection( + model.connection() + ) + + # Feature detect if we have the new charms facade, otherwise fallback + # to the client facade, when making calls. + best_facade_version = client.CharmsFacade.best_facade_version( + model.connection() + ) + if best_facade_version is not None and best_facade_version > 2: + self.charms_facade = client.CharmsFacade.from_connection(model.connection()) + else: + self.charms_facade = None + + # This describes all the change types that the BundleHandler supports. + change_type_cls = [ + AddApplicationChange, + AddCharmChange, + AddMachineChange, + AddRelationChange, + AddUnitChange, + CreateOfferChange, + ConsumeOfferChange, + ExposeChange, + ScaleChange, + SetAnnotationsChange, + ] + self.change_types = {} + for change_cls in change_type_cls: + self.change_types[change_cls.method()] = change_cls + + async def _validate_bundle(self, bundle): + """Validate the bundle for known issues, raises an error if it + encounters a known problem + """ + apps_dict = bundle.get("applications", {}) + for app_name in self.applications: + app_dict = apps_dict[app_name] + app_trusted = app_dict.get("trust") + if (not self.trusted and not self.forced) and app_trusted: + raise JujuError( + "Bundle cannot be deployed without trusting applications with your cloud credentials.\n" + "Please repeat the deploy command with the --trust argument if you consent to trust the following application\n" + f" - {app_name}\n" + ) + return bundle + + async def _handle_local_charms(self, bundle, bundle_dir): + """Search for references to local charms (i.e. filesystem paths) + in the bundle. Upload the local charms to the model, and replace + the filesystem paths with appropriate 'local:' paths in the bundle. + + Return the modified bundle. + + :param dict bundle: Bundle dictionary + :return: Modified bundle dictionary + + """ + apps, args = [], [] + + default_series = bundle.get("series") + apps_dict = bundle.get("applications", {}) + for app_name in self.applications: + app_dict = apps_dict[app_name] + charm_dir = app_dict["charm"] + try: + charm_path = (bundle_dir / charm_dir).resolve() + if not ( + charm_path.is_dir() + or ( + charm_path.is_file() and charm_path.suffix in (".charm", ".zip") + ) + ): + continue + charm_dir = str(charm_path) + except ValueError: + pass + except FileNotFoundError: + continue + series = app_dict.get("series") or default_series + if not series: + metadata = utils.get_local_charm_metadata(charm_dir) + series = await get_charm_series(metadata, self.model) + if not series: + base = utils.get_local_charm_base(None, charm_path, client.Base) + series = utils.base_channel_to_series(base.channel) + if not series: + raise JujuError( + f"Couldn't determine series for charm at {charm_dir}. " + "Add a 'series' key to the bundle." + ) + # Keep track of what we need to update. We keep a list of apps + # that need to be updated, and a corresponding list of args + # needed to update those apps. + apps.append(app_name) + args.append((charm_dir, series)) + + if apps: + # If we have apps to update, spawn all the coroutines concurrently + # and wait for them to finish. + charm_urls = await jasyncio.gather(*[ + self.model.add_local_charm_dir(*params) for params in args + ]) + + # Update the 'charm:' entry for each app with the new 'local:' url. + for app_name, charm_url, (charm_dir, series) in zip(apps, charm_urls, args): + metadata = utils.get_local_charm_metadata(charm_dir) + resources = await self.model.add_local_resources( + app_name, + charm_url, + metadata, + resources=bundle.get("applications", {app_name: {}})[app_name].get( + "resources", {} + ), + ) + apps_dict[app_name]["charm"] = charm_url + apps_dict[app_name]["resources"] = resources + origin = client.CharmOrigin(source="local", risk="stable") + if not self.model.connection().is_using_old_client: + origin.base = utils.get_local_charm_base( + series, charm_dir, client.Base + ) + self.origins[charm_url] = {str(None): origin} + + return bundle + + def _resolve_include_file_config(self, bundle_dir): + """If any of the applications (including the ones in the overlays) + have "config: include-file:..." or "config: + include-base64:...", then we have to resolve and inline them + into the bundle here because they're all files with local + relative paths, so backend can't handle them. + + """ + bundle_apps = [self.bundle.get("applications", {})] + overlay_apps = [overlay.get("applications", {}) for overlay in self.overlays] + + for apps in bundle_apps + overlay_apps: + for app_name, app in apps.items(): + if app and "options" in app: + if "config" in app["options"] and app["options"][ + "config" + ].startswith("include-file"): + # resolve the file + if not bundle_dir: + raise NotImplementedError( + "unable to resolve paths for config:include-file for non-local charms" + ) + try: + config_path = ( + bundle_dir + / Path(app["options"]["config"].split("//")[1]) + ).resolve() + except IndexError: + raise JujuError( + "the path for the included file should start with // and be relative to the bundle" + ) + if not config_path.exists(): + raise JujuError( + "unable to locate config file : %s for : %s" + % (config_path, app_name) + ) + + # get the contents of the file + config_contents = yaml.safe_load(config_path.read_text()) + + # inline the configurations for the current app into + # the app['options'] + for key, val in config_contents[app_name].items(): + app["options"][key] = val + + # remove the 'include-file' config + app["options"].pop("config") + + for option_key, option_val in app["options"].items(): + if isinstance(option_val, str) and option_val.startswith( + "include-base64" + ): + # resolve the file + if not bundle_dir: + raise NotImplementedError( + "unable to resolve paths for config:include-base64 for non-local charms" + ) + try: + base64_path = ( + bundle_dir / Path(option_val.split("//")[1]) + ).resolve() + except IndexError: + raise JujuError( + "the path for the included base64 file should start with // and be relative to the bundle" + ) + + if not base64_path.exists(): + raise JujuError( + "unable to locate the base64 file : %s for : %s" + % (base64_path, app_name) + ) + + # inline the base64 encoded config value + base64_contents = base64.b64decode(base64_path.read_text()) + app["options"][option_key] = base64_contents + + return self.bundle, self.overlays + + async def fetch_plan(self, bundle, origin, overlays=[]): + """fetch_plan is called by the model.deploy(). It gathers the information about the + bundle to be deployed (whether local or CharmHub), straightens it up, applies overlays + if any overlays are given. Validates the bundle against known issues. Resolves and adds + local charms if there's any in the bundle. Resolves and adds --include-file configs if + there's any. Finally it calls the BundleFacade.GetChanges() to get the plan for the + bundle to be handed to the execute_plan() by the model.deploy(). Note that it doesn't + return the plan, just saves it in the self (BundleHandler) to be used later. + + :param client.URL bundle_url: the url of the bundle to be deployed + :param client.CharmOrigin origin: the origin of the bundle to be deployed + :param [string] overlays: paths for the yaml files containing overlays to be applied to + the bundle during deployment + + :returns: None + """ + bundle_dir = None + + if is_local_bundle(str(bundle)): + path = str(bundle) + if path.startswith("local:"): + path = path[6:] + bundle_yaml, bundle_dir = read_local_bundle(path) + + else: + if client.CharmsFacade.best_facade_version(self.model.connection()) < 3: + url = URL.parse(bundle, default_store=Schema.CHARM_STORE) + else: + url = URL.parse(bundle) + path = url.path() + bundle_yaml = await self._download_bundle(bundle, origin) + + if not bundle_yaml: + raise JujuError("empty bundle, nothing to deploy") + + _bundles = [b for b in yaml.safe_load_all(bundle_yaml)] + self.overlays = _bundles[1:] + self.bundle = _bundles[0] + + if overlays != []: + for overlay_yaml_path in overlays: + try: + overlay_contents = Path(overlay_yaml_path).read_text() + except OSError as e: + raise JujuError( + "unable to open overlay %s \n %s" % (overlay_yaml_path, e) + ) + self.overlays.extend(yaml.safe_load_all(overlay_contents)) + + # gather the names of the removed charms so model.deploy + # wouldn't wait for them to appear in the model + for overlay in self.overlays: + overlay_apps = overlay.get("applications", {}) + for charm_name, val in overlay_apps.items(): + if val is None: + self.overlay_removed_charms.add(charm_name) + + self.bundle = await self._validate_bundle(self.bundle) + + if is_local_bundle(path): + self.bundle = await self._handle_local_charms(self.bundle, bundle_dir) + + self.bundle, self.overlays = self._resolve_include_file_config(bundle_dir) + + _yaml_data = [yaml.dump(self.bundle)] + for overlay in self.overlays: + _yaml_data.append(yaml.dump(overlay).replace("null", "")) # noqa: PERF401 + yaml_data = "---\n".join(_yaml_data) + + self.plan = await self.bundle_facade.GetChangesMapArgs( + bundleurl=path, yaml=yaml_data + ) + + if self.plan.errors and any(self.plan.errors): + raise JujuError(self.plan.errors) + + async def _download_bundle(self, charm_url, origin): + if self.charms_facade is None: + raise JujuError( + f"unable to download bundle for {charm_url} using the new charms facade. Upgrade controller to proceed." + ) + + id_ = origin.id_ if origin.id_ else "" + hash_ = origin.hash_ if origin.hash_ else "" + charm_origin = { + "source": origin.source, + "type": origin.type_, + "id": id_, + "hash": hash_, + "revision": origin.revision, + "risk": origin.risk, + "track": origin.track, + "architecture": origin.architecture, + } + if self.model.connection().is_using_old_client: + charm_origin["os"] = origin.os + charm_origin["series"] = origin.series + else: + charm_origin["base"] = origin.base + + resp = await self.charms_facade.GetDownloadInfos( + entities=[{"charm-url": str(charm_url), "charm-origin": charm_origin}] + ) + if len(resp.results) != 1: + raise JujuError(f"expected one result, received {resp.results}") + + result = resp.results[0] + if not result.url: + raise JujuError(f"no url found for bundle {charm_url.name}") + + bundle_resp = requests.get(result.url) # noqa: S113 + bundle_resp.raise_for_status() + + with closing(bundle_resp), zipfile.ZipFile( + io.BytesIO(bundle_resp.content) + ) as archive: + return self._get_bundle_yaml(archive) + + def _get_bundle_yaml(self, archive): + for member in archive.infolist(): + if member.filename == "bundle.yaml": + return archive.read(member) + raise JujuError("bundle.yaml not found") + + async def _resolve_charms(self): + deployed = dict() + + specs = self.applications_specs + + for name in self.applications: + spec = specs[name] + app = self.model.applications.get(name, None) + + cons = None + if app is not None: + deployed[name] = name + + if is_local_charm(spec["charm"]): + spec["charm"] = self.model.applications[name] + continue + if spec["charm"] == app.charm_url: + continue + + cons = await app.get_constraints() + + if is_local_charm(spec["charm"]): + continue + + charm_url = URL.parse(spec["charm"]) + + channel = ( + Channel.parse(spec["channel"]) + if "channel" in spec + else Channel("latest", "stable") + ) + track, risk = channel.track, channel.risk + series = spec.get("series", self.bundle.get("series", None)) + base = get_base_from_origin_or_channel(channel, series) + + if self.charms_facade is not None: + if cons is not None and cons["arch"] != "": + architecture = cons["arch"] + else: + architecture = await self.model._resolve_architecture(charm_url) + + origin = client.CharmOrigin( + source=Source.CHARM_HUB.value, + architecture=architecture, + risk=risk, + track=track, + base=base, + ) + charm_url, charm_origin = await self.model._resolve_charm( + charm_url, origin + ) + spec["charm"] = str(charm_url) + else: + charm_origin = client.CharmOrigin( + source=Source.CHARM_HUB.value, + risk=risk, + track=track, + base=base, + ) + + if str(channel) not in self.origins: + self.origins[str(charm_url)] = {} + self.origins[str(charm_url)][str(channel)] = charm_origin + + async def execute_plan(self): + await self._resolve_charms() + + changes = ChangeSet(self.plan.changes) + for step in changes.sorted(): + change_cls = self.change_types.get(step.method) + if change_cls is None: + raise NotImplementedError(f"unknown change type: {step.method}") + change = change_cls(step.id_, step.requires, step.args) + log.info(f"Applying change: {change}") + self.references[step.id_] = await change.run(self) + + @property + def applications(self): + apps_dict = self.bundle.get("applications", {}) + return set(apps_dict.keys()) - self.overlay_removed_charms + + @property + def applications_specs(self): + return self.bundle.get("applications", {}) + + def resolve_relation(self, reference): + parts = reference.split(":", maxsplit=1) + application = self.resolve(parts[0]) + if len(parts) == 1: + return application + return f"{application}:{parts[1]}" + + def resolve(self, reference): + if reference and reference.startswith("$"): + ref = self.references[reference[1:]] + if ref is not None: + reference = ref + return reference + + +def is_local_charm(charm_url: str): + return ( + charm_url.startswith(".") + or charm_url.startswith("local:") + or os.path.isabs(charm_url) + ) + + +is_local_bundle = is_local_charm + + +def read_local_bundle(path): + path = Path(path) + if os.path.isfile(path): + bundle_yaml = path.read_text() + bundle_dir = path.parent + elif os.path.isdir(path): + bundle_yaml = (path / "bundle.yaml").read_text() + bundle_dir = path + + return (bundle_yaml, bundle_dir) + + +async def get_charm_series(metadata, model): + """Inspects the given metadata and returns a default series from its + metadata.yaml (the first item in the 'series' list). + + Tries to extract the information from the given model if no + series is determined from the given metadata. + Returns None if no series can be determined. + + """ + _series = metadata.get("series") + series = _series[0] if _series else None + + if series is None: + # get the ConfigValue for the 'default-series' from the model + model_config = await model.get_config() + _default_series = model_config.get("default-series") + + if _default_series is not None: + # then update the series with its value + series = _default_series.value + + return series + + +class ChangeSet: + def __init__(self, changes): + self.changes = changes + + # sorted does a topological sort of the changes + def sorted(self): + if len(self.changes) == 0: + return [] + + changes = {} + for change in self.changes: + changes[change.id_] = set(change.requires) + sorted_changes = toposort_flatten(changes) + results = [] + for change_id in sorted_changes: + for change in self.changes: + if change_id == change.id_: + results.append(change) + break + return results + + +class ChangeInfo: + _toPy = {} + + def __init__(self, change_id, requires, params=None): + self.change_id = change_id + self.requires = requires + + type(self).from_dict(self, params) + + @classmethod + def from_dict(cls, self, data): + """from_dict converts a data bag into fields on a class instance. + If a value is missing from the data, then None is assigned to the field + instance value. + """ + d = data or {} + for k, v in cls._toPy.items(): + if k in d: + setattr(self, v, d[k]) + else: + setattr(self, v, None) + + +class AddApplicationChange(ChangeInfo): + _toPy = { + "charm": "charm", + "series": "series", + "application": "application", + "options": "options", + "constraints": "constraints", + "storage": "storage", + "devices": "devices", + "endpoint-bindings": "endpoint_bindings", + "resources": "resources", + "num-units": "num_units", + "channel": "channel", + } + + """AddApplicationChange holds a change for deploying a Juju application. + + :charm: URL of the charm to be used to deploy this application. + :series: series of the application to be deployed if the charm + default is not sufficient. + :application: application name. + :num_units: number of units required. For IAAS models, this will + be 0 and separate AddUnitChanges will be used. For Kubernetes models, + this will be used to scale the application. + :options: holds application options. + :constraints: optional application constraints. + :storage: optional storage constraints, in the form of `{label: constraint}`. + The label is a string specified by the charm, while the constraint is + either a constraints.StorageConstraintDict, or a string following + `the juju storage constraint directive format `_, + specifying the storage pool, number of volumes, and size of each volume. + :devices: optional devices constraints. + :endpoint_bindings: optional endpoint bindings + :resources: identifies the revision to use for each resource of the + application's charm. + :local_resources: identifies the path to the local resource of the + application's charm. + """ + + storage: Mapping[str, str | StorageConstraintDict] | None = None + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "deploy" + + async def run(self, context): + """Executes a AddApplicationChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + # NB: this should really be handled by the controller when generating the + # bundle change plan, and this short-term workaround may be missing some + # aspects of the logic which the CLI client contains to handle edge cases. + model = cast("Model", context.model) # pyright: ignore[reportUnknownMemberType] + if self.application in model.applications: + log.debug("Skipping %s; already in model", self.application) + return self.application + + # resolve indirect references + charm = context.resolve(self.charm) + options = {} + if self.options is not None: + options = self.options + if context.trusted: + if model.info.agent_version < client.Number.from_json("2.4.0"): + raise NotImplementedError( + f"trusted is not supported on model version {model.info.agent_version}" + ) + options["trust"] = "true" + + url = URL.parse(str(charm)) + + # set the channel to the default value if not specified + if not self.channel: + if Schema.CHARM_HUB.matches(url.schema): + self.channel = "latest/stable" + else: # for local charms + self.channel = "" + + channel = None + non_normalized_channel = None + if self.channel is not None and self.channel != "": + non_normalized_channel = Channel.parse(self.channel) + channel = non_normalized_channel.normalize() + + origin = context.origins.get(str(url), {}).get( + str(channel), + context.origins.get(str(url), {}).get(str(non_normalized_channel), None), + ) + if origin is None: + raise JujuError( + f"expected origin to be valid for application {self.application} and charm {url!s} with channel {channel!s}" + ) + + if not self.series: + self.series = context.bundle.get("series", None) + + resources = ( + context.bundle.get("applications", {}) + .get(self.application, {}) + .get("resources", {}) + ) + if Schema.CHARM_HUB.matches(url.schema): + resources = await model._add_charmhub_resources( + self.application, charm, origin, overrides=self.resources + ) + + await model._deploy( + charm_url=charm, + application=self.application, + series=self.series, + config=options, + constraints=self.constraints, + endpoint_bindings=self.endpoint_bindings, + resources=resources, + storage=self.storage, + channel=self.channel, + devices=self.devices, + num_units=self.num_units, + charm_origin=origin, + ) + return self.application + + def __str__(self): + series = "" + if self.series is not None and self.series != "": + series = f" on {self.series}" + units_info = "" + if self.num_units is not None: + plural = "" + if self.num_units > 1: + plural = "s" + units_info = f" with {self.num_units} unit{plural}" + return f"deploy application {self.application}{units_info}{series} using {self.charm}" + + +class AddCharmChange(ChangeInfo): + _toPy = { + "charm": "charm", + "series": "series", + "channel": "channel", + "revision": "revision", + "architecture": "architecture", + } + + """AddCharmChange holds a change for adding a charm to the environment. + + :change_id: id of the change that will be used to identify the current + change + :requires: is a slice of dependencies that are required to happen. + :params: holds the change parameters from the api response. Currently the + params could either be a list or a dict. The later being the newer + return results. + + Params holds the following values: + :charm: URL of the charm to be added. + :series: series of the charm to be added if the charm default is + not sufficient. + :channel: preferred channel for obtaining the charm. + :revision: specified revision of the charm to be added if specified. + """ + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "addCharm" + + async def run(self, context): + """Executes a AddCharmChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + # We don't add local charms because they've already been added + # by self._handle_local_charms + if is_local_charm(str(self.charm)): + return self.charm + + url = URL.parse(str(self.charm)) + ch = None + identifier = None + if Schema.CHARM_HUB.matches(url.schema): + ch = Channel("latest", "stable") + if self.channel: + ch = Channel.parse(self.channel).normalize() + arch = self.architecture + if not arch: + arch = await context.model._resolve_architecture(url) + base = get_base_from_origin_or_channel(ch, self.series) + origin = client.CharmOrigin( + source=Source.CHARM_HUB.value, + architecture=arch, + risk=ch.risk, + track=ch.track, + revision=self.revision, + base=base, + ) + identifier, origin = await context.model._resolve_charm(url, origin) + + if identifier is None: + raise JujuError(f"unknown charm {self.charm}") + + await context.model._add_charm(str(identifier), origin) + + if str(ch) not in context.origins: + context.origins[str(identifier)] = {} + context.origins[str(identifier)][str(ch)] = origin + + return str(identifier) if identifier is not None else url.path() + + def __str__(self): + series = "" + channel = "" + if self.series is not None and self.series != "": + series = f" for series {self.series}" + if self.channel is not None: + channel = f" from channel {self.channel}" + return f"upload charm {self.charm}{series}{channel}" + + +class AddMachineChange(ChangeInfo): + _toPy = { + "series": "series", + "constraints": "constraints", + "container-type": "container_type", + "parent-id": "parent_id", + } + + """AddMachineChange holds a change for adding a machine or container. + + :change_id: id of the change that will be used to identify the current + change + :requires: is a slice of dependencies that are required to happen. + :params: holds the change parameters from the api response. Currently the + params could either be a list or a dict. The later being the newer + return results. + + Params holds the following values: + :series: optional machine OS series. + :constraints: optional machine constraints. + :container_type: optionally type of the container (for instance + "lxc" or kvm"). It is not specified for top level machines. + :parent_id: id of the parent machine. + """ + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "addMachines" + + async def run(self, context): + """Executes a AddMachineChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + # Fix up values, as necessary. + params = {} + if self.parent_id is not None: + if self.parent_id.startswith("$addUnit"): + unit = context.resolve(self.parent_id)[0] + params["parent_id"] = unit.machine.entity_id + else: + params["parent_id"] = context.resolve(self.parent_id) + + params["constraints"] = parse_constraints(self.constraints) + params["jobs"] = params.get("jobs", ["JobHostUnits"]) + if not context.model.connection().is_using_old_client: + params["base"] = client.Base( + channel=utils.get_series_version(self.series), name="ubuntu" + ) + else: + params["series"] = self.series + + if self.container_type == "lxc": + log.warning( + "Juju 2.0 does not support lxc containers. " + "Converting containers to lxd." + ) + params["container_type"] = "lxd" + else: + params["container_type"] = self.container_type + + # Submit the request. + params = client.AddMachineParams(**params) + results = await context.machine_manager_facade.AddMachines(params=[params]) + error = results.machines[0].error + if error: + raise ValueError("Error adding machine: %s" % error.message) + machine = results.machines[0].machine + log.debug("Added new machine %s", machine) + return machine + + def __str__(self): + machine = "new machine" + if self.container_type is not None and self.container_type != "": + machine = f"{self.container_type} container on {machine}" + return f"add {machine}" + + +class AddRelationChange(ChangeInfo): + _toPy = {"endpoint1": "endpoint1", "endpoint2": "endpoint2"} + """AddRelationChange holds a change for adding a relation between two + applications. + + :change_id: id of the change that will be used to identify the current + change + :requires: is a slice of dependencies that are required to happen. + :params: holds the change parameters from the api response. Currently the + params could either be a list or a dict. The later being the newer + return results. + + Params holds the following values: + Endpoint1 and Endpoint2 hold relation endpoints in the + "application:interface" form, where the application is either a + placeholder pointing to an application change or in the case of a model + that already has this application deployed, the name of the + application, and the interface is optional. Examples are + "$deploy-42:web", "$deploy-42", "mysql:db". + """ + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "addRelation" + + async def run(self, context): + """Executes a AddRelationChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + ep1 = context.resolve_relation(self.endpoint1) + ep2 = context.resolve_relation(self.endpoint2) + + # NB: this should really be handled by the controller when generating the + # bundle change plan, and this short-term workaround may be missing some + # aspects of the logic which the CLI client contains to handle edge cases. + existing = [rel for rel in context.model.relations if rel.matches(ep1, ep2)] + if existing: + log.info("Skipping %s <-> %s; already related", ep1, ep2) + return existing[0] + + log.info("Relating %s <-> %s", ep1, ep2) + return await context.model.relate(ep1, ep2) + + def __str__(self): + return f"add relation {self.endpoint1} - {self.endpoint2}" + + +class AddUnitChange(ChangeInfo): + _toPy = {"application": "application", "to": "to"} + """AddUnitChange holds a change for adding an application unit. + + :change_id: id of the change that will be used to identify the current + change + :requires: is a slice of dependencies that are required to happen. + :params: holds the change parameters from the api response. Currently the + params could either be a list or a dict. The later being the newer + return results. + + Params holds the following values: + :application: application placeholder name for which a unit is + added. + :to: optional location where to add the unit, as a placeholder + pointing to another unit change or to a machine change. + """ + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "addUnit" + + async def run(self, context): + """Executes a AddUnitChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + application = context.resolve(self.application) + placement = context.resolve(self.to) + if context._units_by_app.get(application): + # enough units for this application already exist; + # claim one, and carry on + # NB: this should probably honor placement, but the juju client + # doesn't, so we're not bothering, either + unit_name = context._units_by_app[application].pop() + log.debug("Reusing unit %s for %s", unit_name, application) + return context.model.units[unit_name] + + log.debug( + "Adding new unit for %s%s", + application, + " to %s" % placement if placement else "", + ) + return await context.model.applications[application].add_unit( + count=1, + to=placement, + ) + + def __str__(self): + return f"add {self.application} unit to {self.to}" + + +class CreateOfferChange(ChangeInfo): + _toPy = { + "application": "application", + "endpoints": "endpoints", + "offer-name": "offer_name", + } + """CreateOfferChange holds a change for creating a new application endpoint + offer. + + :change_id: id of the change that will be used to identify the current + change + :requires: is a slice of dependencies that are required to happen. + :params: holds the change parameters from the api response. Currently the + params could either be a list or a dict. The later being the newer + return results. + + Params holds the following values: + :application: is the name of the application to create an offer for. + added. + :endpoint: is a list of application endpoint to expose as part of an + offer. + :offer_name: describes the offer name. + """ + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "createOffer" + + async def run(self, context): + """Executes a CreateOfferChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + application = context.resolve(self.application) + for ep in self.endpoints: + await context.model.create_offer( + ep, offer_name=self.offer_name, application_name=application + ) + + def __str__(self): + endpoints = "" + if self.endpoints is not None: + endpoints = ":{}".format(",".join(self.endpoints)) + return f"create offer {self.offer_name} using {self.application}{endpoints}" + + +class ConsumeOfferChange(ChangeInfo): + _toPy = {"url": "url", "application-name": "application_name"} + """CreateOfferChange holds a change for consuming a offer. + + :change_id: id of the change that will be used to identify the current + change + :requires: is a slice of dependencies that are required to happen. + :params: holds the change parameters from the api response. Currently the + params could either be a list or a dict. The later being the newer + return results. + + Params holds the following values: + :url: contains the location of the offer + :application_name: describes the application name on offer. + """ + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "consumeOffer" + + async def run(self, context): + """Executes a ConsumeOfferChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + application = context.resolve(self.application_name) + local_name = await context.model.consume( + self.url, application_alias=application + ) + return local_name + + def __str__(self): + return f"consume offer {self.application_name} at {self.url}" + + +class ExposeChange(ChangeInfo): + _toPy = { + "application": "application", + "exposed-endpoints": "exposed_endpoints", + } + """ExposeChange holds a change for exposing an application. + + :change_id: id of the change that will be used to identify the current + change + :requires: is a slice of dependencies that are required to happen. + :params: holds the change parameters from the api response. Currently the + params could either be a list or a dict. The later being the newer + return results. + + Params holds the following values: + :application: placeholder name of the application that must be + exposed. + :exposed_endpoints: a an optional dictionary where keys are endpoint + names and values are dicts that specify the space names and CIDRs + that should be able to access the port ranges that the application + has opened for each endpoint. + """ + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "expose" + + async def run(self, context): + """Executes a ExposeChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + application = context.resolve(self.application) + log.info("Exposing %s", application) + return await context.model.applications[application].expose( + self.exposed_endpoints + ) + + def __str__(self): + return f"expose {self.application}" + + +class ScaleChange(ChangeInfo): + _toPy = {"application": "application", "scale": "scale"} + """ + ScaleChange holds a change for scaling an application. + + :change_id: id of the change that will be used to identify the current + change + :requires: is a slice of dependencies that are required to happen. + :params: holds the change parameters from the api response. Currently the + params could either be a list or a dict. The later being the newer + return results. + + Params holds the following values: + :application: placeholder name of the application to be scaled. + :scale: is the new scale value to use. + """ + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "scale" + + async def run(self, context): + """Executes a ScaleChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + application = context.resolve(self.application) + return await context.model.applications[application].scale(scale=self.scale) + + def __str__(self): + return f"scale {self.application} to {self.scale} units" + + +class SetAnnotationsChange(ChangeInfo): + _toPy = {"id": "id", "entity-type": "entity_type", "annotations": "annotations"} + """SetAnnotationsChange holds a change for setting application and machine + annotations. + + :change_id: id of the change that will be used to identify the current + change + :requires: is a slice of dependencies that are required to happen. + :params: holds the change parameters from the api response. Currently the + params could either be a list or a dict. The later being the newer + return results. + + Params holds the following values: + :id: is the placeholder for the application or machine change + corresponding to the entity to be annotated. + :entity_type: type of the entity, "application" or "machine". + :ennotations: annotations as key/value pairs. + """ + + @staticmethod + def method(): + """Method returns an associated ID for the Juju API call.""" + return "setAnnotations" + + async def run(self, context): + """Executes a SetAnnotationsChange using the returned parameters from + the API server. + + :param context: is used for any methods or properties required to + perform a change. + """ + entity_id = context.resolve(self.id) + try: + entity = context.model.state.get_entity(self.entity_type, entity_id) + except KeyError: + entity = await context.model._wait_for_new(self.entity_type, entity_id) + return await entity.set_annotations(self.annotations) + + def __str__(self): + return f"set annotations for {self.id}" diff --git a/build/lib/juju/charm.py b/build/lib/juju/charm.py new file mode 100644 index 00000000..4d2fe2f3 --- /dev/null +++ b/build/lib/juju/charm.py @@ -0,0 +1,12 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import logging + +from . import model + +log = logging.getLogger(__name__) + + +class Charm(model.ModelEntity): + pass diff --git a/build/lib/juju/charmhub.py b/build/lib/juju/charmhub.py new file mode 100644 index 00000000..61cbfcca --- /dev/null +++ b/build/lib/juju/charmhub.py @@ -0,0 +1,181 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import json + +import requests + +from juju import jasyncio + +from .client import client +from .errors import JujuError + + +class CharmHub: + def __init__(self, model): + self.model = model + + async def _charmhub_url(self): + model_conf = await self.model.get_config() + return model_conf["charmhub-url"] + + async def request_charmhub_with_retry(self, url, retries): + for _ in range(retries): + _response = requests.get(url) # noqa: S113 + if _response.status_code == 200: + return _response + await jasyncio.sleep(5) + raise JujuError(f"Got {_response.status_code} from {url}") + + async def get_charm_id(self, charm_name): + _conn, _headers, _path_prefix = self.model.connection().https_connection() + + charmhub_url = await self._charmhub_url() + url = f"{charmhub_url.value}/v2/charms/info/{charm_name}" + _response = await self.request_charmhub_with_retry(url, 5) + response = json.loads(_response.text) + return response["id"], response["name"] + + async def is_subordinate(self, charm_name): + _conn, _headers, _path_prefix = self.model.connection().https_connection() + + charmhub_url = await self._charmhub_url() + url = f"{charmhub_url.value}/v2/charms/info/{charm_name}?fields=default-release.revision.subordinate" + _response = await self.request_charmhub_with_retry(url, 5) + response = json.loads(_response.text) + rev_response = response["default-release"]["revision"] + return "subordinate" in rev_response and rev_response["subordinate"] + + # TODO (caner) : we should be able to recreate the channel-map through the + # api call without needing the CharmHub facade + + async def list_resources(self, charm_name): + _conn, _headers, __path_prefix = self.model.connection().https_connection() + + charmhub_url = await self._charmhub_url() + url = f"{charmhub_url.value}/v2/charms/info/{charm_name}?fields=default-release.resources" + _response = await self.request_charmhub_with_retry(url, 5) + response = json.loads(_response.text) + return response["default-release"]["resources"] + + async def info(self, name, channel=None): + """Info displays detailed information about a CharmHub charm. The charm + can be specified by the exact name. + + Channel is a hint for providing the metadata for a given channel. + Without the channel hint then only the default release will have the + metadata. + + """ + if not name: + raise JujuError("name expected") + + if self.model.connection().is_using_old_client: + if channel is None: + channel = "" + facade = self._facade() + res = await facade.Info(tag=f"application-{name}", channel=channel) + err_code = res.errors.error_list.code + if err_code: + raise JujuError( + f"charmhub.info - {err_code} : {res.errors.error_list.message}" + ) + result = res.result + result.channel_map = CharmHub._channel_map_to_dict( + result.channel_map, name, channel=channel + ) + result = result.serialize() + else: + charmhub_url = await self._charmhub_url() + url = f"{charmhub_url.value}/v2/charms/info/{name}?fields=channel-map" + try: + _response = await self.request_charmhub_with_retry(url, 5) + except JujuError as e: + if "404" in e.message: + raise JujuError(f"{name} not found") from e + result = json.loads(_response.text) + result["channel-map"] = CharmHub._channel_list_to_map( + result["channel-map"], name, channel=channel + ) + return result + + @staticmethod + def _channel_list_to_map(channel_list_map, name, channel=""): + """Charmhub API returns the channel map as a list of channel objects + (with risk, track, revision, download etc). This turns that into a map + that's keyed with the channel=track/risk for easy + filtering/processing. This representation is also closer to the + result of the 2.9 facade call. + + So basically, + [{'channel':{'risk':'stable', 'track':'latest'}, 'revision': 58}] + becomes: + {'latest/stable': {'channel':{'risk':'stable', 'track':'latest'}, + 'revision': 58}} + + :param channel_list_map: [map[str][any]] + :return: map[str][map[str][any]] + """ + channel_map = {} + for ch in channel_list_map: + ch_name = f"{ch['channel']['track']}/{ch['channel']['risk']}" + if channel and channel != ch_name: + # If channel is given, then filter out the rest + continue + channel_map[ch_name] = ch + if channel == ch_name: + # If we found the desired channel, no need to continue + break + # After loop is done, check for non-existent channel + if channel and channel not in channel_map: + raise JujuError(f"Charmhub.info : channel {channel} not found for {name}") + return channel_map + + @staticmethod + def _channel_map_to_dict(channel_map, name, channel=""): + """Converts the client.definitions.Channel objects into python maps + inside a channel map (for pylibjuju <3.0) + + :param channel_map: map[str][Channel] + :return: map[str][map[str][any]] + """ + channel_dict = {} + for ch_name, ch_obj in channel_map.items(): + # No need to worry about filtering channel + # Charmhub facade will take care of that + _ch = ch_obj.serialize() + _ch["platforms"] = [p.serialize() for p in _ch["platforms"]] + channel_dict[ch_name] = _ch + if channel and channel not in channel_dict: + raise JujuError(f"Charmhub.info : channel {channel} not found for {name}") + return channel_dict + + async def find( + self, + query, + category=None, + channel=None, + charm_type=None, + platforms=None, + publisher=None, + relation_requires=None, + relation_provides=None, + ): + """Find queries the CharmHub store for available charms or bundles.""" + if charm_type is not None and charm_type not in ["charm", "bundle"]: + raise JujuError("expected either charm or bundle for charm_type") + + facade = self._facade() + return await facade.Find( + query=query, + category=category, + channel=channel, + type_=charm_type, + platforms=platforms, + publisher=publisher, + relation_provides=relation_provides, + relation_requires=relation_requires, + ) + + def _facade(self): + return client.CharmHubFacade.from_connection(self.model.connection()) diff --git a/build/lib/juju/client/__init__.py b/build/lib/juju/client/__init__.py new file mode 100644 index 00000000..4b9e20c5 --- /dev/null +++ b/build/lib/juju/client/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2024 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +"""Juju client.""" diff --git a/build/lib/juju/client/_client.py b/build/lib/juju/client/_client.py new file mode 100644 index 00000000..e72ae738 --- /dev/null +++ b/build/lib/juju/client/_client.py @@ -0,0 +1,236 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client import ( + _client1, + _client2, + _client3, + _client4, + _client5, + _client6, + _client7, + _client8, + _client9, + _client10, + _client11, + _client12, + _client17, + _client19, + _client20, +) +from juju.client._definitions import * + +CLIENTS = { + "7": _client7, + "3": _client3, + "4": _client4, + "2": _client2, + "17": _client17, + "6": _client6, + "11": _client11, + "1": _client1, + "10": _client10, + "9": _client9, + "5": _client5, + "19": _client19, + "20": _client20, + "8": _client8, + "12": _client12, +} + + +def lookup_facade(name, version): + """Given a facade name and version, attempt to pull that facade out + of the correct client.py file. + + """ + for _version in range(int(version), 0, -1): + try: + facade = getattr(CLIENTS[str(_version)], name) + return facade + except (KeyError, AttributeError): + continue + else: + raise ImportError(f"No supported version for facade: {name}") + + +class TypeFactory: + @classmethod + def from_connection(cls, connection): + """Given a connected Connection object, return an initialized and + connected instance of an API Interface matching the name of + this class. + + @param connection: initialized Connection object. + + """ + facade_name = cls.__name__ + if not facade_name.endswith("Facade"): + raise TypeError(f"Unexpected class name: {facade_name}") + facade_name = facade_name[: -len("Facade")] + version = connection.facades.get(facade_name) + if version is None: + raise Exception(f"No facade {facade_name} in facades {connection.facades}") + + c = lookup_facade(cls.__name__, version) + c = c() + c.connect(connection) + + return c + + @classmethod + def best_facade_version(cls, connection): + """Returns the best facade version for a given facade. This will help with + trying to provide different functionality for different facade versions. + + @param connection: initialized Connection object. + """ + facade_name = cls.__name__ + if not facade_name.endswith("Facade"): + raise TypeError(f"Unexpected class name: {facade_name}") + facade_name = facade_name[: -len("Facade")] + return connection.facades.get(facade_name) + + +class ActionFacade(TypeFactory): + pass + + +class AdminFacade(TypeFactory): + pass + + +class AllModelWatcherFacade(TypeFactory): + pass + + +class AllWatcherFacade(TypeFactory): + pass + + +class AnnotationsFacade(TypeFactory): + pass + + +class ApplicationFacade(TypeFactory): + pass + + +class ApplicationOffersFacade(TypeFactory): + pass + + +class BackupsFacade(TypeFactory): + pass + + +class BlockFacade(TypeFactory): + pass + + +class BundleFacade(TypeFactory): + pass + + +class CharmsFacade(TypeFactory): + pass + + +class ClientFacade(TypeFactory): + pass + + +class CloudFacade(TypeFactory): + pass + + +class ControllerFacade(TypeFactory): + pass + + +class CredentialManagerFacade(TypeFactory): + pass + + +class FacadeVersions(TypeFactory): + pass + + +class FirewallRulesFacade(TypeFactory): + pass + + +class HighAvailabilityFacade(TypeFactory): + pass + + +class ImageMetadataManagerFacade(TypeFactory): + pass + + +class KeyManagerFacade(TypeFactory): + pass + + +class MachineManagerFacade(TypeFactory): + pass + + +class MetricsDebugFacade(TypeFactory): + pass + + +class ModelConfigFacade(TypeFactory): + pass + + +class ModelGenerationFacade(TypeFactory): + pass + + +class ModelManagerFacade(TypeFactory): + pass + + +class ModelUpgraderFacade(TypeFactory): + pass + + +class PayloadsFacade(TypeFactory): + pass + + +class PingerFacade(TypeFactory): + pass + + +class ResourcesFacade(TypeFactory): + pass + + +class SSHClientFacade(TypeFactory): + pass + + +class SecretBackendsFacade(TypeFactory): + pass + + +class SecretsFacade(TypeFactory): + pass + + +class SpacesFacade(TypeFactory): + pass + + +class StorageFacade(TypeFactory): + pass + + +class SubnetsFacade(TypeFactory): + pass + + +class UserManagerFacade(TypeFactory): + pass diff --git a/build/lib/juju/client/_client1.py b/build/lib/juju/client/_client1.py new file mode 100644 index 00000000..1c91138c --- /dev/null +++ b/build/lib/juju/client/_client1.py @@ -0,0 +1,550 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class CredentialManagerFacade(Type): + name = "CredentialManager" + version = 1 + + @ReturnMapping(ErrorResult) + async def InvalidateModelCredential(self, reason=None): + """InvalidateModelCredential marks the cloud credential for this model as invalid. + + reason : str + Returns -> ErrorResult + """ + if reason is not None and not isinstance(reason, (bytes, str)): + raise Exception(f"Expected reason to be a str, received: {type(reason)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="CredentialManager", + request="InvalidateModelCredential", + version=1, + params=_params, + ) + _params["reason"] = reason + reply = await self.rpc(msg) + return reply + + +class FirewallRulesFacade(Type): + name = "FirewallRules" + version = 1 + + @ReturnMapping(ListFirewallRulesResults) + async def ListFirewallRules(self): + """ListFirewallRules returns all the firewall rules. + + Returns -> ListFirewallRulesResults + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="FirewallRules", request="ListFirewallRules", version=1, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetFirewallRules(self, args=None): + """SetFirewallRules creates or updates the specified firewall rules. + + args : typing.Sequence[~FirewallRule] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="FirewallRules", request="SetFirewallRules", version=1, params=_params + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + +class ImageMetadataManagerFacade(Type): + name = "ImageMetadataManager" + version = 1 + + @ReturnMapping(ErrorResults) + async def Delete(self, image_ids=None): + """Delete deletes cloud image metadata for given image ids. + It supports bulk calls. + + image_ids : typing.Sequence[str] + Returns -> ErrorResults + """ + if image_ids is not None and not isinstance(image_ids, (bytes, str, list)): + raise Exception( + f"Expected image_ids to be a Sequence, received: {type(image_ids)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ImageMetadataManager", request="Delete", version=1, params=_params + ) + _params["image-ids"] = image_ids + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ListCloudImageMetadataResult) + async def List( + self, + arches=None, + region=None, + root_storage_type=None, + stream=None, + versions=None, + virt_type=None, + ): + """List returns all found cloud image metadata that satisfy + given filter. + Returned list contains metadata ordered by priority. + + arches : typing.Sequence[str] + region : str + root_storage_type : str + stream : str + versions : typing.Sequence[str] + virt_type : str + Returns -> ListCloudImageMetadataResult + """ + if arches is not None and not isinstance(arches, (bytes, str, list)): + raise Exception( + f"Expected arches to be a Sequence, received: {type(arches)}" + ) + + if region is not None and not isinstance(region, (bytes, str)): + raise Exception(f"Expected region to be a str, received: {type(region)}") + + if root_storage_type is not None and not isinstance( + root_storage_type, (bytes, str) + ): + raise Exception( + f"Expected root_storage_type to be a str, received: {type(root_storage_type)}" + ) + + if stream is not None and not isinstance(stream, (bytes, str)): + raise Exception(f"Expected stream to be a str, received: {type(stream)}") + + if versions is not None and not isinstance(versions, (bytes, str, list)): + raise Exception( + f"Expected versions to be a Sequence, received: {type(versions)}" + ) + + if virt_type is not None and not isinstance(virt_type, (bytes, str)): + raise Exception( + f"Expected virt_type to be a str, received: {type(virt_type)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ImageMetadataManager", request="List", version=1, params=_params + ) + _params["arches"] = arches + _params["region"] = region + _params["root-storage-type"] = root_storage_type + _params["stream"] = stream + _params["versions"] = versions + _params["virt-type"] = virt_type + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Save(self, metadata=None): + """Save stores given cloud image metadata. + It supports bulk calls. + + metadata : typing.Sequence[~CloudImageMetadataList] + Returns -> ErrorResults + """ + if metadata is not None and not isinstance(metadata, (bytes, str, list)): + raise Exception( + f"Expected metadata to be a Sequence, received: {type(metadata)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ImageMetadataManager", request="Save", version=1, params=_params + ) + _params["metadata"] = metadata + reply = await self.rpc(msg) + return reply + + +class KeyManagerFacade(Type): + name = "KeyManager" + version = 1 + + @ReturnMapping(ErrorResults) + async def AddKeys(self, ssh_keys=None, user=None): + """AddKeys adds new authorised ssh keys for the specified user. + + ssh_keys : typing.Sequence[str] + user : str + Returns -> ErrorResults + """ + if ssh_keys is not None and not isinstance(ssh_keys, (bytes, str, list)): + raise Exception( + f"Expected ssh_keys to be a Sequence, received: {type(ssh_keys)}" + ) + + if user is not None and not isinstance(user, (bytes, str)): + raise Exception(f"Expected user to be a str, received: {type(user)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="KeyManager", request="AddKeys", version=1, params=_params) + _params["ssh-keys"] = ssh_keys + _params["user"] = user + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DeleteKeys(self, ssh_keys=None, user=None): + """DeleteKeys deletes the authorised ssh keys for the specified user. + + ssh_keys : typing.Sequence[str] + user : str + Returns -> ErrorResults + """ + if ssh_keys is not None and not isinstance(ssh_keys, (bytes, str, list)): + raise Exception( + f"Expected ssh_keys to be a Sequence, received: {type(ssh_keys)}" + ) + + if user is not None and not isinstance(user, (bytes, str)): + raise Exception(f"Expected user to be a str, received: {type(user)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="KeyManager", request="DeleteKeys", version=1, params=_params) + _params["ssh-keys"] = ssh_keys + _params["user"] = user + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ImportKeys(self, ssh_keys=None, user=None): + """ImportKeys imports new authorised ssh keys from the specified key ids for the specified user. + + ssh_keys : typing.Sequence[str] + user : str + Returns -> ErrorResults + """ + if ssh_keys is not None and not isinstance(ssh_keys, (bytes, str, list)): + raise Exception( + f"Expected ssh_keys to be a Sequence, received: {type(ssh_keys)}" + ) + + if user is not None and not isinstance(user, (bytes, str)): + raise Exception(f"Expected user to be a str, received: {type(user)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="KeyManager", request="ImportKeys", version=1, params=_params) + _params["ssh-keys"] = ssh_keys + _params["user"] = user + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringsResults) + async def ListKeys(self, entities=None, mode=None): + """ListKeys returns the authorised ssh keys for the specified users. + + entities : Entities + mode : bool + Returns -> StringsResults + """ + if entities is not None and not isinstance(entities, (dict, Entities)): + raise Exception( + f"Expected entities to be a Entities, received: {type(entities)}" + ) + + if mode is not None and not isinstance(mode, bool): + raise Exception(f"Expected mode to be a bool, received: {type(mode)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="KeyManager", request="ListKeys", version=1, params=_params) + _params["entities"] = entities + _params["mode"] = mode + reply = await self.rpc(msg) + return reply + + +class ModelUpgraderFacade(Type): + name = "ModelUpgrader" + version = 1 + + @ReturnMapping(None) + async def AbortModelUpgrade(self, model_tag=None): + """AbortModelUpgrade aborts and archives the model upgrade + synchronisation record, if any. + + model_tag : str + Returns -> None + """ + if model_tag is not None and not isinstance(model_tag, (bytes, str)): + raise Exception( + f"Expected model_tag to be a str, received: {type(model_tag)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelUpgrader", request="AbortModelUpgrade", version=1, params=_params + ) + _params["model-tag"] = model_tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UpgradeModelResult) + async def UpgradeModel( + self, + agent_stream=None, + dry_run=None, + ignore_agent_versions=None, + model_tag=None, + target_version=None, + ): + """UpgradeModel upgrades a model. + + agent_stream : str + dry_run : bool + ignore_agent_versions : bool + model_tag : str + target_version : Number + Returns -> UpgradeModelResult + """ + if agent_stream is not None and not isinstance(agent_stream, (bytes, str)): + raise Exception( + f"Expected agent_stream to be a str, received: {type(agent_stream)}" + ) + + if dry_run is not None and not isinstance(dry_run, bool): + raise Exception(f"Expected dry_run to be a bool, received: {type(dry_run)}") + + if ignore_agent_versions is not None and not isinstance( + ignore_agent_versions, bool + ): + raise Exception( + f"Expected ignore_agent_versions to be a bool, received: {type(ignore_agent_versions)}" + ) + + if model_tag is not None and not isinstance(model_tag, (bytes, str)): + raise Exception( + f"Expected model_tag to be a str, received: {type(model_tag)}" + ) + + if target_version is not None and not isinstance( + target_version, (dict, Number) + ): + raise Exception( + f"Expected target_version to be a Number, received: {type(target_version)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelUpgrader", request="UpgradeModel", version=1, params=_params + ) + _params["agent-stream"] = agent_stream + _params["dry-run"] = dry_run + _params["ignore-agent-versions"] = ignore_agent_versions + _params["model-tag"] = model_tag + _params["target-version"] = target_version + reply = await self.rpc(msg) + return reply + + +class PayloadsFacade(Type): + name = "Payloads" + version = 1 + + @ReturnMapping(PayloadListResults) + async def List(self, patterns=None): + """List builds the list of payloads being tracked for + the given unit and IDs. If no IDs are provided then all tracked + payloads for the unit are returned. + + patterns : typing.Sequence[str] + Returns -> PayloadListResults + """ + if patterns is not None and not isinstance(patterns, (bytes, str, list)): + raise Exception( + f"Expected patterns to be a Sequence, received: {type(patterns)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Payloads", request="List", version=1, params=_params) + _params["patterns"] = patterns + reply = await self.rpc(msg) + return reply + + +class PingerFacade(Type): + name = "Pinger" + version = 1 + + @ReturnMapping(None) + async def Ping(self): + """Returns -> None""" + # map input types to rpc msg + _params = dict() + msg = dict(type="Pinger", request="Ping", version=1, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def Stop(self): + """Returns -> None""" + # map input types to rpc msg + _params = dict() + msg = dict(type="Pinger", request="Stop", version=1, params=_params) + + reply = await self.rpc(msg) + return reply + + +class SecretBackendsFacade(Type): + name = "SecretBackends" + version = 1 + + @ReturnMapping(ErrorResults) + async def AddSecretBackends(self, args=None): + """AddSecretBackends adds new secret backends. + + args : typing.Sequence[~AddSecretBackendArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="SecretBackends", + request="AddSecretBackends", + version=1, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ListSecretBackendsResults) + async def ListSecretBackends(self, names=None, reveal=None): + """ListSecretBackends lists available secret backends. + + names : typing.Sequence[str] + reveal : bool + Returns -> ListSecretBackendsResults + """ + if names is not None and not isinstance(names, (bytes, str, list)): + raise Exception(f"Expected names to be a Sequence, received: {type(names)}") + + if reveal is not None and not isinstance(reveal, bool): + raise Exception(f"Expected reveal to be a bool, received: {type(reveal)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="SecretBackends", + request="ListSecretBackends", + version=1, + params=_params, + ) + _params["names"] = names + _params["reveal"] = reveal + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def RemoveSecretBackends(self, args=None): + """RemoveSecretBackends removes secret backends. + + args : typing.Sequence[~RemoveSecretBackendArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="SecretBackends", + request="RemoveSecretBackends", + version=1, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UpdateSecretBackends(self, args=None): + """UpdateSecretBackends updates secret backends. + + args : typing.Sequence[~UpdateSecretBackendArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="SecretBackends", + request="UpdateSecretBackends", + version=1, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + +class SecretsFacade(Type): + name = "Secrets" + version = 1 + + @ReturnMapping(ListSecretResults) + async def ListSecrets(self, filter_=None, show_secrets=None): + """ListSecrets lists available secrets. + + filter_ : SecretsFilter + show_secrets : bool + Returns -> ListSecretResults + """ + if filter_ is not None and not isinstance(filter_, (dict, SecretsFilter)): + raise Exception( + f"Expected filter_ to be a SecretsFilter, received: {type(filter_)}" + ) + + if show_secrets is not None and not isinstance(show_secrets, bool): + raise Exception( + f"Expected show_secrets to be a bool, received: {type(show_secrets)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Secrets", request="ListSecrets", version=1, params=_params) + _params["filter"] = filter_ + _params["show-secrets"] = show_secrets + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client10.py b/build/lib/juju/client/_client10.py new file mode 100644 index 00000000..2de5c721 --- /dev/null +++ b/build/lib/juju/client/_client10.py @@ -0,0 +1,674 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class MachineManagerFacade(Type): + name = "MachineManager" + version = 10 + + @ReturnMapping(AddMachinesResults) + async def AddMachines(self, params=None): + """AddMachines adds new machines with the supplied parameters. + The args will contain Base info. + + params : typing.Sequence[~AddMachineParams] + Returns -> AddMachinesResults + """ + if params is not None and not isinstance(params, (bytes, str, list)): + raise Exception( + f"Expected params to be a Sequence, received: {type(params)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", request="AddMachines", version=10, params=_params + ) + _params["params"] = params + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DestroyMachineResults) + async def DestroyMachineWithParams( + self, dry_run=None, force=None, keep=None, machine_tags=None, max_wait=None + ): + """DestroyMachineWithParams removes a set of machines from the model. + + dry_run : bool + force : bool + keep : bool + machine_tags : typing.Sequence[str] + max_wait : int + Returns -> DestroyMachineResults + """ + if dry_run is not None and not isinstance(dry_run, bool): + raise Exception(f"Expected dry_run to be a bool, received: {type(dry_run)}") + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if keep is not None and not isinstance(keep, bool): + raise Exception(f"Expected keep to be a bool, received: {type(keep)}") + + if machine_tags is not None and not isinstance( + machine_tags, (bytes, str, list) + ): + raise Exception( + f"Expected machine_tags to be a Sequence, received: {type(machine_tags)}" + ) + + if max_wait is not None and not isinstance(max_wait, int): + raise Exception( + f"Expected max_wait to be a int, received: {type(max_wait)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", + request="DestroyMachineWithParams", + version=10, + params=_params, + ) + _params["dry-run"] = dry_run + _params["force"] = force + _params["keep"] = keep + _params["machine-tags"] = machine_tags + _params["max-wait"] = max_wait + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringsResults) + async def GetUpgradeSeriesMessages(self, params=None): + """GetUpgradeSeriesMessages returns all new messages associated with upgrade + series events. Messages that have already been retrieved once are not + returned by this method. + + params : typing.Sequence[~UpgradeSeriesNotificationParam] + Returns -> StringsResults + """ + if params is not None and not isinstance(params, (bytes, str, list)): + raise Exception( + f"Expected params to be a Sequence, received: {type(params)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", + request="GetUpgradeSeriesMessages", + version=10, + params=_params, + ) + _params["params"] = params + reply = await self.rpc(msg) + return reply + + @ReturnMapping(InstanceTypesResults) + async def InstanceTypes(self, constraints=None): + """InstanceTypes returns instance type information for the cloud and region + in which the current model is deployed. + + constraints : typing.Sequence[~ModelInstanceTypesConstraint] + Returns -> InstanceTypesResults + """ + if constraints is not None and not isinstance(constraints, (bytes, str, list)): + raise Exception( + f"Expected constraints to be a Sequence, received: {type(constraints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", request="InstanceTypes", version=10, params=_params + ) + _params["constraints"] = constraints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ProvisioningScriptResult) + async def ProvisioningScript( + self, data_dir=None, disable_package_commands=None, machine_id=None, nonce=None + ): + """ProvisioningScript returns a shell script that, when run, + provisions a machine agent on the machine executing the script. + + data_dir : str + disable_package_commands : bool + machine_id : str + nonce : str + Returns -> ProvisioningScriptResult + """ + if data_dir is not None and not isinstance(data_dir, (bytes, str)): + raise Exception( + f"Expected data_dir to be a str, received: {type(data_dir)}" + ) + + if disable_package_commands is not None and not isinstance( + disable_package_commands, bool + ): + raise Exception( + f"Expected disable_package_commands to be a bool, received: {type(disable_package_commands)}" + ) + + if machine_id is not None and not isinstance(machine_id, (bytes, str)): + raise Exception( + f"Expected machine_id to be a str, received: {type(machine_id)}" + ) + + if nonce is not None and not isinstance(nonce, (bytes, str)): + raise Exception(f"Expected nonce to be a str, received: {type(nonce)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", + request="ProvisioningScript", + version=10, + params=_params, + ) + _params["data-dir"] = data_dir + _params["disable-package-commands"] = disable_package_commands + _params["machine-id"] = machine_id + _params["nonce"] = nonce + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def RetryProvisioning(self, all_=None, machines=None): + """RetryProvisioning marks a provisioning error as transient on the machines. + + all_ : bool + machines : typing.Sequence[str] + Returns -> ErrorResults + """ + if all_ is not None and not isinstance(all_, bool): + raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") + + if machines is not None and not isinstance(machines, (bytes, str, list)): + raise Exception( + f"Expected machines to be a Sequence, received: {type(machines)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", + request="RetryProvisioning", + version=10, + params=_params, + ) + _params["all"] = all_ + _params["machines"] = machines + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResult) + async def UpgradeSeriesComplete(self, channel=None, force=None, tag=None): + """UpgradeSeriesComplete marks a machine as having completed a managed series + upgrade. + + channel : str + force : bool + tag : Entity + Returns -> ErrorResult + """ + if channel is not None and not isinstance(channel, (bytes, str)): + raise Exception(f"Expected channel to be a str, received: {type(channel)}") + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if tag is not None and not isinstance(tag, (dict, Entity)): + raise Exception(f"Expected tag to be a Entity, received: {type(tag)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", + request="UpgradeSeriesComplete", + version=10, + params=_params, + ) + _params["channel"] = channel + _params["force"] = force + _params["tag"] = tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResult) + async def UpgradeSeriesPrepare(self, channel=None, force=None, tag=None): + """UpgradeSeriesPrepare prepares a machine for a OS series upgrade. + + channel : str + force : bool + tag : Entity + Returns -> ErrorResult + """ + if channel is not None and not isinstance(channel, (bytes, str)): + raise Exception(f"Expected channel to be a str, received: {type(channel)}") + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if tag is not None and not isinstance(tag, (dict, Entity)): + raise Exception(f"Expected tag to be a Entity, received: {type(tag)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", + request="UpgradeSeriesPrepare", + version=10, + params=_params, + ) + _params["channel"] = channel + _params["force"] = force + _params["tag"] = tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UpgradeSeriesUnitsResults) + async def UpgradeSeriesValidate(self, args=None): + """UpgradeSeriesValidate validates that the incoming arguments correspond to a + valid series upgrade for the target machine. + If they do, a list of the machine's current units is returned for use in + soliciting user confirmation of the command. + + args : typing.Sequence[~UpdateChannelArg] + Returns -> UpgradeSeriesUnitsResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", + request="UpgradeSeriesValidate", + version=10, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(NotifyWatchResults) + async def WatchUpgradeSeriesNotifications(self, entities=None): + """WatchUpgradeSeriesNotifications returns a watcher that fires on upgrade + series events. + + entities : typing.Sequence[~Entity] + Returns -> NotifyWatchResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MachineManager", + request="WatchUpgradeSeriesNotifications", + version=10, + params=_params, + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + +class ModelManagerFacade(Type): + name = "ModelManager" + version = 10 + + @ReturnMapping(ErrorResults) + async def ChangeModelCredential(self, model_credentials=None): + """ChangeModelCredential changes cloud credential reference for models. + These new cloud credentials must already exist on the controller. + + model_credentials : typing.Sequence[~ChangeModelCredentialParams] + Returns -> ErrorResults + """ + if model_credentials is not None and not isinstance( + model_credentials, (bytes, str, list) + ): + raise Exception( + f"Expected model_credentials to be a Sequence, received: {type(model_credentials)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", + request="ChangeModelCredential", + version=10, + params=_params, + ) + _params["model-credentials"] = model_credentials + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelInfo) + async def CreateModel( + self, + cloud_tag=None, + config=None, + credential=None, + name=None, + owner_tag=None, + region=None, + ): + """CreateModel creates a new model using the account and + model config specified in the args. + + cloud_tag : str + config : typing.Mapping[str, typing.Any] + credential : str + name : str + owner_tag : str + region : str + Returns -> ModelInfo + """ + if cloud_tag is not None and not isinstance(cloud_tag, (bytes, str)): + raise Exception( + f"Expected cloud_tag to be a str, received: {type(cloud_tag)}" + ) + + if config is not None and not isinstance(config, dict): + raise Exception( + f"Expected config to be a Mapping, received: {type(config)}" + ) + + if credential is not None and not isinstance(credential, (bytes, str)): + raise Exception( + f"Expected credential to be a str, received: {type(credential)}" + ) + + if name is not None and not isinstance(name, (bytes, str)): + raise Exception(f"Expected name to be a str, received: {type(name)}") + + if owner_tag is not None and not isinstance(owner_tag, (bytes, str)): + raise Exception( + f"Expected owner_tag to be a str, received: {type(owner_tag)}" + ) + + if region is not None and not isinstance(region, (bytes, str)): + raise Exception(f"Expected region to be a str, received: {type(region)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="CreateModel", version=10, params=_params + ) + _params["cloud-tag"] = cloud_tag + _params["config"] = config + _params["credential"] = credential + _params["name"] = name + _params["owner-tag"] = owner_tag + _params["region"] = region + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DestroyModels(self, models=None): + """DestroyModels will try to destroy the specified models. + If there is a block on destruction, this method will return an error. + From ModelManager v7 onwards, DestroyModels gains 'force' and 'max-wait' parameters. + + models : typing.Sequence[~DestroyModelParams] + Returns -> ErrorResults + """ + if models is not None and not isinstance(models, (bytes, str, list)): + raise Exception( + f"Expected models to be a Sequence, received: {type(models)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="DestroyModels", version=10, params=_params + ) + _params["models"] = models + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResults) + async def DumpModels(self, entities=None, simplified=None): + """DumpModels will export the models into the database agnostic + representation. The user needs to either be a controller admin, or have + admin privileges on the model itself. + + entities : typing.Sequence[~Entity] + simplified : bool + Returns -> StringResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + if simplified is not None and not isinstance(simplified, bool): + raise Exception( + f"Expected simplified to be a bool, received: {type(simplified)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="DumpModels", version=10, params=_params + ) + _params["entities"] = entities + _params["simplified"] = simplified + reply = await self.rpc(msg) + return reply + + @ReturnMapping(MapResults) + async def DumpModelsDB(self, entities=None): + """DumpModelsDB will gather all documents from all model collections + for the specified model. The map result contains a map of collection + names to lists of documents represented as maps. + + entities : typing.Sequence[~Entity] + Returns -> MapResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="DumpModelsDB", version=10, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelSummaryResults) + async def ListModelSummaries(self, all_=None, user_tag=None): + """ListModelSummaries returns models that the specified user + has access to in the current server. Controller admins (superuser) + can list models for any user. Other users + can only ask about their own models. + + all_ : bool + user_tag : str + Returns -> ModelSummaryResults + """ + if all_ is not None and not isinstance(all_, bool): + raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") + + if user_tag is not None and not isinstance(user_tag, (bytes, str)): + raise Exception( + f"Expected user_tag to be a str, received: {type(user_tag)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", + request="ListModelSummaries", + version=10, + params=_params, + ) + _params["all"] = all_ + _params["user-tag"] = user_tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UserModelList) + async def ListModels(self, tag=None): + """ListModels returns the models that the specified user + has access to in the current server. Controller admins (superuser) + can list models for any user. Other users + can only ask about their own models. + + tag : str + Returns -> UserModelList + """ + if tag is not None and not isinstance(tag, (bytes, str)): + raise Exception(f"Expected tag to be a str, received: {type(tag)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="ListModels", version=10, params=_params + ) + _params["tag"] = tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelDefaultsResults) + async def ModelDefaultsForClouds(self, entities=None): + """ModelDefaultsForClouds returns the default config values for the specified + clouds. + + entities : typing.Sequence[~Entity] + Returns -> ModelDefaultsResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", + request="ModelDefaultsForClouds", + version=10, + params=_params, + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelInfoResults) + async def ModelInfo(self, entities=None): + """ModelInfo returns information about the specified models. + + entities : typing.Sequence[~Entity] + Returns -> ModelInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelManager", request="ModelInfo", version=10, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelStatusResults) + async def ModelStatus(self, entities=None): + """ModelStatus returns a summary of the model. + + entities : typing.Sequence[~Entity] + Returns -> ModelStatusResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="ModelStatus", version=10, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ModifyModelAccess(self, changes=None): + """ModifyModelAccess changes the model access granted to users. + + changes : typing.Sequence[~ModifyModelAccess] + Returns -> ErrorResults + """ + if changes is not None and not isinstance(changes, (bytes, str, list)): + raise Exception( + f"Expected changes to be a Sequence, received: {type(changes)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="ModifyModelAccess", version=10, params=_params + ) + _params["changes"] = changes + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetModelDefaults(self, config=None): + """SetModelDefaults writes new values for the specified default model settings. + + config : typing.Sequence[~ModelDefaultValues] + Returns -> ErrorResults + """ + if config is not None and not isinstance(config, (bytes, str, list)): + raise Exception( + f"Expected config to be a Sequence, received: {type(config)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="SetModelDefaults", version=10, params=_params + ) + _params["config"] = config + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UnsetModelDefaults(self, keys=None): + """UnsetModelDefaults removes the specified default model settings. + + keys : typing.Sequence[~ModelUnsetKeys] + Returns -> ErrorResults + """ + if keys is not None and not isinstance(keys, (bytes, str, list)): + raise Exception(f"Expected keys to be a Sequence, received: {type(keys)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", + request="UnsetModelDefaults", + version=10, + params=_params, + ) + _params["keys"] = keys + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client11.py b/build/lib/juju/client/_client11.py new file mode 100644 index 00000000..c1f00f6b --- /dev/null +++ b/build/lib/juju/client/_client11.py @@ -0,0 +1,481 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class ControllerFacade(Type): + name = "Controller" + version = 11 + + @ReturnMapping(UserModelList) + async def AllModels(self): + """AllModels allows controller administrators to get the list of all the + models in the controller. + + Returns -> UserModelList + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Controller", request="AllModels", version=11, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CloudSpecResults) + async def CloudSpec(self, entities=None): + """CloudSpec returns the model's cloud spec. + + entities : typing.Sequence[~Entity] + Returns -> CloudSpecResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Controller", request="CloudSpec", version=11, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def ConfigSet(self, config=None): + """ConfigSet changes the value of specified controller configuration + settings. Only some settings can be changed after bootstrap. + Settings that aren't specified in the params are left unchanged. + + config : typing.Mapping[str, typing.Any] + Returns -> None + """ + if config is not None and not isinstance(config, dict): + raise Exception( + f"Expected config to be a Mapping, received: {type(config)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Controller", request="ConfigSet", version=11, params=_params) + _params["config"] = config + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ControllerAPIInfoResults) + async def ControllerAPIInfoForModels(self, entities=None): + """ControllerAPIInfoForModels returns the controller api connection details for the specified models. + + entities : typing.Sequence[~Entity] + Returns -> ControllerAPIInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="ControllerAPIInfoForModels", + version=11, + params=_params, + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ControllerConfigResult) + async def ControllerConfig(self): + """ControllerConfig returns the controller's configuration. + + Returns -> ControllerConfigResult + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="ControllerConfig", version=11, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ControllerVersionResults) + async def ControllerVersion(self): + """ControllerVersion returns the version information associated with this + controller binary. + + NOTE: the implementation intentionally does not check for SuperuserAccess + as the Version is known even to users with login access. + + Returns -> ControllerVersionResults + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="ControllerVersion", version=11, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DashboardConnectionInfo) + async def DashboardConnectionInfo(self): + """DashboardConnectionInfo returns the connection information for a client to + connect to the Juju Dashboard including any proxying information. + + Returns -> DashboardConnectionInfo + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="DashboardConnectionInfo", + version=11, + params=_params, + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def DestroyController( + self, + destroy_models=None, + destroy_storage=None, + force=None, + max_wait=None, + model_timeout=None, + ): + """DestroyController destroys the controller. + + If the args specify the destruction of the models, this method will + attempt to do so. Otherwise, if the controller has any non-empty, + non-Dead hosted models, then an error with the code + params.CodeHasHostedModels will be transmitted. + + destroy_models : bool + destroy_storage : bool + force : bool + max_wait : int + model_timeout : int + Returns -> None + """ + if destroy_models is not None and not isinstance(destroy_models, bool): + raise Exception( + f"Expected destroy_models to be a bool, received: {type(destroy_models)}" + ) + + if destroy_storage is not None and not isinstance(destroy_storage, bool): + raise Exception( + f"Expected destroy_storage to be a bool, received: {type(destroy_storage)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if max_wait is not None and not isinstance(max_wait, int): + raise Exception( + f"Expected max_wait to be a int, received: {type(max_wait)}" + ) + + if model_timeout is not None and not isinstance(model_timeout, int): + raise Exception( + f"Expected model_timeout to be a int, received: {type(model_timeout)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="DestroyController", version=11, params=_params + ) + _params["destroy-models"] = destroy_models + _params["destroy-storage"] = destroy_storage + _params["force"] = force + _params["max-wait"] = max_wait + _params["model-timeout"] = model_timeout + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CloudSpecResult) + async def GetCloudSpec(self): + """GetCloudSpec constructs the CloudSpec for a validated and authorized model. + + Returns -> CloudSpecResult + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="GetCloudSpec", version=11, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UserAccessResults) + async def GetControllerAccess(self, entities=None): + """GetControllerAccess returns the level of access the specified users + have on the controller. + + entities : typing.Sequence[~Entity] + Returns -> UserAccessResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="GetControllerAccess", version=11, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(HostedModelConfigsResults) + async def HostedModelConfigs(self): + """HostedModelConfigs returns all the information that the client needs in + order to connect directly with the host model's provider and destroy it + directly. + + Returns -> HostedModelConfigsResults + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="HostedModelConfigs", version=11, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResult) + async def IdentityProviderURL(self): + """IdentityProviderURL returns the URL of the configured external identity + provider for this controller or an empty string if no external identity + provider has been configured when the controller was bootstrapped. + + NOTE: the implementation intentionally does not check for SuperuserAccess + as the URL is known even to users with login access. + + Returns -> StringResult + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="IdentityProviderURL", version=11, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(InitiateMigrationResults) + async def InitiateMigration(self, specs=None): + """InitiateMigration attempts to begin the migration of one or + more models to other controllers. + + specs : typing.Sequence[~MigrationSpec] + Returns -> InitiateMigrationResults + """ + if specs is not None and not isinstance(specs, (bytes, str, list)): + raise Exception(f"Expected specs to be a Sequence, received: {type(specs)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="InitiateMigration", version=11, params=_params + ) + _params["specs"] = specs + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelBlockInfoList) + async def ListBlockedModels(self): + """ListBlockedModels returns a list of all models on the controller + which have a block in place. The resulting slice is sorted by model + name, then owner. Callers must be controller administrators to retrieve the + list. + + Returns -> ModelBlockInfoList + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="ListBlockedModels", version=11, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelConfigResults) + async def ModelConfig(self): + """ModelConfig returns the model config for the controller + model. For information on the current model, use + client.ModelGet + + Returns -> ModelConfigResults + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Controller", request="ModelConfig", version=11, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelStatusResults) + async def ModelStatus(self, entities=None): + """ModelStatus returns a summary of the model. + + entities : typing.Sequence[~Entity] + Returns -> ModelStatusResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Controller", request="ModelStatus", version=11, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ModifyControllerAccess(self, changes=None): + """ModifyControllerAccess changes the model access granted to users. + + changes : typing.Sequence[~ModifyControllerAccess] + Returns -> ErrorResults + """ + if changes is not None and not isinstance(changes, (bytes, str, list)): + raise Exception( + f"Expected changes to be a Sequence, received: {type(changes)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="ModifyControllerAccess", + version=11, + params=_params, + ) + _params["changes"] = changes + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResult) + async def MongoVersion(self): + """MongoVersion allows the introspection of the mongo version per controller + + Returns -> StringResult + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="MongoVersion", version=11, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def RemoveBlocks(self, all_=None): + """RemoveBlocks removes all the blocks in the controller. + + all_ : bool + Returns -> None + """ + if all_ is not None and not isinstance(all_, bool): + raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="RemoveBlocks", version=11, params=_params + ) + _params["all"] = all_ + reply = await self.rpc(msg) + return reply + + @ReturnMapping(SummaryWatcherID) + async def WatchAllModelSummaries(self): + """WatchAllModelSummaries starts watching the summary updates from the cache. + This method is superuser access only, and watches all models in the + controller. + + Returns -> SummaryWatcherID + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="WatchAllModelSummaries", + version=11, + params=_params, + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(AllWatcherId) + async def WatchAllModels(self): + """WatchAllModels starts watching events for all models in the + controller. The returned AllWatcherId should be used with Next on the + AllModelWatcher endpoint to receive deltas. + + Returns -> AllWatcherId + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="WatchAllModels", version=11, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(NotifyWatchResults) + async def WatchCloudSpecsChanges(self, entities=None): + """WatchCloudSpecsChanges returns a watcher for cloud spec changes. + + entities : typing.Sequence[~Entity] + Returns -> NotifyWatchResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="WatchCloudSpecsChanges", + version=11, + params=_params, + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(SummaryWatcherID) + async def WatchModelSummaries(self): + """WatchModelSummaries starts watching the summary updates from the cache. + Only models that the user has access to are returned. + + Returns -> SummaryWatcherID + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="WatchModelSummaries", version=11, params=_params + ) + + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client12.py b/build/lib/juju/client/_client12.py new file mode 100644 index 00000000..55624321 --- /dev/null +++ b/build/lib/juju/client/_client12.py @@ -0,0 +1,466 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class ControllerFacade(Type): + name = "Controller" + version = 12 + + @ReturnMapping(UserModelList) + async def AllModels(self): + """AllModels allows controller administrators to get the list of all the + models in the controller. + + Returns -> UserModelList + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Controller", request="AllModels", version=12, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CloudSpecResults) + async def CloudSpec(self, entities=None): + """CloudSpec returns the model's cloud spec. + + entities : typing.Sequence[~Entity] + Returns -> CloudSpecResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Controller", request="CloudSpec", version=12, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def ConfigSet(self, config=None): + """ConfigSet changes the value of specified controller configuration + settings. Only some settings can be changed after bootstrap. + Settings that aren't specified in the params are left unchanged. + + config : typing.Mapping[str, typing.Any] + Returns -> None + """ + if config is not None and not isinstance(config, dict): + raise Exception( + f"Expected config to be a Mapping, received: {type(config)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Controller", request="ConfigSet", version=12, params=_params) + _params["config"] = config + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ControllerAPIInfoResults) + async def ControllerAPIInfoForModels(self, entities=None): + """ControllerAPIInfoForModels returns the controller api connection details for the specified models. + + entities : typing.Sequence[~Entity] + Returns -> ControllerAPIInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="ControllerAPIInfoForModels", + version=12, + params=_params, + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ControllerConfigResult) + async def ControllerConfig(self): + """ControllerConfig returns the controller's configuration. + + Returns -> ControllerConfigResult + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="ControllerConfig", version=12, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ControllerVersionResults) + async def ControllerVersion(self): + """ControllerVersion returns the version information associated with this + controller binary. + + NOTE: the implementation intentionally does not check for SuperuserAccess + as the Version is known even to users with login access. + + Returns -> ControllerVersionResults + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="ControllerVersion", version=12, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DashboardConnectionInfo) + async def DashboardConnectionInfo(self): + """DashboardConnectionInfo returns the connection information for a client to + connect to the Juju Dashboard including any proxying information. + + Returns -> DashboardConnectionInfo + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="DashboardConnectionInfo", + version=12, + params=_params, + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def DestroyController( + self, + destroy_models=None, + destroy_storage=None, + force=None, + max_wait=None, + model_timeout=None, + ): + """DestroyController destroys the controller. + + If the args specify the destruction of the models, this method will + attempt to do so. Otherwise, if the controller has any non-empty, + non-Dead hosted models, then an error with the code + params.CodeHasHostedModels will be transmitted. + + destroy_models : bool + destroy_storage : bool + force : bool + max_wait : int + model_timeout : int + Returns -> None + """ + if destroy_models is not None and not isinstance(destroy_models, bool): + raise Exception( + f"Expected destroy_models to be a bool, received: {type(destroy_models)}" + ) + + if destroy_storage is not None and not isinstance(destroy_storage, bool): + raise Exception( + f"Expected destroy_storage to be a bool, received: {type(destroy_storage)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if max_wait is not None and not isinstance(max_wait, int): + raise Exception( + f"Expected max_wait to be a int, received: {type(max_wait)}" + ) + + if model_timeout is not None and not isinstance(model_timeout, int): + raise Exception( + f"Expected model_timeout to be a int, received: {type(model_timeout)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="DestroyController", version=12, params=_params + ) + _params["destroy-models"] = destroy_models + _params["destroy-storage"] = destroy_storage + _params["force"] = force + _params["max-wait"] = max_wait + _params["model-timeout"] = model_timeout + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CloudSpecResult) + async def GetCloudSpec(self): + """GetCloudSpec constructs the CloudSpec for a validated and authorized model. + + Returns -> CloudSpecResult + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="GetCloudSpec", version=12, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UserAccessResults) + async def GetControllerAccess(self, entities=None): + """GetControllerAccess returns the level of access the specified users + have on the controller. + + entities : typing.Sequence[~Entity] + Returns -> UserAccessResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="GetControllerAccess", version=12, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(HostedModelConfigsResults) + async def HostedModelConfigs(self): + """HostedModelConfigs returns all the information that the client needs in + order to connect directly with the host model's provider and destroy it + directly. + + Returns -> HostedModelConfigsResults + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="HostedModelConfigs", version=12, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResult) + async def IdentityProviderURL(self): + """IdentityProviderURL returns the URL of the configured external identity + provider for this controller or an empty string if no external identity + provider has been configured when the controller was bootstrapped. + + NOTE: the implementation intentionally does not check for SuperuserAccess + as the URL is known even to users with login access. + + Returns -> StringResult + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="IdentityProviderURL", version=12, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(InitiateMigrationResults) + async def InitiateMigration(self, specs=None): + """InitiateMigration attempts to begin the migration of one or + more models to other controllers. + + specs : typing.Sequence[~MigrationSpec] + Returns -> InitiateMigrationResults + """ + if specs is not None and not isinstance(specs, (bytes, str, list)): + raise Exception(f"Expected specs to be a Sequence, received: {type(specs)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="InitiateMigration", version=12, params=_params + ) + _params["specs"] = specs + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelBlockInfoList) + async def ListBlockedModels(self): + """ListBlockedModels returns a list of all models on the controller + which have a block in place. The resulting slice is sorted by model + name, then owner. Callers must be controller administrators to retrieve the + list. + + Returns -> ModelBlockInfoList + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="ListBlockedModels", version=12, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelStatusResults) + async def ModelStatus(self, entities=None): + """ModelStatus returns a summary of the model. + + entities : typing.Sequence[~Entity] + Returns -> ModelStatusResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Controller", request="ModelStatus", version=12, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ModifyControllerAccess(self, changes=None): + """ModifyControllerAccess changes the model access granted to users. + + changes : typing.Sequence[~ModifyControllerAccess] + Returns -> ErrorResults + """ + if changes is not None and not isinstance(changes, (bytes, str, list)): + raise Exception( + f"Expected changes to be a Sequence, received: {type(changes)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="ModifyControllerAccess", + version=12, + params=_params, + ) + _params["changes"] = changes + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResult) + async def MongoVersion(self): + """MongoVersion allows the introspection of the mongo version per controller + + Returns -> StringResult + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="MongoVersion", version=12, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def RemoveBlocks(self, all_=None): + """RemoveBlocks removes all the blocks in the controller. + + all_ : bool + Returns -> None + """ + if all_ is not None and not isinstance(all_, bool): + raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="RemoveBlocks", version=12, params=_params + ) + _params["all"] = all_ + reply = await self.rpc(msg) + return reply + + @ReturnMapping(SummaryWatcherID) + async def WatchAllModelSummaries(self): + """WatchAllModelSummaries starts watching the summary updates from the cache. + This method is superuser access only, and watches all models in the + controller. + + Returns -> SummaryWatcherID + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="WatchAllModelSummaries", + version=12, + params=_params, + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(AllWatcherId) + async def WatchAllModels(self): + """WatchAllModels starts watching events for all models in the + controller. The returned AllWatcherId should be used with Next on the + AllModelWatcher endpoint to receive deltas. + + Returns -> AllWatcherId + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="WatchAllModels", version=12, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(NotifyWatchResults) + async def WatchCloudSpecsChanges(self, entities=None): + """WatchCloudSpecsChanges returns a watcher for cloud spec changes. + + entities : typing.Sequence[~Entity] + Returns -> NotifyWatchResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", + request="WatchCloudSpecsChanges", + version=12, + params=_params, + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(SummaryWatcherID) + async def WatchModelSummaries(self): + """WatchModelSummaries starts watching the summary updates from the cache. + Only models that the user has access to are returned. + + Returns -> SummaryWatcherID + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Controller", request="WatchModelSummaries", version=12, params=_params + ) + + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client17.py b/build/lib/juju/client/_client17.py new file mode 100644 index 00000000..50ecbcf6 --- /dev/null +++ b/build/lib/juju/client/_client17.py @@ -0,0 +1,817 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class ApplicationFacade(Type): + name = "Application" + version = 17 + + @ReturnMapping(AddRelationResults) + async def AddRelation(self, endpoints=None, via_cidrs=None): + """AddRelation adds a relation between the specified endpoints and returns the relation info. + + endpoints : typing.Sequence[str] + via_cidrs : typing.Sequence[str] + Returns -> AddRelationResults + """ + if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): + raise Exception( + f"Expected endpoints to be a Sequence, received: {type(endpoints)}" + ) + + if via_cidrs is not None and not isinstance(via_cidrs, (bytes, str, list)): + raise Exception( + f"Expected via_cidrs to be a Sequence, received: {type(via_cidrs)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="AddRelation", version=17, params=_params + ) + _params["endpoints"] = endpoints + _params["via-cidrs"] = via_cidrs + reply = await self.rpc(msg) + return reply + + @ReturnMapping(AddApplicationUnitsResults) + async def AddUnits( + self, + application=None, + attach_storage=None, + num_units=None, + placement=None, + policy=None, + ): + """AddUnits adds a given number of units to an application. + + application : str + attach_storage : typing.Sequence[str] + num_units : int + placement : typing.Sequence[~Placement] + policy : str + Returns -> AddApplicationUnitsResults + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if attach_storage is not None and not isinstance( + attach_storage, (bytes, str, list) + ): + raise Exception( + f"Expected attach_storage to be a Sequence, received: {type(attach_storage)}" + ) + + if num_units is not None and not isinstance(num_units, int): + raise Exception( + f"Expected num_units to be a int, received: {type(num_units)}" + ) + + if placement is not None and not isinstance(placement, (bytes, str, list)): + raise Exception( + f"Expected placement to be a Sequence, received: {type(placement)}" + ) + + if policy is not None and not isinstance(policy, (bytes, str)): + raise Exception(f"Expected policy to be a str, received: {type(policy)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="AddUnits", version=17, params=_params) + _params["application"] = application + _params["attach-storage"] = attach_storage + _params["num-units"] = num_units + _params["placement"] = placement + _params["policy"] = policy + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationInfoResults) + async def ApplicationsInfo(self, entities=None): + """ApplicationsInfo returns applications information. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="ApplicationsInfo", version=17, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetConfigResults) + async def CharmConfig(self, args=None): + """CharmConfig returns charm config for the input list of applications and + model generations. + + args : typing.Sequence[~ApplicationGet] + Returns -> ApplicationGetConfigResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="CharmConfig", version=17, params=_params + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationCharmRelationsResults) + async def CharmRelations(self, application=None): + """CharmRelations implements the server side of Application.CharmRelations. + + application : str + Returns -> ApplicationCharmRelationsResults + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="CharmRelations", version=17, params=_params + ) + _params["application"] = application + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Consume(self, args=None): + """Consume adds remote applications to the model without creating any + relations. + + args : typing.Sequence[~ConsumeApplicationArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Consume", version=17, params=_params) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Deploy(self, applications=None): + """Deploy fetches the charms from the charm store and deploys them + using the specified placement directives. + + applications : typing.Sequence[~ApplicationDeploy] + Returns -> ErrorResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Deploy", version=17, params=_params) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DestroyApplicationResults) + async def DestroyApplication(self, applications=None): + """DestroyApplication removes a given set of applications. + + applications : typing.Sequence[~DestroyApplicationParams] + Returns -> DestroyApplicationResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="DestroyApplication", version=17, params=_params + ) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DestroyConsumedApplications(self, applications=None): + """DestroyConsumedApplications removes a given set of consumed (remote) applications. + + applications : typing.Sequence[~DestroyConsumedApplicationParams] + Returns -> ErrorResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="DestroyConsumedApplications", + version=17, + params=_params, + ) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def DestroyRelation( + self, endpoints=None, force=None, max_wait=None, relation_id=None + ): + """DestroyRelation removes the relation between the + specified endpoints or an id. + + endpoints : typing.Sequence[str] + force : bool + max_wait : int + relation_id : int + Returns -> None + """ + if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): + raise Exception( + f"Expected endpoints to be a Sequence, received: {type(endpoints)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if max_wait is not None and not isinstance(max_wait, int): + raise Exception( + f"Expected max_wait to be a int, received: {type(max_wait)}" + ) + + if relation_id is not None and not isinstance(relation_id, int): + raise Exception( + f"Expected relation_id to be a int, received: {type(relation_id)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="DestroyRelation", version=17, params=_params + ) + _params["endpoints"] = endpoints + _params["force"] = force + _params["max-wait"] = max_wait + _params["relation-id"] = relation_id + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DestroyUnitResults) + async def DestroyUnit(self, units=None): + """DestroyUnit removes a given set of application units. + + units : typing.Sequence[~DestroyUnitParams] + Returns -> DestroyUnitResults + """ + if units is not None and not isinstance(units, (bytes, str, list)): + raise Exception(f"Expected units to be a Sequence, received: {type(units)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="DestroyUnit", version=17, params=_params + ) + _params["units"] = units + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def Expose(self, application=None, exposed_endpoints=None): + """Expose changes the juju-managed firewall to expose any ports that + were also explicitly marked by units as open. + + application : str + exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if exposed_endpoints is not None and not isinstance(exposed_endpoints, dict): + raise Exception( + f"Expected exposed_endpoints to be a Mapping, received: {type(exposed_endpoints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Expose", version=17, params=_params) + _params["application"] = application + _params["exposed-endpoints"] = exposed_endpoints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetResults) + async def Get(self, application=None, branch=None): + """Get returns the charm configuration for an application. + + application : str + branch : str + Returns -> ApplicationGetResults + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Get", version=17, params=_params) + _params["application"] = application + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CharmURLOriginResult) + async def GetCharmURLOrigin(self, application=None, branch=None): + """GetCharmURLOrigin returns the charm URL and charm origin the given + application is running at present. + + application : str + branch : str + Returns -> CharmURLOriginResult + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="GetCharmURLOrigin", version=17, params=_params + ) + _params["application"] = application + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetConfigResults) + async def GetConfig(self, entities=None): + """GetConfig returns the charm config for each of the input applications. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationGetConfigResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="GetConfig", version=17, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetConstraintsResults) + async def GetConstraints(self, entities=None): + """GetConstraints returns the constraints for a given application. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationGetConstraintsResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="GetConstraints", version=17, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResult) + async def Leader(self, tag=None): + """Leader returns the unit name of the leader for the given application. + + tag : str + Returns -> StringResult + """ + if tag is not None and not isinstance(tag, (bytes, str)): + raise Exception(f"Expected tag to be a str, received: {type(tag)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Leader", version=17, params=_params) + _params["tag"] = tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def MergeBindings(self, args=None): + """MergeBindings merges operator-defined bindings with the current bindings for + one or more applications. + + args : typing.Sequence[~ApplicationMergeBindings] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="MergeBindings", version=17, params=_params + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ResolveUnitErrors(self, all_=None, retry=None, tags=None): + """ResolveUnitErrors marks errors on the specified units as resolved. + + all_ : bool + retry : bool + tags : Entities + Returns -> ErrorResults + """ + if all_ is not None and not isinstance(all_, bool): + raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") + + if retry is not None and not isinstance(retry, bool): + raise Exception(f"Expected retry to be a bool, received: {type(retry)}") + + if tags is not None and not isinstance(tags, (dict, Entities)): + raise Exception(f"Expected tags to be a Entities, received: {type(tags)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="ResolveUnitErrors", version=17, params=_params + ) + _params["all"] = all_ + _params["retry"] = retry + _params["tags"] = tags + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ScaleApplicationResults) + async def ScaleApplications(self, applications=None): + """ScaleApplications scales the specified application to the requested number of units. + + applications : typing.Sequence[~ScaleApplicationParams] + Returns -> ScaleApplicationResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="ScaleApplications", version=17, params=_params + ) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def SetCharm( + self, + application=None, + channel=None, + charm_origin=None, + charm_url=None, + config_settings=None, + config_settings_yaml=None, + endpoint_bindings=None, + force=None, + force_base=None, + force_units=None, + generation=None, + resource_ids=None, + storage_constraints=None, + ): + """SetCharm sets the charm for a given for the application. + + application : str + channel : str + charm_origin : CharmOrigin + charm_url : str + config_settings : typing.Mapping[str, str] + config_settings_yaml : str + endpoint_bindings : typing.Mapping[str, str] + force : bool + force_base : bool + force_units : bool + generation : str + resource_ids : typing.Mapping[str, str] + storage_constraints : typing.Mapping[str, ~StorageConstraints] + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if channel is not None and not isinstance(channel, (bytes, str)): + raise Exception(f"Expected channel to be a str, received: {type(channel)}") + + if charm_origin is not None and not isinstance( + charm_origin, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" + ) + + if charm_url is not None and not isinstance(charm_url, (bytes, str)): + raise Exception( + f"Expected charm_url to be a str, received: {type(charm_url)}" + ) + + if config_settings is not None and not isinstance(config_settings, dict): + raise Exception( + f"Expected config_settings to be a Mapping, received: {type(config_settings)}" + ) + + if config_settings_yaml is not None and not isinstance( + config_settings_yaml, (bytes, str) + ): + raise Exception( + f"Expected config_settings_yaml to be a str, received: {type(config_settings_yaml)}" + ) + + if endpoint_bindings is not None and not isinstance(endpoint_bindings, dict): + raise Exception( + f"Expected endpoint_bindings to be a Mapping, received: {type(endpoint_bindings)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if force_base is not None and not isinstance(force_base, bool): + raise Exception( + f"Expected force_base to be a bool, received: {type(force_base)}" + ) + + if force_units is not None and not isinstance(force_units, bool): + raise Exception( + f"Expected force_units to be a bool, received: {type(force_units)}" + ) + + if generation is not None and not isinstance(generation, (bytes, str)): + raise Exception( + f"Expected generation to be a str, received: {type(generation)}" + ) + + if resource_ids is not None and not isinstance(resource_ids, dict): + raise Exception( + f"Expected resource_ids to be a Mapping, received: {type(resource_ids)}" + ) + + if storage_constraints is not None and not isinstance( + storage_constraints, dict + ): + raise Exception( + f"Expected storage_constraints to be a Mapping, received: {type(storage_constraints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="SetCharm", version=17, params=_params) + _params["application"] = application + _params["channel"] = channel + _params["charm-origin"] = charm_origin + _params["charm-url"] = charm_url + _params["config-settings"] = config_settings + _params["config-settings-yaml"] = config_settings_yaml + _params["endpoint-bindings"] = endpoint_bindings + _params["force"] = force + _params["force-base"] = force_base + _params["force-units"] = force_units + _params["generation"] = generation + _params["resource-ids"] = resource_ids + _params["storage-constraints"] = storage_constraints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetConfigs(self, args=None): + """SetConfigs implements the server side of Application.SetConfig. Both + application and charm config are set. It does not unset values in + Config map that are set to an empty string. Unset should be used for that. + + args : typing.Sequence[~ConfigSet] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="SetConfigs", version=17, params=_params) + _params["Args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def SetConstraints(self, application=None, constraints=None): + """SetConstraints sets the constraints for a given application. + + application : str + constraints : Value + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if constraints is not None and not isinstance(constraints, (dict, Value)): + raise Exception( + f"Expected constraints to be a Value, received: {type(constraints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="SetConstraints", version=17, params=_params + ) + _params["application"] = application + _params["constraints"] = constraints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetMetricCredentials(self, creds=None): + """SetMetricCredentials sets credentials on the application. + + creds : typing.Sequence[~ApplicationMetricCredential] + Returns -> ErrorResults + """ + if creds is not None and not isinstance(creds, (bytes, str, list)): + raise Exception(f"Expected creds to be a Sequence, received: {type(creds)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="SetMetricCredentials", + version=17, + params=_params, + ) + _params["creds"] = creds + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetRelationsSuspended(self, args=None): + """SetRelationsSuspended sets the suspended status of the specified relations. + + args : typing.Sequence[~RelationSuspendedArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="SetRelationsSuspended", + version=17, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def Unexpose(self, application=None, exposed_endpoints=None): + """Unexpose changes the juju-managed firewall to unexpose any ports that + were also explicitly marked by units as open. + + application : str + exposed_endpoints : typing.Sequence[str] + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if exposed_endpoints is not None and not isinstance( + exposed_endpoints, (bytes, str, list) + ): + raise Exception( + f"Expected exposed_endpoints to be a Sequence, received: {type(exposed_endpoints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Unexpose", version=17, params=_params) + _params["application"] = application + _params["exposed-endpoints"] = exposed_endpoints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UnitInfoResults) + async def UnitsInfo(self, entities=None): + """UnitsInfo returns unit information for the given entities (units or + applications). + + entities : typing.Sequence[~Entity] + Returns -> UnitInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="UnitsInfo", version=17, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UnsetApplicationsConfig(self, args=None): + """UnsetApplicationsConfig implements the server side of Application.UnsetApplicationsConfig. + + args : typing.Sequence[~ApplicationUnset] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="UnsetApplicationsConfig", + version=17, + params=_params, + ) + _params["Args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UpdateApplicationBase(self, args=None): + """UpdateApplicationBase updates the application base. + Base for subordinates is updated too. + + args : typing.Sequence[~UpdateChannelArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="UpdateApplicationBase", + version=17, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client19.py b/build/lib/juju/client/_client19.py new file mode 100644 index 00000000..6437cccd --- /dev/null +++ b/build/lib/juju/client/_client19.py @@ -0,0 +1,845 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class ApplicationFacade(Type): + name = "Application" + version = 19 + + @ReturnMapping(AddRelationResults) + async def AddRelation(self, endpoints=None, via_cidrs=None): + """AddRelation adds a relation between the specified endpoints and returns the relation info. + + endpoints : typing.Sequence[str] + via_cidrs : typing.Sequence[str] + Returns -> AddRelationResults + """ + if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): + raise Exception( + f"Expected endpoints to be a Sequence, received: {type(endpoints)}" + ) + + if via_cidrs is not None and not isinstance(via_cidrs, (bytes, str, list)): + raise Exception( + f"Expected via_cidrs to be a Sequence, received: {type(via_cidrs)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="AddRelation", version=19, params=_params + ) + _params["endpoints"] = endpoints + _params["via-cidrs"] = via_cidrs + reply = await self.rpc(msg) + return reply + + @ReturnMapping(AddApplicationUnitsResults) + async def AddUnits( + self, + application=None, + attach_storage=None, + num_units=None, + placement=None, + policy=None, + ): + """AddUnits adds a given number of units to an application. + + application : str + attach_storage : typing.Sequence[str] + num_units : int + placement : typing.Sequence[~Placement] + policy : str + Returns -> AddApplicationUnitsResults + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if attach_storage is not None and not isinstance( + attach_storage, (bytes, str, list) + ): + raise Exception( + f"Expected attach_storage to be a Sequence, received: {type(attach_storage)}" + ) + + if num_units is not None and not isinstance(num_units, int): + raise Exception( + f"Expected num_units to be a int, received: {type(num_units)}" + ) + + if placement is not None and not isinstance(placement, (bytes, str, list)): + raise Exception( + f"Expected placement to be a Sequence, received: {type(placement)}" + ) + + if policy is not None and not isinstance(policy, (bytes, str)): + raise Exception(f"Expected policy to be a str, received: {type(policy)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="AddUnits", version=19, params=_params) + _params["application"] = application + _params["attach-storage"] = attach_storage + _params["num-units"] = num_units + _params["placement"] = placement + _params["policy"] = policy + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationInfoResults) + async def ApplicationsInfo(self, entities=None): + """ApplicationsInfo returns applications information. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="ApplicationsInfo", version=19, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetConfigResults) + async def CharmConfig(self, args=None): + """CharmConfig returns charm config for the input list of applications and + model generations. + + args : typing.Sequence[~ApplicationGet] + Returns -> ApplicationGetConfigResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="CharmConfig", version=19, params=_params + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationCharmRelationsResults) + async def CharmRelations(self, application=None): + """CharmRelations implements the server side of Application.CharmRelations. + + application : str + Returns -> ApplicationCharmRelationsResults + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="CharmRelations", version=19, params=_params + ) + _params["application"] = application + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Consume(self, args=None): + """Consume adds remote applications to the model without creating any + relations. + + args : typing.Sequence[~ConsumeApplicationArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Consume", version=19, params=_params) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Deploy(self, applications=None): + """Deploy fetches the charms from the charm store and deploys them + using the specified placement directives. + + applications : typing.Sequence[~ApplicationDeploy] + Returns -> ErrorResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Deploy", version=19, params=_params) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DeployFromRepositoryResults) + async def DeployFromRepository(self, args=None): + """DeployFromRepository is a one-stop deployment method for repository + charms. Only a charm name is required to deploy. If argument validation + fails, a list of all errors found in validation will be returned. If a + local resource is provided, details required for uploading the validated + resource will be returned. + + args : typing.Sequence[~DeployFromRepositoryArg] + Returns -> DeployFromRepositoryResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="DeployFromRepository", + version=19, + params=_params, + ) + _params["Args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DestroyApplicationResults) + async def DestroyApplication(self, applications=None): + """DestroyApplication removes a given set of applications. + + applications : typing.Sequence[~DestroyApplicationParams] + Returns -> DestroyApplicationResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="DestroyApplication", version=19, params=_params + ) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DestroyConsumedApplications(self, applications=None): + """DestroyConsumedApplications removes a given set of consumed (remote) applications. + + applications : typing.Sequence[~DestroyConsumedApplicationParams] + Returns -> ErrorResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="DestroyConsumedApplications", + version=19, + params=_params, + ) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def DestroyRelation( + self, endpoints=None, force=None, max_wait=None, relation_id=None + ): + """DestroyRelation removes the relation between the + specified endpoints or an id. + + endpoints : typing.Sequence[str] + force : bool + max_wait : int + relation_id : int + Returns -> None + """ + if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): + raise Exception( + f"Expected endpoints to be a Sequence, received: {type(endpoints)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if max_wait is not None and not isinstance(max_wait, int): + raise Exception( + f"Expected max_wait to be a int, received: {type(max_wait)}" + ) + + if relation_id is not None and not isinstance(relation_id, int): + raise Exception( + f"Expected relation_id to be a int, received: {type(relation_id)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="DestroyRelation", version=19, params=_params + ) + _params["endpoints"] = endpoints + _params["force"] = force + _params["max-wait"] = max_wait + _params["relation-id"] = relation_id + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DestroyUnitResults) + async def DestroyUnit(self, units=None): + """DestroyUnit removes a given set of application units. + + units : typing.Sequence[~DestroyUnitParams] + Returns -> DestroyUnitResults + """ + if units is not None and not isinstance(units, (bytes, str, list)): + raise Exception(f"Expected units to be a Sequence, received: {type(units)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="DestroyUnit", version=19, params=_params + ) + _params["units"] = units + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def Expose(self, application=None, exposed_endpoints=None): + """Expose changes the juju-managed firewall to expose any ports that + were also explicitly marked by units as open. + + application : str + exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if exposed_endpoints is not None and not isinstance(exposed_endpoints, dict): + raise Exception( + f"Expected exposed_endpoints to be a Mapping, received: {type(exposed_endpoints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Expose", version=19, params=_params) + _params["application"] = application + _params["exposed-endpoints"] = exposed_endpoints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetResults) + async def Get(self, application=None, branch=None): + """Get returns the charm configuration for an application. + + application : str + branch : str + Returns -> ApplicationGetResults + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Get", version=19, params=_params) + _params["application"] = application + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CharmURLOriginResult) + async def GetCharmURLOrigin(self, application=None, branch=None): + """GetCharmURLOrigin returns the charm URL and charm origin the given + application is running at present. + + application : str + branch : str + Returns -> CharmURLOriginResult + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="GetCharmURLOrigin", version=19, params=_params + ) + _params["application"] = application + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetConfigResults) + async def GetConfig(self, entities=None): + """GetConfig returns the charm config for each of the input applications. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationGetConfigResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="GetConfig", version=19, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetConstraintsResults) + async def GetConstraints(self, entities=None): + """GetConstraints returns the constraints for a given application. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationGetConstraintsResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="GetConstraints", version=19, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResult) + async def Leader(self, tag=None): + """Leader returns the unit name of the leader for the given application. + + tag : str + Returns -> StringResult + """ + if tag is not None and not isinstance(tag, (bytes, str)): + raise Exception(f"Expected tag to be a str, received: {type(tag)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Leader", version=19, params=_params) + _params["tag"] = tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def MergeBindings(self, args=None): + """MergeBindings merges operator-defined bindings with the current bindings for + one or more applications. + + args : typing.Sequence[~ApplicationMergeBindings] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="MergeBindings", version=19, params=_params + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ResolveUnitErrors(self, all_=None, retry=None, tags=None): + """ResolveUnitErrors marks errors on the specified units as resolved. + + all_ : bool + retry : bool + tags : Entities + Returns -> ErrorResults + """ + if all_ is not None and not isinstance(all_, bool): + raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") + + if retry is not None and not isinstance(retry, bool): + raise Exception(f"Expected retry to be a bool, received: {type(retry)}") + + if tags is not None and not isinstance(tags, (dict, Entities)): + raise Exception(f"Expected tags to be a Entities, received: {type(tags)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="ResolveUnitErrors", version=19, params=_params + ) + _params["all"] = all_ + _params["retry"] = retry + _params["tags"] = tags + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ScaleApplicationResults) + async def ScaleApplications(self, applications=None): + """ScaleApplications scales the specified application to the requested number of units. + + applications : typing.Sequence[~ScaleApplicationParams] + Returns -> ScaleApplicationResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="ScaleApplications", version=19, params=_params + ) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def SetCharm( + self, + application=None, + channel=None, + charm_origin=None, + charm_url=None, + config_settings=None, + config_settings_yaml=None, + endpoint_bindings=None, + force=None, + force_base=None, + force_units=None, + generation=None, + resource_ids=None, + storage_constraints=None, + ): + """SetCharm sets the charm for a given for the application. + + application : str + channel : str + charm_origin : CharmOrigin + charm_url : str + config_settings : typing.Mapping[str, str] + config_settings_yaml : str + endpoint_bindings : typing.Mapping[str, str] + force : bool + force_base : bool + force_units : bool + generation : str + resource_ids : typing.Mapping[str, str] + storage_constraints : typing.Mapping[str, ~StorageConstraints] + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if channel is not None and not isinstance(channel, (bytes, str)): + raise Exception(f"Expected channel to be a str, received: {type(channel)}") + + if charm_origin is not None and not isinstance( + charm_origin, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" + ) + + if charm_url is not None and not isinstance(charm_url, (bytes, str)): + raise Exception( + f"Expected charm_url to be a str, received: {type(charm_url)}" + ) + + if config_settings is not None and not isinstance(config_settings, dict): + raise Exception( + f"Expected config_settings to be a Mapping, received: {type(config_settings)}" + ) + + if config_settings_yaml is not None and not isinstance( + config_settings_yaml, (bytes, str) + ): + raise Exception( + f"Expected config_settings_yaml to be a str, received: {type(config_settings_yaml)}" + ) + + if endpoint_bindings is not None and not isinstance(endpoint_bindings, dict): + raise Exception( + f"Expected endpoint_bindings to be a Mapping, received: {type(endpoint_bindings)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if force_base is not None and not isinstance(force_base, bool): + raise Exception( + f"Expected force_base to be a bool, received: {type(force_base)}" + ) + + if force_units is not None and not isinstance(force_units, bool): + raise Exception( + f"Expected force_units to be a bool, received: {type(force_units)}" + ) + + if generation is not None and not isinstance(generation, (bytes, str)): + raise Exception( + f"Expected generation to be a str, received: {type(generation)}" + ) + + if resource_ids is not None and not isinstance(resource_ids, dict): + raise Exception( + f"Expected resource_ids to be a Mapping, received: {type(resource_ids)}" + ) + + if storage_constraints is not None and not isinstance( + storage_constraints, dict + ): + raise Exception( + f"Expected storage_constraints to be a Mapping, received: {type(storage_constraints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="SetCharm", version=19, params=_params) + _params["application"] = application + _params["channel"] = channel + _params["charm-origin"] = charm_origin + _params["charm-url"] = charm_url + _params["config-settings"] = config_settings + _params["config-settings-yaml"] = config_settings_yaml + _params["endpoint-bindings"] = endpoint_bindings + _params["force"] = force + _params["force-base"] = force_base + _params["force-units"] = force_units + _params["generation"] = generation + _params["resource-ids"] = resource_ids + _params["storage-constraints"] = storage_constraints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetConfigs(self, args=None): + """SetConfigs implements the server side of Application.SetConfig. Both + application and charm config are set. It does not unset values in + Config map that are set to an empty string. Unset should be used for that. + + args : typing.Sequence[~ConfigSet] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="SetConfigs", version=19, params=_params) + _params["Args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def SetConstraints(self, application=None, constraints=None): + """SetConstraints sets the constraints for a given application. + + application : str + constraints : Value + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if constraints is not None and not isinstance(constraints, (dict, Value)): + raise Exception( + f"Expected constraints to be a Value, received: {type(constraints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="SetConstraints", version=19, params=_params + ) + _params["application"] = application + _params["constraints"] = constraints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetMetricCredentials(self, creds=None): + """SetMetricCredentials sets credentials on the application. + TODO (cderici) only used for metered charms in cmd MeteredDeployAPI, + kept for client compatibility, remove in juju 4.0 + + creds : typing.Sequence[~ApplicationMetricCredential] + Returns -> ErrorResults + """ + if creds is not None and not isinstance(creds, (bytes, str, list)): + raise Exception(f"Expected creds to be a Sequence, received: {type(creds)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="SetMetricCredentials", + version=19, + params=_params, + ) + _params["creds"] = creds + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetRelationsSuspended(self, args=None): + """SetRelationsSuspended sets the suspended status of the specified relations. + + args : typing.Sequence[~RelationSuspendedArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="SetRelationsSuspended", + version=19, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def Unexpose(self, application=None, exposed_endpoints=None): + """Unexpose changes the juju-managed firewall to unexpose any ports that + were also explicitly marked by units as open. + + application : str + exposed_endpoints : typing.Sequence[str] + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if exposed_endpoints is not None and not isinstance( + exposed_endpoints, (bytes, str, list) + ): + raise Exception( + f"Expected exposed_endpoints to be a Sequence, received: {type(exposed_endpoints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Unexpose", version=19, params=_params) + _params["application"] = application + _params["exposed-endpoints"] = exposed_endpoints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UnitInfoResults) + async def UnitsInfo(self, entities=None): + """UnitsInfo returns unit information for the given entities (units or + applications). + + entities : typing.Sequence[~Entity] + Returns -> UnitInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="UnitsInfo", version=19, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UnsetApplicationsConfig(self, args=None): + """UnsetApplicationsConfig implements the server side of Application.UnsetApplicationsConfig. + + args : typing.Sequence[~ApplicationUnset] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="UnsetApplicationsConfig", + version=19, + params=_params, + ) + _params["Args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UpdateApplicationBase(self, args=None): + """UpdateApplicationBase updates the application base. + Base for subordinates is updated too. + + args : typing.Sequence[~UpdateChannelArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="UpdateApplicationBase", + version=19, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client2.py b/build/lib/juju/client/_client2.py new file mode 100644 index 00000000..91cf26f2 --- /dev/null +++ b/build/lib/juju/client/_client2.py @@ -0,0 +1,326 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class AnnotationsFacade(Type): + name = "Annotations" + version = 2 + + @ReturnMapping(AnnotationsGetResults) + async def Get(self, entities=None): + """Get returns annotations for given entities. + If annotations cannot be retrieved for a given entity, an error is returned. + Each entity is treated independently and, hence, will fail or succeed independently. + + entities : typing.Sequence[~Entity] + Returns -> AnnotationsGetResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Annotations", request="Get", version=2, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Set(self, annotations=None): + """Set stores annotations for given entities + + annotations : typing.Sequence[~EntityAnnotations] + Returns -> ErrorResults + """ + if annotations is not None and not isinstance(annotations, (bytes, str, list)): + raise Exception( + f"Expected annotations to be a Sequence, received: {type(annotations)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Annotations", request="Set", version=2, params=_params) + _params["annotations"] = annotations + reply = await self.rpc(msg) + return reply + + +class BlockFacade(Type): + name = "Block" + version = 2 + + @ReturnMapping(BlockResults) + async def List(self): + """List implements Block.List(). + + Returns -> BlockResults + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Block", request="List", version=2, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResult) + async def SwitchBlockOff(self, message=None, type_=None): + """SwitchBlockOff implements Block.SwitchBlockOff(). + + message : str + type_ : str + Returns -> ErrorResult + """ + if message is not None and not isinstance(message, (bytes, str)): + raise Exception(f"Expected message to be a str, received: {type(message)}") + + if type_ is not None and not isinstance(type_, (bytes, str)): + raise Exception(f"Expected type_ to be a str, received: {type(type_)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Block", request="SwitchBlockOff", version=2, params=_params) + _params["message"] = message + _params["type"] = type_ + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResult) + async def SwitchBlockOn(self, message=None, type_=None): + """SwitchBlockOn implements Block.SwitchBlockOn(). + + message : str + type_ : str + Returns -> ErrorResult + """ + if message is not None and not isinstance(message, (bytes, str)): + raise Exception(f"Expected message to be a str, received: {type(message)}") + + if type_ is not None and not isinstance(type_, (bytes, str)): + raise Exception(f"Expected type_ to be a str, received: {type(type_)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Block", request="SwitchBlockOn", version=2, params=_params) + _params["message"] = message + _params["type"] = type_ + reply = await self.rpc(msg) + return reply + + +class HighAvailabilityFacade(Type): + name = "HighAvailability" + version = 2 + + @ReturnMapping(ControllersChangeResults) + async def EnableHA(self, specs=None): + """EnableHA adds controller machines as necessary to ensure the + controller has the number of machines specified. + + specs : typing.Sequence[~ControllersSpec] + Returns -> ControllersChangeResults + """ + if specs is not None and not isinstance(specs, (bytes, str, list)): + raise Exception(f"Expected specs to be a Sequence, received: {type(specs)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="HighAvailability", request="EnableHA", version=2, params=_params + ) + _params["specs"] = specs + reply = await self.rpc(msg) + return reply + + +class MetricsDebugFacade(Type): + name = "MetricsDebug" + version = 2 + + @ReturnMapping(MetricResults) + async def GetMetrics(self, entities=None): + """GetMetrics returns all metrics stored by the state server. + + entities : typing.Sequence[~Entity] + Returns -> MetricResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="MetricsDebug", request="GetMetrics", version=2, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetMeterStatus(self, statues=None): + """SetMeterStatus sets meter statuses for entities. + + statues : typing.Sequence[~MeterStatusParam] + Returns -> ErrorResults + """ + if statues is not None and not isinstance(statues, (bytes, str, list)): + raise Exception( + f"Expected statues to be a Sequence, received: {type(statues)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="MetricsDebug", request="SetMeterStatus", version=2, params=_params + ) + _params["statues"] = statues + reply = await self.rpc(msg) + return reply + + +class SecretsFacade(Type): + name = "Secrets" + version = 2 + + @ReturnMapping(StringResults) + async def CreateSecrets(self, args=None): + """CreateSecrets creates new secrets. + + args : typing.Sequence[~CreateSecretArg] + Returns -> StringResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Secrets", request="CreateSecrets", version=2, params=_params) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def GrantSecret(self, applications=None, label=None, uri=None): + """GrantSecret grants access to a user secret. + + applications : typing.Sequence[str] + label : str + uri : str + Returns -> ErrorResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + if label is not None and not isinstance(label, (bytes, str)): + raise Exception(f"Expected label to be a str, received: {type(label)}") + + if uri is not None and not isinstance(uri, (bytes, str)): + raise Exception(f"Expected uri to be a str, received: {type(uri)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Secrets", request="GrantSecret", version=2, params=_params) + _params["applications"] = applications + _params["label"] = label + _params["uri"] = uri + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ListSecretResults) + async def ListSecrets(self, filter_=None, show_secrets=None): + """ListSecrets lists available secrets. + + filter_ : SecretsFilter + show_secrets : bool + Returns -> ListSecretResults + """ + if filter_ is not None and not isinstance(filter_, (dict, SecretsFilter)): + raise Exception( + f"Expected filter_ to be a SecretsFilter, received: {type(filter_)}" + ) + + if show_secrets is not None and not isinstance(show_secrets, bool): + raise Exception( + f"Expected show_secrets to be a bool, received: {type(show_secrets)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Secrets", request="ListSecrets", version=2, params=_params) + _params["filter"] = filter_ + _params["show-secrets"] = show_secrets + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def RemoveSecrets(self, args=None): + """RemoveSecrets remove user secret. + + args : typing.Sequence[~DeleteSecretArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Secrets", request="RemoveSecrets", version=2, params=_params) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def RevokeSecret(self, applications=None, label=None, uri=None): + """RevokeSecret revokes access to a user secret. + + applications : typing.Sequence[str] + label : str + uri : str + Returns -> ErrorResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + if label is not None and not isinstance(label, (bytes, str)): + raise Exception(f"Expected label to be a str, received: {type(label)}") + + if uri is not None and not isinstance(uri, (bytes, str)): + raise Exception(f"Expected uri to be a str, received: {type(uri)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Secrets", request="RevokeSecret", version=2, params=_params) + _params["applications"] = applications + _params["label"] = label + _params["uri"] = uri + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UpdateSecrets(self, args=None): + """UpdateSecrets creates new secrets. + + args : typing.Sequence[~UpdateUserSecretArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Secrets", request="UpdateSecrets", version=2, params=_params) + _params["args"] = args + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client20.py b/build/lib/juju/client/_client20.py new file mode 100644 index 00000000..846ab264 --- /dev/null +++ b/build/lib/juju/client/_client20.py @@ -0,0 +1,845 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class ApplicationFacade(Type): + name = "Application" + version = 20 + + @ReturnMapping(AddRelationResults) + async def AddRelation(self, endpoints=None, via_cidrs=None): + """AddRelation adds a relation between the specified endpoints and returns the relation info. + + endpoints : typing.Sequence[str] + via_cidrs : typing.Sequence[str] + Returns -> AddRelationResults + """ + if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): + raise Exception( + f"Expected endpoints to be a Sequence, received: {type(endpoints)}" + ) + + if via_cidrs is not None and not isinstance(via_cidrs, (bytes, str, list)): + raise Exception( + f"Expected via_cidrs to be a Sequence, received: {type(via_cidrs)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="AddRelation", version=20, params=_params + ) + _params["endpoints"] = endpoints + _params["via-cidrs"] = via_cidrs + reply = await self.rpc(msg) + return reply + + @ReturnMapping(AddApplicationUnitsResults) + async def AddUnits( + self, + application=None, + attach_storage=None, + num_units=None, + placement=None, + policy=None, + ): + """AddUnits adds a given number of units to an application. + + application : str + attach_storage : typing.Sequence[str] + num_units : int + placement : typing.Sequence[~Placement] + policy : str + Returns -> AddApplicationUnitsResults + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if attach_storage is not None and not isinstance( + attach_storage, (bytes, str, list) + ): + raise Exception( + f"Expected attach_storage to be a Sequence, received: {type(attach_storage)}" + ) + + if num_units is not None and not isinstance(num_units, int): + raise Exception( + f"Expected num_units to be a int, received: {type(num_units)}" + ) + + if placement is not None and not isinstance(placement, (bytes, str, list)): + raise Exception( + f"Expected placement to be a Sequence, received: {type(placement)}" + ) + + if policy is not None and not isinstance(policy, (bytes, str)): + raise Exception(f"Expected policy to be a str, received: {type(policy)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="AddUnits", version=20, params=_params) + _params["application"] = application + _params["attach-storage"] = attach_storage + _params["num-units"] = num_units + _params["placement"] = placement + _params["policy"] = policy + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationInfoResults) + async def ApplicationsInfo(self, entities=None): + """ApplicationsInfo returns applications information. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="ApplicationsInfo", version=20, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetConfigResults) + async def CharmConfig(self, args=None): + """CharmConfig returns charm config for the input list of applications and + model generations. + + args : typing.Sequence[~ApplicationGet] + Returns -> ApplicationGetConfigResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="CharmConfig", version=20, params=_params + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationCharmRelationsResults) + async def CharmRelations(self, application=None): + """CharmRelations implements the server side of Application.CharmRelations. + + application : str + Returns -> ApplicationCharmRelationsResults + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="CharmRelations", version=20, params=_params + ) + _params["application"] = application + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Consume(self, args=None): + """Consume adds remote applications to the model without creating any + relations. + + args : typing.Sequence[~ConsumeApplicationArgV5] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Consume", version=20, params=_params) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Deploy(self, applications=None): + """Deploy fetches the charms from the charm store and deploys them + using the specified placement directives. + + applications : typing.Sequence[~ApplicationDeploy] + Returns -> ErrorResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Deploy", version=20, params=_params) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DeployFromRepositoryResults) + async def DeployFromRepository(self, args=None): + """DeployFromRepository is a one-stop deployment method for repository + charms. Only a charm name is required to deploy. If argument validation + fails, a list of all errors found in validation will be returned. If a + local resource is provided, details required for uploading the validated + resource will be returned. + + args : typing.Sequence[~DeployFromRepositoryArg] + Returns -> DeployFromRepositoryResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="DeployFromRepository", + version=20, + params=_params, + ) + _params["Args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DestroyApplicationResults) + async def DestroyApplication(self, applications=None): + """DestroyApplication removes a given set of applications. + + applications : typing.Sequence[~DestroyApplicationParams] + Returns -> DestroyApplicationResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="DestroyApplication", version=20, params=_params + ) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DestroyConsumedApplications(self, applications=None): + """DestroyConsumedApplications removes a given set of consumed (remote) applications. + + applications : typing.Sequence[~DestroyConsumedApplicationParams] + Returns -> ErrorResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="DestroyConsumedApplications", + version=20, + params=_params, + ) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def DestroyRelation( + self, endpoints=None, force=None, max_wait=None, relation_id=None + ): + """DestroyRelation removes the relation between the + specified endpoints or an id. + + endpoints : typing.Sequence[str] + force : bool + max_wait : int + relation_id : int + Returns -> None + """ + if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): + raise Exception( + f"Expected endpoints to be a Sequence, received: {type(endpoints)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if max_wait is not None and not isinstance(max_wait, int): + raise Exception( + f"Expected max_wait to be a int, received: {type(max_wait)}" + ) + + if relation_id is not None and not isinstance(relation_id, int): + raise Exception( + f"Expected relation_id to be a int, received: {type(relation_id)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="DestroyRelation", version=20, params=_params + ) + _params["endpoints"] = endpoints + _params["force"] = force + _params["max-wait"] = max_wait + _params["relation-id"] = relation_id + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DestroyUnitResults) + async def DestroyUnit(self, units=None): + """DestroyUnit removes a given set of application units. + + units : typing.Sequence[~DestroyUnitParams] + Returns -> DestroyUnitResults + """ + if units is not None and not isinstance(units, (bytes, str, list)): + raise Exception(f"Expected units to be a Sequence, received: {type(units)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="DestroyUnit", version=20, params=_params + ) + _params["units"] = units + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def Expose(self, application=None, exposed_endpoints=None): + """Expose changes the juju-managed firewall to expose any ports that + were also explicitly marked by units as open. + + application : str + exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if exposed_endpoints is not None and not isinstance(exposed_endpoints, dict): + raise Exception( + f"Expected exposed_endpoints to be a Mapping, received: {type(exposed_endpoints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Expose", version=20, params=_params) + _params["application"] = application + _params["exposed-endpoints"] = exposed_endpoints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetResults) + async def Get(self, application=None, branch=None): + """Get returns the charm configuration for an application. + + application : str + branch : str + Returns -> ApplicationGetResults + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Get", version=20, params=_params) + _params["application"] = application + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CharmURLOriginResult) + async def GetCharmURLOrigin(self, application=None, branch=None): + """GetCharmURLOrigin returns the charm URL and charm origin the given + application is running at present. + + application : str + branch : str + Returns -> CharmURLOriginResult + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="GetCharmURLOrigin", version=20, params=_params + ) + _params["application"] = application + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetConfigResults) + async def GetConfig(self, entities=None): + """GetConfig returns the charm config for each of the input applications. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationGetConfigResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="GetConfig", version=20, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationGetConstraintsResults) + async def GetConstraints(self, entities=None): + """GetConstraints returns the constraints for a given application. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationGetConstraintsResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="GetConstraints", version=20, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResult) + async def Leader(self, tag=None): + """Leader returns the unit name of the leader for the given application. + + tag : str + Returns -> StringResult + """ + if tag is not None and not isinstance(tag, (bytes, str)): + raise Exception(f"Expected tag to be a str, received: {type(tag)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Leader", version=20, params=_params) + _params["tag"] = tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def MergeBindings(self, args=None): + """MergeBindings merges operator-defined bindings with the current bindings for + one or more applications. + + args : typing.Sequence[~ApplicationMergeBindings] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="MergeBindings", version=20, params=_params + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ResolveUnitErrors(self, all_=None, retry=None, tags=None): + """ResolveUnitErrors marks errors on the specified units as resolved. + + all_ : bool + retry : bool + tags : Entities + Returns -> ErrorResults + """ + if all_ is not None and not isinstance(all_, bool): + raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") + + if retry is not None and not isinstance(retry, bool): + raise Exception(f"Expected retry to be a bool, received: {type(retry)}") + + if tags is not None and not isinstance(tags, (dict, Entities)): + raise Exception(f"Expected tags to be a Entities, received: {type(tags)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="ResolveUnitErrors", version=20, params=_params + ) + _params["all"] = all_ + _params["retry"] = retry + _params["tags"] = tags + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ScaleApplicationResults) + async def ScaleApplications(self, applications=None): + """ScaleApplications scales the specified application to the requested number of units. + + applications : typing.Sequence[~ScaleApplicationParams] + Returns -> ScaleApplicationResults + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="ScaleApplications", version=20, params=_params + ) + _params["applications"] = applications + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def SetCharm( + self, + application=None, + channel=None, + charm_origin=None, + charm_url=None, + config_settings=None, + config_settings_yaml=None, + endpoint_bindings=None, + force=None, + force_base=None, + force_units=None, + generation=None, + resource_ids=None, + storage_constraints=None, + ): + """SetCharm sets the charm for a given for the application. + + application : str + channel : str + charm_origin : CharmOrigin + charm_url : str + config_settings : typing.Mapping[str, str] + config_settings_yaml : str + endpoint_bindings : typing.Mapping[str, str] + force : bool + force_base : bool + force_units : bool + generation : str + resource_ids : typing.Mapping[str, str] + storage_constraints : typing.Mapping[str, ~StorageConstraints] + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if channel is not None and not isinstance(channel, (bytes, str)): + raise Exception(f"Expected channel to be a str, received: {type(channel)}") + + if charm_origin is not None and not isinstance( + charm_origin, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" + ) + + if charm_url is not None and not isinstance(charm_url, (bytes, str)): + raise Exception( + f"Expected charm_url to be a str, received: {type(charm_url)}" + ) + + if config_settings is not None and not isinstance(config_settings, dict): + raise Exception( + f"Expected config_settings to be a Mapping, received: {type(config_settings)}" + ) + + if config_settings_yaml is not None and not isinstance( + config_settings_yaml, (bytes, str) + ): + raise Exception( + f"Expected config_settings_yaml to be a str, received: {type(config_settings_yaml)}" + ) + + if endpoint_bindings is not None and not isinstance(endpoint_bindings, dict): + raise Exception( + f"Expected endpoint_bindings to be a Mapping, received: {type(endpoint_bindings)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if force_base is not None and not isinstance(force_base, bool): + raise Exception( + f"Expected force_base to be a bool, received: {type(force_base)}" + ) + + if force_units is not None and not isinstance(force_units, bool): + raise Exception( + f"Expected force_units to be a bool, received: {type(force_units)}" + ) + + if generation is not None and not isinstance(generation, (bytes, str)): + raise Exception( + f"Expected generation to be a str, received: {type(generation)}" + ) + + if resource_ids is not None and not isinstance(resource_ids, dict): + raise Exception( + f"Expected resource_ids to be a Mapping, received: {type(resource_ids)}" + ) + + if storage_constraints is not None and not isinstance( + storage_constraints, dict + ): + raise Exception( + f"Expected storage_constraints to be a Mapping, received: {type(storage_constraints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="SetCharm", version=20, params=_params) + _params["application"] = application + _params["channel"] = channel + _params["charm-origin"] = charm_origin + _params["charm-url"] = charm_url + _params["config-settings"] = config_settings + _params["config-settings-yaml"] = config_settings_yaml + _params["endpoint-bindings"] = endpoint_bindings + _params["force"] = force + _params["force-base"] = force_base + _params["force-units"] = force_units + _params["generation"] = generation + _params["resource-ids"] = resource_ids + _params["storage-constraints"] = storage_constraints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetConfigs(self, args=None): + """SetConfigs implements the server side of Application.SetConfig. Both + application and charm config are set. It does not unset values in + Config map that are set to an empty string. Unset should be used for that. + + args : typing.Sequence[~ConfigSet] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="SetConfigs", version=20, params=_params) + _params["Args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def SetConstraints(self, application=None, constraints=None): + """SetConstraints sets the constraints for a given application. + + application : str + constraints : Value + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if constraints is not None and not isinstance(constraints, (dict, Value)): + raise Exception( + f"Expected constraints to be a Value, received: {type(constraints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", request="SetConstraints", version=20, params=_params + ) + _params["application"] = application + _params["constraints"] = constraints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetMetricCredentials(self, creds=None): + """SetMetricCredentials sets credentials on the application. + TODO (cderici) only used for metered charms in cmd MeteredDeployAPI, + kept for client compatibility, remove in juju 4.0 + + creds : typing.Sequence[~ApplicationMetricCredential] + Returns -> ErrorResults + """ + if creds is not None and not isinstance(creds, (bytes, str, list)): + raise Exception(f"Expected creds to be a Sequence, received: {type(creds)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="SetMetricCredentials", + version=20, + params=_params, + ) + _params["creds"] = creds + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetRelationsSuspended(self, args=None): + """SetRelationsSuspended sets the suspended status of the specified relations. + + args : typing.Sequence[~RelationSuspendedArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="SetRelationsSuspended", + version=20, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def Unexpose(self, application=None, exposed_endpoints=None): + """Unexpose changes the juju-managed firewall to unexpose any ports that + were also explicitly marked by units as open. + + application : str + exposed_endpoints : typing.Sequence[str] + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if exposed_endpoints is not None and not isinstance( + exposed_endpoints, (bytes, str, list) + ): + raise Exception( + f"Expected exposed_endpoints to be a Sequence, received: {type(exposed_endpoints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="Unexpose", version=20, params=_params) + _params["application"] = application + _params["exposed-endpoints"] = exposed_endpoints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UnitInfoResults) + async def UnitsInfo(self, entities=None): + """UnitsInfo returns unit information for the given entities (units or + applications). + + entities : typing.Sequence[~Entity] + Returns -> UnitInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Application", request="UnitsInfo", version=20, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UnsetApplicationsConfig(self, args=None): + """UnsetApplicationsConfig implements the server side of Application.UnsetApplicationsConfig. + + args : typing.Sequence[~ApplicationUnset] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="UnsetApplicationsConfig", + version=20, + params=_params, + ) + _params["Args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UpdateApplicationBase(self, args=None): + """UpdateApplicationBase updates the application base. + Base for subordinates is updated too. + + args : typing.Sequence[~UpdateChannelArg] + Returns -> ErrorResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Application", + request="UpdateApplicationBase", + version=20, + params=_params, + ) + _params["args"] = args + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client3.py b/build/lib/juju/client/_client3.py new file mode 100644 index 00000000..b119ce03 --- /dev/null +++ b/build/lib/juju/client/_client3.py @@ -0,0 +1,605 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class AdminFacade(Type): + name = "Admin" + version = 3 + + @ReturnMapping(LoginResult) + async def Login( + self, + auth_tag=None, + bakery_version=None, + cli_args=None, + client_version=None, + credentials=None, + macaroons=None, + nonce=None, + token=None, + user_data=None, + ): + """Login logs in with the provided credentials. All subsequent requests on the + connection will act as the authenticated user. + + auth_tag : str + bakery_version : int + cli_args : str + client_version : str + credentials : str + macaroons : typing.Sequence[~Macaroon] + nonce : str + token : str + user_data : str + Returns -> LoginResult + """ + if auth_tag is not None and not isinstance(auth_tag, (bytes, str)): + raise Exception( + f"Expected auth_tag to be a str, received: {type(auth_tag)}" + ) + + if bakery_version is not None and not isinstance(bakery_version, int): + raise Exception( + f"Expected bakery_version to be a int, received: {type(bakery_version)}" + ) + + if cli_args is not None and not isinstance(cli_args, (bytes, str)): + raise Exception( + f"Expected cli_args to be a str, received: {type(cli_args)}" + ) + + if client_version is not None and not isinstance(client_version, (bytes, str)): + raise Exception( + f"Expected client_version to be a str, received: {type(client_version)}" + ) + + if credentials is not None and not isinstance(credentials, (bytes, str)): + raise Exception( + f"Expected credentials to be a str, received: {type(credentials)}" + ) + + if macaroons is not None and not isinstance(macaroons, (bytes, str, list)): + raise Exception( + f"Expected macaroons to be a Sequence, received: {type(macaroons)}" + ) + + if nonce is not None and not isinstance(nonce, (bytes, str)): + raise Exception(f"Expected nonce to be a str, received: {type(nonce)}") + + if token is not None and not isinstance(token, (bytes, str)): + raise Exception(f"Expected token to be a str, received: {type(token)}") + + if user_data is not None and not isinstance(user_data, (bytes, str)): + raise Exception( + f"Expected user_data to be a str, received: {type(user_data)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Admin", request="Login", version=3, params=_params) + _params["auth-tag"] = auth_tag + _params["bakery-version"] = bakery_version + _params["cli-args"] = cli_args + _params["client-version"] = client_version + _params["credentials"] = credentials + _params["macaroons"] = macaroons + _params["nonce"] = nonce + _params["token"] = token + _params["user-data"] = user_data + reply = await self.rpc(msg) + return reply + + @ReturnMapping(RedirectInfoResult) + async def RedirectInfo(self): + """RedirectInfo returns redirected host information for the model. + In Juju it always returns an error because the Juju controller + does not multiplex controllers. + + Returns -> RedirectInfoResult + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Admin", request="RedirectInfo", version=3, params=_params) + + reply = await self.rpc(msg) + return reply + + +class AllWatcherFacade(Type): + name = "AllWatcher" + version = 3 + + @ReturnMapping(AllWatcherNextResults) + async def Next(self): + """Next will return the current state of everything on the first call + and subsequent calls will + + Returns -> AllWatcherNextResults + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="AllWatcher", request="Next", version=3, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def Stop(self): + """Stop stops the watcher. + + Returns -> None + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="AllWatcher", request="Stop", version=3, params=_params) + + reply = await self.rpc(msg) + return reply + + async def rpc(self, msg): + """Patch rpc method to add Id.""" + if not hasattr(self, "Id"): + raise RuntimeError('Missing "Id" field') + msg["Id"] = id + + from .facade import TypeEncoder + + reply = await self.connection.rpc(msg, encoder=TypeEncoder) + return reply + + +class BackupsFacade(Type): + name = "Backups" + version = 3 + + @ReturnMapping(BackupsMetadataResult) + async def Create(self, no_download=None, notes=None): + """Create is the API method that requests juju to create a new backup + of its state. + + no_download : bool + notes : str + Returns -> BackupsMetadataResult + """ + if no_download is not None and not isinstance(no_download, bool): + raise Exception( + f"Expected no_download to be a bool, received: {type(no_download)}" + ) + + if notes is not None and not isinstance(notes, (bytes, str)): + raise Exception(f"Expected notes to be a str, received: {type(notes)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Backups", request="Create", version=3, params=_params) + _params["no-download"] = no_download + _params["notes"] = notes + reply = await self.rpc(msg) + return reply + + +class ModelConfigFacade(Type): + name = "ModelConfig" + version = 3 + + @ReturnMapping(GetConstraintsResults) + async def GetModelConstraints(self): + """GetModelConstraints returns the constraints for the model. + + Returns -> GetConstraintsResults + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelConfig", request="GetModelConstraints", version=3, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelConfigResults) + async def ModelGet(self): + """ModelGet implements the server-side part of the + model-config CLI command. + + Returns -> ModelConfigResults + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelConfig", request="ModelGet", version=3, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def ModelSet(self, config=None): + """ModelSet implements the server-side part of the + set-model-config CLI command. + + config : typing.Mapping[str, typing.Any] + Returns -> None + """ + if config is not None and not isinstance(config, dict): + raise Exception( + f"Expected config to be a Mapping, received: {type(config)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelConfig", request="ModelSet", version=3, params=_params) + _params["config"] = config + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def ModelUnset(self, keys=None): + """ModelUnset implements the server-side part of the + set-model-config CLI command. + + keys : typing.Sequence[str] + Returns -> None + """ + if keys is not None and not isinstance(keys, (bytes, str, list)): + raise Exception(f"Expected keys to be a Sequence, received: {type(keys)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelConfig", request="ModelUnset", version=3, params=_params) + _params["keys"] = keys + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResult) + async def SLALevel(self): + """SLALevel returns the current sla level for the model. + + Returns -> StringResult + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelConfig", request="SLALevel", version=3, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelSequencesResult) + async def Sequences(self): + """Sequences returns the model's sequence names and next values. + + Returns -> ModelSequencesResult + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelConfig", request="Sequences", version=3, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def SetModelConstraints(self, application=None, constraints=None): + """SetModelConstraints sets the constraints for the model. + + application : str + constraints : Value + Returns -> None + """ + if application is not None and not isinstance(application, (bytes, str)): + raise Exception( + f"Expected application to be a str, received: {type(application)}" + ) + + if constraints is not None and not isinstance(constraints, (dict, Value)): + raise Exception( + f"Expected constraints to be a Value, received: {type(constraints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelConfig", request="SetModelConstraints", version=3, params=_params + ) + _params["application"] = application + _params["constraints"] = constraints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def SetSLALevel(self, modelslainfo=None, creds=None, level=None, owner=None): + """SetSLALevel sets the sla level on the model. + + modelslainfo : ModelSLAInfo + creds : typing.Sequence[int] + level : str + owner : str + Returns -> None + """ + if modelslainfo is not None and not isinstance( + modelslainfo, (dict, ModelSLAInfo) + ): + raise Exception( + f"Expected modelslainfo to be a ModelSLAInfo, received: {type(modelslainfo)}" + ) + + if creds is not None and not isinstance(creds, (bytes, str, list)): + raise Exception(f"Expected creds to be a Sequence, received: {type(creds)}") + + if level is not None and not isinstance(level, (bytes, str)): + raise Exception(f"Expected level to be a str, received: {type(level)}") + + if owner is not None and not isinstance(owner, (bytes, str)): + raise Exception(f"Expected owner to be a str, received: {type(owner)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelConfig", request="SetSLALevel", version=3, params=_params) + _params["ModelSLAInfo"] = modelslainfo + _params["creds"] = creds + _params["level"] = level + _params["owner"] = owner + reply = await self.rpc(msg) + return reply + + +class ResourcesFacade(Type): + name = "Resources" + version = 3 + + @ReturnMapping(AddPendingResourcesResult) + async def AddPendingResources( + self, + entity=None, + charm_origin=None, + macaroon=None, + resources=None, + tag=None, + url=None, + ): + """AddPendingResources adds the provided resources (info) to the Juju + model in a pending state, meaning they are not available until + resolved. Handles CharmHub and Local charms. + + entity : Entity + charm_origin : CharmOrigin + macaroon : Macaroon + resources : typing.Sequence[~CharmResource] + tag : str + url : str + Returns -> AddPendingResourcesResult + """ + if entity is not None and not isinstance(entity, (dict, Entity)): + raise Exception(f"Expected entity to be a Entity, received: {type(entity)}") + + if charm_origin is not None and not isinstance( + charm_origin, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" + ) + + if macaroon is not None and not isinstance(macaroon, (dict, Macaroon)): + raise Exception( + f"Expected macaroon to be a Macaroon, received: {type(macaroon)}" + ) + + if resources is not None and not isinstance(resources, (bytes, str, list)): + raise Exception( + f"Expected resources to be a Sequence, received: {type(resources)}" + ) + + if tag is not None and not isinstance(tag, (bytes, str)): + raise Exception(f"Expected tag to be a str, received: {type(tag)}") + + if url is not None and not isinstance(url, (bytes, str)): + raise Exception(f"Expected url to be a str, received: {type(url)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Resources", request="AddPendingResources", version=3, params=_params + ) + _params["Entity"] = entity + _params["charm-origin"] = charm_origin + _params["macaroon"] = macaroon + _params["resources"] = resources + _params["tag"] = tag + _params["url"] = url + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ResourcesResults) + async def ListResources(self, entities=None): + """ListResources returns the list of resources for the given application. + + entities : typing.Sequence[~Entity] + Returns -> ResourcesResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Resources", request="ListResources", version=3, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + +class UserManagerFacade(Type): + name = "UserManager" + version = 3 + + @ReturnMapping(AddUserResults) + async def AddUser(self, users=None): + """AddUser adds a user with a username, and either a password or + a randomly generated secret key which will be returned. + + users : typing.Sequence[~AddUser] + Returns -> AddUserResults + """ + if users is not None and not isinstance(users, (bytes, str, list)): + raise Exception(f"Expected users to be a Sequence, received: {type(users)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="UserManager", request="AddUser", version=3, params=_params) + _params["users"] = users + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DisableUser(self, entities=None): + """DisableUser disables one or more users. If the user is already disabled, + the action is considered a success. + + entities : typing.Sequence[~Entity] + Returns -> ErrorResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="UserManager", request="DisableUser", version=3, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def EnableUser(self, entities=None): + """EnableUser enables one or more users. If the user is already enabled, + the action is considered a success. + + entities : typing.Sequence[~Entity] + Returns -> ErrorResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="UserManager", request="EnableUser", version=3, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelUserInfoResults) + async def ModelUserInfo(self, entities=None): + """ModelUserInfo returns information on all users in the model. + + entities : typing.Sequence[~Entity] + Returns -> ModelUserInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="UserManager", request="ModelUserInfo", version=3, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def RemoveUser(self, entities=None): + """RemoveUser permanently removes a user from the current controller for each + entity provided. While the user is permanently removed we keep it's + information around for auditing purposes. + TODO(redir): Add information about getting deleted user information when we + add that capability. + + entities : typing.Sequence[~Entity] + Returns -> ErrorResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="UserManager", request="RemoveUser", version=3, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(AddUserResults) + async def ResetPassword(self, entities=None): + """ResetPassword resets password for supplied users by + invalidating current passwords (if any) and generating + new random secret keys which will be returned. + Users cannot reset their own password. + + entities : typing.Sequence[~Entity] + Returns -> AddUserResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="UserManager", request="ResetPassword", version=3, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetPassword(self, changes=None): + """SetPassword changes the stored password for the specified users. + + changes : typing.Sequence[~EntityPassword] + Returns -> ErrorResults + """ + if changes is not None and not isinstance(changes, (bytes, str, list)): + raise Exception( + f"Expected changes to be a Sequence, received: {type(changes)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="UserManager", request="SetPassword", version=3, params=_params) + _params["changes"] = changes + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UserInfoResults) + async def UserInfo(self, entities=None, include_disabled=None): + """UserInfo returns information on a user. + + entities : typing.Sequence[~Entity] + include_disabled : bool + Returns -> UserInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + if include_disabled is not None and not isinstance(include_disabled, bool): + raise Exception( + f"Expected include_disabled to be a bool, received: {type(include_disabled)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="UserManager", request="UserInfo", version=3, params=_params) + _params["entities"] = entities + _params["include-disabled"] = include_disabled + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client4.py b/build/lib/juju/client/_client4.py new file mode 100644 index 00000000..0e8c6890 --- /dev/null +++ b/build/lib/juju/client/_client4.py @@ -0,0 +1,572 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class AllModelWatcherFacade(Type): + name = "AllModelWatcher" + version = 4 + + @ReturnMapping(AllWatcherNextResults) + async def Next(self): + """Next will return the current state of everything on the first call + and subsequent calls will + + Returns -> AllWatcherNextResults + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="AllModelWatcher", request="Next", version=4, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def Stop(self): + """Stop stops the watcher. + + Returns -> None + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="AllModelWatcher", request="Stop", version=4, params=_params) + + reply = await self.rpc(msg) + return reply + + async def rpc(self, msg): + """Patch rpc method to add Id.""" + if not hasattr(self, "Id"): + raise RuntimeError('Missing "Id" field') + msg["Id"] = id + + from .facade import TypeEncoder + + reply = await self.connection.rpc(msg, encoder=TypeEncoder) + return reply + + +class ApplicationOffersFacade(Type): + name = "ApplicationOffers" + version = 4 + + @ReturnMapping(ApplicationOffersResults) + async def ApplicationOffers(self, bakery_version=None, offer_urls=None): + """ApplicationOffers gets details about remote applications that match given URLs. + + bakery_version : int + offer_urls : typing.Sequence[str] + Returns -> ApplicationOffersResults + """ + if bakery_version is not None and not isinstance(bakery_version, int): + raise Exception( + f"Expected bakery_version to be a int, received: {type(bakery_version)}" + ) + + if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): + raise Exception( + f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="ApplicationOffers", + version=4, + params=_params, + ) + _params["bakery-version"] = bakery_version + _params["offer-urls"] = offer_urls + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DestroyOffers(self, force=None, offer_urls=None): + """DestroyOffers removes the offers specified by the given URLs, forcing if necessary. + + force : bool + offer_urls : typing.Sequence[str] + Returns -> ErrorResults + """ + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): + raise Exception( + f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", request="DestroyOffers", version=4, params=_params + ) + _params["force"] = force + _params["offer-urls"] = offer_urls + reply = await self.rpc(msg) + return reply + + @ReturnMapping(QueryApplicationOffersResults) + async def FindApplicationOffers(self, filters=None): + """FindApplicationOffers gets details about remote applications that match given filter. + + filters : typing.Sequence[~OfferFilter] + Returns -> QueryApplicationOffersResults + """ + if filters is not None and not isinstance(filters, (bytes, str, list)): + raise Exception( + f"Expected filters to be a Sequence, received: {type(filters)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="FindApplicationOffers", + version=4, + params=_params, + ) + _params["Filters"] = filters + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ConsumeOfferDetailsResults) + async def GetConsumeDetails(self, offer_urls=None, user_tag=None): + """GetConsumeDetails returns the details necessary to pass to another model + to allow the specified args user to consume the offers represented by the args URLs. + + offer_urls : OfferURLs + user_tag : str + Returns -> ConsumeOfferDetailsResults + """ + if offer_urls is not None and not isinstance(offer_urls, (dict, OfferURLs)): + raise Exception( + f"Expected offer_urls to be a OfferURLs, received: {type(offer_urls)}" + ) + + if user_tag is not None and not isinstance(user_tag, (bytes, str)): + raise Exception( + f"Expected user_tag to be a str, received: {type(user_tag)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="GetConsumeDetails", + version=4, + params=_params, + ) + _params["offer-urls"] = offer_urls + _params["user-tag"] = user_tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(QueryApplicationOffersResults) + async def ListApplicationOffers(self, filters=None): + """ListApplicationOffers gets deployed details about application offers that match given filter. + The results contain details about the deployed applications such as connection count. + + filters : typing.Sequence[~OfferFilter] + Returns -> QueryApplicationOffersResults + """ + if filters is not None and not isinstance(filters, (bytes, str, list)): + raise Exception( + f"Expected filters to be a Sequence, received: {type(filters)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="ListApplicationOffers", + version=4, + params=_params, + ) + _params["Filters"] = filters + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ModifyOfferAccess(self, changes=None): + """ModifyOfferAccess changes the application offer access granted to users. + + changes : typing.Sequence[~ModifyOfferAccess] + Returns -> ErrorResults + """ + if changes is not None and not isinstance(changes, (bytes, str, list)): + raise Exception( + f"Expected changes to be a Sequence, received: {type(changes)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="ModifyOfferAccess", + version=4, + params=_params, + ) + _params["changes"] = changes + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Offer(self, offers=None): + """Offer makes application endpoints available for consumption at a specified URL. + + offers : typing.Sequence[~AddApplicationOffer] + Returns -> ErrorResults + """ + if offers is not None and not isinstance(offers, (bytes, str, list)): + raise Exception( + f"Expected offers to be a Sequence, received: {type(offers)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="ApplicationOffers", request="Offer", version=4, params=_params) + _params["Offers"] = offers + reply = await self.rpc(msg) + return reply + + @ReturnMapping(RemoteApplicationInfoResults) + async def RemoteApplicationInfo(self, bakery_version=None, offer_urls=None): + """RemoteApplicationInfo returns information about the requested remote application. + This call currently has no client side API, only there for the Dashboard at this stage. + + bakery_version : int + offer_urls : typing.Sequence[str] + Returns -> RemoteApplicationInfoResults + """ + if bakery_version is not None and not isinstance(bakery_version, int): + raise Exception( + f"Expected bakery_version to be a int, received: {type(bakery_version)}" + ) + + if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): + raise Exception( + f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="RemoteApplicationInfo", + version=4, + params=_params, + ) + _params["bakery-version"] = bakery_version + _params["offer-urls"] = offer_urls + reply = await self.rpc(msg) + return reply + + +class ModelGenerationFacade(Type): + name = "ModelGeneration" + version = 4 + + @ReturnMapping(ErrorResult) + async def AbortBranch(self, branch=None): + """AbortBranch aborts the input branch, marking it complete. However no + changes are made applicable to the whole model. No units may be assigned + to the branch when aborting. + + branch : str + Returns -> ErrorResult + """ + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelGeneration", request="AbortBranch", version=4, params=_params + ) + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResult) + async def AddBranch(self, branch=None): + """AddBranch adds a new branch with the input name to the model. + + branch : str + Returns -> ErrorResult + """ + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelGeneration", request="AddBranch", version=4, params=_params + ) + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(BranchResults) + async def BranchInfo(self, branches=None, detailed=None): + """BranchInfo will return details of branch identified by the input argument, + including units on the branch and the configuration disjoint with the + master generation. + An error is returned if no in-flight branch matching in input is found. + + branches : typing.Sequence[str] + detailed : bool + Returns -> BranchResults + """ + if branches is not None and not isinstance(branches, (bytes, str, list)): + raise Exception( + f"Expected branches to be a Sequence, received: {type(branches)}" + ) + + if detailed is not None and not isinstance(detailed, bool): + raise Exception( + f"Expected detailed to be a bool, received: {type(detailed)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelGeneration", request="BranchInfo", version=4, params=_params + ) + _params["branches"] = branches + _params["detailed"] = detailed + reply = await self.rpc(msg) + return reply + + @ReturnMapping(IntResult) + async def CommitBranch(self, branch=None): + """CommitBranch commits the input branch, making its changes applicable to + the whole model and marking it complete. + + branch : str + Returns -> IntResult + """ + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelGeneration", request="CommitBranch", version=4, params=_params + ) + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(BoolResult) + async def HasActiveBranch(self, branch=None): + """HasActiveBranch returns a true result if the input model has an "in-flight" + branch matching the input name. + + branch : str + Returns -> BoolResult + """ + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelGeneration", request="HasActiveBranch", version=4, params=_params + ) + _params["branch"] = branch + reply = await self.rpc(msg) + return reply + + @ReturnMapping(BranchResults) + async def ListCommits(self): + """ListCommits will return the commits, hence only branches with generation_id higher than 0 + + Returns -> BranchResults + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelGeneration", request="ListCommits", version=4, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(GenerationResult) + async def ShowCommit(self, generation_id=None): + """ShowCommit will return details a commit given by its generationId + An error is returned if either no branch can be found corresponding to the generation id. + Or the generation id given is below 1. + + generation_id : int + Returns -> GenerationResult + """ + if generation_id is not None and not isinstance(generation_id, int): + raise Exception( + f"Expected generation_id to be a int, received: {type(generation_id)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelGeneration", request="ShowCommit", version=4, params=_params + ) + _params["generation-id"] = generation_id + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def TrackBranch(self, branch=None, entities=None, num_units=None): + """TrackBranch marks the input units and/or applications as tracking the input + branch, causing them to realise changes made under that branch. + + branch : str + entities : typing.Sequence[~Entity] + num_units : int + Returns -> ErrorResults + """ + if branch is not None and not isinstance(branch, (bytes, str)): + raise Exception(f"Expected branch to be a str, received: {type(branch)}") + + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + if num_units is not None and not isinstance(num_units, int): + raise Exception( + f"Expected num_units to be a int, received: {type(num_units)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelGeneration", request="TrackBranch", version=4, params=_params + ) + _params["branch"] = branch + _params["entities"] = entities + _params["num-units"] = num_units + reply = await self.rpc(msg) + return reply + + +class SSHClientFacade(Type): + name = "SSHClient" + version = 4 + + @ReturnMapping(SSHAddressesResults) + async def AllAddresses(self, entities=None): + """AllAddresses reports all addresses that might have SSH listening for each + entity in args. The result is sorted with public addresses first. + Machines and units are supported as entity types. + + entities : typing.Sequence[~Entity] + Returns -> SSHAddressesResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="SSHClient", request="AllAddresses", version=4, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CloudSpecResult) + async def ModelCredentialForSSH(self): + """ModelCredentialForSSH returns a cloud spec for ssh purpose. + This facade call is only used for k8s model. + + Returns -> CloudSpecResult + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="SSHClient", request="ModelCredentialForSSH", version=4, params=_params + ) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(SSHAddressResults) + async def PrivateAddress(self, entities=None): + """PrivateAddress reports the preferred private network address for one or + more entities. Machines and units are supported. + + entities : typing.Sequence[~Entity] + Returns -> SSHAddressResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="SSHClient", request="PrivateAddress", version=4, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(SSHProxyResult) + async def Proxy(self): + """Proxy returns whether SSH connections should be proxied through the + controller hosts for the model associated with the API connection. + + Returns -> SSHProxyResult + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="SSHClient", request="Proxy", version=4, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(SSHAddressResults) + async def PublicAddress(self, entities=None): + """PublicAddress reports the preferred public network address for one + or more entities. Machines and units are supported. + + entities : typing.Sequence[~Entity] + Returns -> SSHAddressResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="SSHClient", request="PublicAddress", version=4, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(SSHPublicKeysResults) + async def PublicKeys(self, entities=None): + """PublicKeys returns the public SSH hosts for one or more + entities. Machines and units are supported. + + entities : typing.Sequence[~Entity] + Returns -> SSHPublicKeysResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="SSHClient", request="PublicKeys", version=4, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client5.py b/build/lib/juju/client/_client5.py new file mode 100644 index 00000000..ee64edd4 --- /dev/null +++ b/build/lib/juju/client/_client5.py @@ -0,0 +1,285 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class ApplicationOffersFacade(Type): + name = "ApplicationOffers" + version = 5 + + @ReturnMapping(ApplicationOffersResults) + async def ApplicationOffers(self, bakery_version=None, offer_urls=None): + """ApplicationOffers gets details about remote applications that match given URLs. + + bakery_version : int + offer_urls : typing.Sequence[str] + Returns -> ApplicationOffersResults + """ + if bakery_version is not None and not isinstance(bakery_version, int): + raise Exception( + f"Expected bakery_version to be a int, received: {type(bakery_version)}" + ) + + if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): + raise Exception( + f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="ApplicationOffers", + version=5, + params=_params, + ) + _params["bakery-version"] = bakery_version + _params["offer-urls"] = offer_urls + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DestroyOffers(self, force=None, offer_urls=None): + """DestroyOffers removes the offers specified by the given URLs, forcing if necessary. + + force : bool + offer_urls : typing.Sequence[str] + Returns -> ErrorResults + """ + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): + raise Exception( + f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", request="DestroyOffers", version=5, params=_params + ) + _params["force"] = force + _params["offer-urls"] = offer_urls + reply = await self.rpc(msg) + return reply + + @ReturnMapping(QueryApplicationOffersResultsV5) + async def FindApplicationOffers(self, filters=None): + """FindApplicationOffers gets details about remote applications that match given filter. + + filters : typing.Sequence[~OfferFilter] + Returns -> QueryApplicationOffersResultsV5 + """ + if filters is not None and not isinstance(filters, (bytes, str, list)): + raise Exception( + f"Expected filters to be a Sequence, received: {type(filters)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="FindApplicationOffers", + version=5, + params=_params, + ) + _params["Filters"] = filters + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ConsumeOfferDetailsResults) + async def GetConsumeDetails(self, offer_urls=None, user_tag=None): + """GetConsumeDetails returns the details necessary to pass to another model + to allow the specified args user to consume the offers represented by the args URLs. + + offer_urls : OfferURLs + user_tag : str + Returns -> ConsumeOfferDetailsResults + """ + if offer_urls is not None and not isinstance(offer_urls, (dict, OfferURLs)): + raise Exception( + f"Expected offer_urls to be a OfferURLs, received: {type(offer_urls)}" + ) + + if user_tag is not None and not isinstance(user_tag, (bytes, str)): + raise Exception( + f"Expected user_tag to be a str, received: {type(user_tag)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="GetConsumeDetails", + version=5, + params=_params, + ) + _params["offer-urls"] = offer_urls + _params["user-tag"] = user_tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(QueryApplicationOffersResultsV5) + async def ListApplicationOffers(self, filters=None): + """ListApplicationOffers gets deployed details about application offers that match given filter. + The results contain details about the deployed applications such as connection count. + + filters : typing.Sequence[~OfferFilter] + Returns -> QueryApplicationOffersResultsV5 + """ + if filters is not None and not isinstance(filters, (bytes, str, list)): + raise Exception( + f"Expected filters to be a Sequence, received: {type(filters)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="ListApplicationOffers", + version=5, + params=_params, + ) + _params["Filters"] = filters + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ModifyOfferAccess(self, changes=None): + """ModifyOfferAccess changes the application offer access granted to users. + + changes : typing.Sequence[~ModifyOfferAccess] + Returns -> ErrorResults + """ + if changes is not None and not isinstance(changes, (bytes, str, list)): + raise Exception( + f"Expected changes to be a Sequence, received: {type(changes)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="ModifyOfferAccess", + version=5, + params=_params, + ) + _params["changes"] = changes + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Offer(self, offers=None): + """Offer makes application endpoints available for consumption at a specified URL. + + offers : typing.Sequence[~AddApplicationOffer] + Returns -> ErrorResults + """ + if offers is not None and not isinstance(offers, (bytes, str, list)): + raise Exception( + f"Expected offers to be a Sequence, received: {type(offers)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="ApplicationOffers", request="Offer", version=5, params=_params) + _params["Offers"] = offers + reply = await self.rpc(msg) + return reply + + @ReturnMapping(RemoteApplicationInfoResults) + async def RemoteApplicationInfo(self, bakery_version=None, offer_urls=None): + """RemoteApplicationInfo returns information about the requested remote application. + This call currently has no client side API, only there for the Dashboard at this stage. + + bakery_version : int + offer_urls : typing.Sequence[str] + Returns -> RemoteApplicationInfoResults + """ + if bakery_version is not None and not isinstance(bakery_version, int): + raise Exception( + f"Expected bakery_version to be a int, received: {type(bakery_version)}" + ) + + if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): + raise Exception( + f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ApplicationOffers", + request="RemoteApplicationInfo", + version=5, + params=_params, + ) + _params["bakery-version"] = bakery_version + _params["offer-urls"] = offer_urls + reply = await self.rpc(msg) + return reply + + +class SubnetsFacade(Type): + name = "Subnets" + version = 5 + + @ReturnMapping(ZoneResults) + async def AllZones(self): + """AllZones returns all availability zones known to Juju. If a + zone is unusable, unavailable, or deprecated the Available + field will be false. + + Returns -> ZoneResults + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Subnets", request="AllZones", version=5, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ListSubnetsResults) + async def ListSubnets(self, space_tag=None, zone=None): + """ListSubnets returns the matching subnets after applying + optional filters. + + space_tag : str + zone : str + Returns -> ListSubnetsResults + """ + if space_tag is not None and not isinstance(space_tag, (bytes, str)): + raise Exception( + f"Expected space_tag to be a str, received: {type(space_tag)}" + ) + + if zone is not None and not isinstance(zone, (bytes, str)): + raise Exception(f"Expected zone to be a str, received: {type(zone)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Subnets", request="ListSubnets", version=5, params=_params) + _params["space-tag"] = space_tag + _params["zone"] = zone + reply = await self.rpc(msg) + return reply + + @ReturnMapping(SubnetsResults) + async def SubnetsByCIDR(self, cidrs=None): + """SubnetsByCIDR returns the collection of subnets matching each CIDR in the input. + + cidrs : typing.Sequence[str] + Returns -> SubnetsResults + """ + if cidrs is not None and not isinstance(cidrs, (bytes, str, list)): + raise Exception(f"Expected cidrs to be a Sequence, received: {type(cidrs)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Subnets", request="SubnetsByCIDR", version=5, params=_params) + _params["cidrs"] = cidrs + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client6.py b/build/lib/juju/client/_client6.py new file mode 100644 index 00000000..29c2cb93 --- /dev/null +++ b/build/lib/juju/client/_client6.py @@ -0,0 +1,781 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class BundleFacade(Type): + name = "Bundle" + version = 6 + + @ReturnMapping(StringResult) + async def ExportBundle(self, include_charm_defaults=None, include_series=None): + """ExportBundle exports the current model configuration as bundle. + + include_charm_defaults : bool + include_series : bool + Returns -> StringResult + """ + if include_charm_defaults is not None and not isinstance( + include_charm_defaults, bool + ): + raise Exception( + f"Expected include_charm_defaults to be a bool, received: {type(include_charm_defaults)}" + ) + + if include_series is not None and not isinstance(include_series, bool): + raise Exception( + f"Expected include_series to be a bool, received: {type(include_series)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Bundle", request="ExportBundle", version=6, params=_params) + _params["include-charm-defaults"] = include_charm_defaults + _params["include-series"] = include_series + reply = await self.rpc(msg) + return reply + + @ReturnMapping(BundleChangesResults) + async def GetChanges(self, bundleurl=None, yaml=None): + """GetChanges returns the list of changes required to deploy the given bundle + data. The changes are sorted by requirements, so that they can be applied in + order. + GetChanges has been superseded in favour of GetChangesMapArgs. It's + preferable to use that new method to add new functionality and move clients + away from this one. + + bundleurl : str + yaml : str + Returns -> BundleChangesResults + """ + if bundleurl is not None and not isinstance(bundleurl, (bytes, str)): + raise Exception( + f"Expected bundleurl to be a str, received: {type(bundleurl)}" + ) + + if yaml is not None and not isinstance(yaml, (bytes, str)): + raise Exception(f"Expected yaml to be a str, received: {type(yaml)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Bundle", request="GetChanges", version=6, params=_params) + _params["bundleURL"] = bundleurl + _params["yaml"] = yaml + reply = await self.rpc(msg) + return reply + + @ReturnMapping(BundleChangesMapArgsResults) + async def GetChangesMapArgs(self, bundleurl=None, yaml=None): + """GetChangesMapArgs returns the list of changes required to deploy the given + bundle data. The changes are sorted by requirements, so that they can be + applied in order. + V4 GetChangesMapArgs is not supported on anything less than v4 + + bundleurl : str + yaml : str + Returns -> BundleChangesMapArgsResults + """ + if bundleurl is not None and not isinstance(bundleurl, (bytes, str)): + raise Exception( + f"Expected bundleurl to be a str, received: {type(bundleurl)}" + ) + + if yaml is not None and not isinstance(yaml, (bytes, str)): + raise Exception(f"Expected yaml to be a str, received: {type(yaml)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Bundle", request="GetChangesMapArgs", version=6, params=_params + ) + _params["bundleURL"] = bundleurl + _params["yaml"] = yaml + reply = await self.rpc(msg) + return reply + + +class CharmsFacade(Type): + name = "Charms" + version = 6 + + @ReturnMapping(CharmOriginResult) + async def AddCharm(self, charm_origin=None, force=None, url=None): + """AddCharm adds the given charm URL (which must include revision) to the + environment, if it does not exist yet. Local charms are not supported, + only charm store and charm hub URLs. See also AddLocalCharm(). + + charm_origin : CharmOrigin + force : bool + url : str + Returns -> CharmOriginResult + """ + if charm_origin is not None and not isinstance( + charm_origin, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if url is not None and not isinstance(url, (bytes, str)): + raise Exception(f"Expected url to be a str, received: {type(url)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="AddCharm", version=6, params=_params) + _params["charm-origin"] = charm_origin + _params["force"] = force + _params["url"] = url + reply = await self.rpc(msg) + return reply + + @ReturnMapping(Charm) + async def CharmInfo(self, url=None): + """CharmInfo returns information about the requested charm. + + url : str + Returns -> Charm + """ + if url is not None and not isinstance(url, (bytes, str)): + raise Exception(f"Expected url to be a str, received: {type(url)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="CharmInfo", version=6, params=_params) + _params["url"] = url + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def CheckCharmPlacement(self, placements=None): + """CheckCharmPlacement checks if a charm is allowed to be placed with in a + given application. + + placements : typing.Sequence[~ApplicationCharmPlacement] + Returns -> ErrorResults + """ + if placements is not None and not isinstance(placements, (bytes, str, list)): + raise Exception( + f"Expected placements to be a Sequence, received: {type(placements)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Charms", request="CheckCharmPlacement", version=6, params=_params + ) + _params["placements"] = placements + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DownloadInfoResults) + async def GetDownloadInfos(self, entities=None): + """GetDownloadInfos attempts to get the bundle corresponding to the charm url + and origin. + + entities : typing.Sequence[~CharmURLAndOrigin] + Returns -> DownloadInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="GetDownloadInfos", version=6, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(IsMeteredResult) + async def IsMetered(self, url=None): + """IsMetered returns whether or not the charm is metered. + + url : str + Returns -> IsMeteredResult + """ + if url is not None and not isinstance(url, (bytes, str)): + raise Exception(f"Expected url to be a str, received: {type(url)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="IsMetered", version=6, params=_params) + _params["url"] = url + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CharmsListResult) + async def List(self, names=None): + """List returns a list of charm URLs currently in the state. + If supplied parameter contains any names, the result will + be filtered to return only the charms with supplied names. + + names : typing.Sequence[str] + Returns -> CharmsListResult + """ + if names is not None and not isinstance(names, (bytes, str, list)): + raise Exception(f"Expected names to be a Sequence, received: {type(names)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="List", version=6, params=_params) + _params["names"] = names + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CharmResourcesResults) + async def ListCharmResources(self, entities=None): + """ListCharmResources returns a series of resources for a given charm. + + entities : typing.Sequence[~CharmURLAndOrigin] + Returns -> CharmResourcesResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Charms", request="ListCharmResources", version=6, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ResolveCharmWithChannelResults) + async def ResolveCharms(self, macaroon=None, resolve=None): + """ResolveCharms resolves the given charm URLs with an optionally specified + preferred channel. Channel provided via CharmOrigin. + + macaroon : Macaroon + resolve : typing.Sequence[~ResolveCharmWithChannel] + Returns -> ResolveCharmWithChannelResults + """ + if macaroon is not None and not isinstance(macaroon, (dict, Macaroon)): + raise Exception( + f"Expected macaroon to be a Macaroon, received: {type(macaroon)}" + ) + + if resolve is not None and not isinstance(resolve, (bytes, str, list)): + raise Exception( + f"Expected resolve to be a Sequence, received: {type(resolve)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="ResolveCharms", version=6, params=_params) + _params["macaroon"] = macaroon + _params["resolve"] = resolve + reply = await self.rpc(msg) + return reply + + +class ClientFacade(Type): + name = "Client" + version = 6 + + @ReturnMapping(FindToolsResult) + async def FindTools( + self, agentstream=None, arch=None, major=None, number=None, os_type=None + ): + """FindTools returns a List containing all tools matching the given parameters. + TODO(juju 3.1) - remove, used by 2.9 client only + + agentstream : str + arch : str + major : int + number : Number + os_type : str + Returns -> FindToolsResult + """ + if agentstream is not None and not isinstance(agentstream, (bytes, str)): + raise Exception( + f"Expected agentstream to be a str, received: {type(agentstream)}" + ) + + if arch is not None and not isinstance(arch, (bytes, str)): + raise Exception(f"Expected arch to be a str, received: {type(arch)}") + + if major is not None and not isinstance(major, int): + raise Exception(f"Expected major to be a int, received: {type(major)}") + + if number is not None and not isinstance(number, (dict, Number)): + raise Exception(f"Expected number to be a Number, received: {type(number)}") + + if os_type is not None and not isinstance(os_type, (bytes, str)): + raise Exception(f"Expected os_type to be a str, received: {type(os_type)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="FindTools", version=6, params=_params) + _params["agentstream"] = agentstream + _params["arch"] = arch + _params["major"] = major + _params["number"] = number + _params["os-type"] = os_type + reply = await self.rpc(msg) + return reply + + @ReturnMapping(FullStatus) + async def FullStatus(self, patterns=None): + """FullStatus gives the information needed for juju status over the api + + patterns : typing.Sequence[str] + Returns -> FullStatus + """ + if patterns is not None and not isinstance(patterns, (bytes, str, list)): + raise Exception( + f"Expected patterns to be a Sequence, received: {type(patterns)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="FullStatus", version=6, params=_params) + _params["patterns"] = patterns + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StatusHistoryResults) + async def StatusHistory(self, requests=None): + """StatusHistory returns a slice of past statuses for several entities. + + requests : typing.Sequence[~StatusHistoryRequest] + Returns -> StatusHistoryResults + """ + if requests is not None and not isinstance(requests, (bytes, str, list)): + raise Exception( + f"Expected requests to be a Sequence, received: {type(requests)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="StatusHistory", version=6, params=_params) + _params["requests"] = requests + reply = await self.rpc(msg) + return reply + + @ReturnMapping(AllWatcherId) + async def WatchAll(self): + """WatchAll initiates a watcher for entities in the connected model. + + Returns -> AllWatcherId + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="WatchAll", version=6, params=_params) + + reply = await self.rpc(msg) + return reply + + +class SpacesFacade(Type): + name = "Spaces" + version = 6 + + @ReturnMapping(ErrorResults) + async def CreateSpaces(self, spaces=None): + """CreateSpaces creates a new Juju network space, associating the + specified subnets with it (optional; can be empty). + + spaces : typing.Sequence[~CreateSpaceParams] + Returns -> ErrorResults + """ + if spaces is not None and not isinstance(spaces, (bytes, str, list)): + raise Exception( + f"Expected spaces to be a Sequence, received: {type(spaces)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Spaces", request="CreateSpaces", version=6, params=_params) + _params["spaces"] = spaces + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ListSpacesResults) + async def ListSpaces(self): + """ListSpaces lists all the available spaces and their associated subnets. + + Returns -> ListSpacesResults + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Spaces", request="ListSpaces", version=6, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(MoveSubnetsResults) + async def MoveSubnets(self, args=None): + """MoveSubnets ensures that the input subnets are in the input space. + + args : typing.Sequence[~MoveSubnetsParam] + Returns -> MoveSubnetsResults + """ + if args is not None and not isinstance(args, (bytes, str, list)): + raise Exception(f"Expected args to be a Sequence, received: {type(args)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Spaces", request="MoveSubnets", version=6, params=_params) + _params["args"] = args + reply = await self.rpc(msg) + return reply + + @ReturnMapping(None) + async def ReloadSpaces(self): + """ReloadSpaces refreshes spaces from substrate + + Returns -> None + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Spaces", request="ReloadSpaces", version=6, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(RemoveSpaceResults) + async def RemoveSpace(self, space_param=None): + """RemoveSpace removes a space. + Returns SpaceResults if entities/settings are found which makes the deletion not possible. + + space_param : typing.Sequence[~RemoveSpaceParam] + Returns -> RemoveSpaceResults + """ + if space_param is not None and not isinstance(space_param, (bytes, str, list)): + raise Exception( + f"Expected space_param to be a Sequence, received: {type(space_param)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Spaces", request="RemoveSpace", version=6, params=_params) + _params["space-param"] = space_param + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def RenameSpace(self, changes=None): + """RenameSpace renames a space. + + changes : typing.Sequence[~RenameSpaceParams] + Returns -> ErrorResults + """ + if changes is not None and not isinstance(changes, (bytes, str, list)): + raise Exception( + f"Expected changes to be a Sequence, received: {type(changes)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Spaces", request="RenameSpace", version=6, params=_params) + _params["changes"] = changes + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ShowSpaceResults) + async def ShowSpace(self, entities=None): + """ShowSpace shows the spaces for a set of given entities. + + entities : typing.Sequence[~Entity] + Returns -> ShowSpaceResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Spaces", request="ShowSpace", version=6, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + +class StorageFacade(Type): + name = "Storage" + version = 6 + + @ReturnMapping(AddStorageResults) + async def AddToUnit(self, storages=None): + """AddToUnit validates and creates additional storage instances for units. + A "CHANGE" block can block this operation. + + storages : typing.Sequence[~StorageAddParams] + Returns -> AddStorageResults + """ + if storages is not None and not isinstance(storages, (bytes, str, list)): + raise Exception( + f"Expected storages to be a Sequence, received: {type(storages)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="AddToUnit", version=6, params=_params) + _params["storages"] = storages + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Attach(self, ids=None): + """Attach attaches existing storage instances to units. + A "CHANGE" block can block this operation. + + ids : typing.Sequence[~StorageAttachmentId] + Returns -> ErrorResults + """ + if ids is not None and not isinstance(ids, (bytes, str, list)): + raise Exception(f"Expected ids to be a Sequence, received: {type(ids)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="Attach", version=6, params=_params) + _params["ids"] = ids + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def CreatePool(self, pools=None): + """CreatePool creates a new pool with specified parameters. + + pools : typing.Sequence[~StoragePool] + Returns -> ErrorResults + """ + if pools is not None and not isinstance(pools, (bytes, str, list)): + raise Exception(f"Expected pools to be a Sequence, received: {type(pools)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="CreatePool", version=6, params=_params) + _params["pools"] = pools + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DetachStorage(self, force=None, ids=None, max_wait=None): + """DetachStorage sets the specified storage attachments to Dying, unless they are + already Dying or Dead. Any associated, persistent storage will remain + alive. This call can be forced. + + force : bool + ids : StorageAttachmentIds + max_wait : int + Returns -> ErrorResults + """ + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if ids is not None and not isinstance(ids, (dict, StorageAttachmentIds)): + raise Exception( + f"Expected ids to be a StorageAttachmentIds, received: {type(ids)}" + ) + + if max_wait is not None and not isinstance(max_wait, int): + raise Exception( + f"Expected max_wait to be a int, received: {type(max_wait)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="DetachStorage", version=6, params=_params) + _params["force"] = force + _params["ids"] = ids + _params["max-wait"] = max_wait + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ImportStorageResults) + async def Import(self, storage=None): + """Import imports existing storage into the model. + A "CHANGE" block can block this operation. + + storage : typing.Sequence[~ImportStorageParams] + Returns -> ImportStorageResults + """ + if storage is not None and not isinstance(storage, (bytes, str, list)): + raise Exception( + f"Expected storage to be a Sequence, received: {type(storage)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="Import", version=6, params=_params) + _params["storage"] = storage + reply = await self.rpc(msg) + return reply + + @ReturnMapping(FilesystemDetailsListResults) + async def ListFilesystems(self, filters=None): + """ListFilesystems returns a list of filesystems in the environment matching + the provided filter. Each result describes a filesystem in detail, including + the filesystem's attachments. + + filters : typing.Sequence[~FilesystemFilter] + Returns -> FilesystemDetailsListResults + """ + if filters is not None and not isinstance(filters, (bytes, str, list)): + raise Exception( + f"Expected filters to be a Sequence, received: {type(filters)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="ListFilesystems", version=6, params=_params) + _params["filters"] = filters + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StoragePoolsResults) + async def ListPools(self, filters=None): + """ListPools returns a list of pools. + If filter is provided, returned list only contains pools that match + the filter. + Pools can be filtered on names and provider types. + If both names and types are provided as filter, + pools that match either are returned. + This method lists union of pools and environment provider types. + If no filter is provided, all pools are returned. + + filters : typing.Sequence[~StoragePoolFilter] + Returns -> StoragePoolsResults + """ + if filters is not None and not isinstance(filters, (bytes, str, list)): + raise Exception( + f"Expected filters to be a Sequence, received: {type(filters)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="ListPools", version=6, params=_params) + _params["filters"] = filters + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StorageDetailsListResults) + async def ListStorageDetails(self, filters=None): + """ListStorageDetails returns storage matching a filter. + + filters : typing.Sequence[~StorageFilter] + Returns -> StorageDetailsListResults + """ + if filters is not None and not isinstance(filters, (bytes, str, list)): + raise Exception( + f"Expected filters to be a Sequence, received: {type(filters)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Storage", request="ListStorageDetails", version=6, params=_params + ) + _params["filters"] = filters + reply = await self.rpc(msg) + return reply + + @ReturnMapping(VolumeDetailsListResults) + async def ListVolumes(self, filters=None): + """ListVolumes lists volumes with the given filters. Each filter produces + an independent list of volumes, or an error if the filter is invalid + or the volumes could not be listed. + + filters : typing.Sequence[~VolumeFilter] + Returns -> VolumeDetailsListResults + """ + if filters is not None and not isinstance(filters, (bytes, str, list)): + raise Exception( + f"Expected filters to be a Sequence, received: {type(filters)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="ListVolumes", version=6, params=_params) + _params["filters"] = filters + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def Remove(self, storage=None): + """Remove sets the specified storage entities to Dying, unless they are + already Dying or Dead, such that the storage will eventually be removed + from the model. If the arguments specify that the storage should be + destroyed, then the associated cloud storage will be destroyed first; + otherwise it will only be released from Juju's control. + + storage : typing.Sequence[~RemoveStorageInstance] + Returns -> ErrorResults + """ + if storage is not None and not isinstance(storage, (bytes, str, list)): + raise Exception( + f"Expected storage to be a Sequence, received: {type(storage)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="Remove", version=6, params=_params) + _params["storage"] = storage + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def RemovePool(self, pools=None): + """RemovePool deletes the named pool + + pools : typing.Sequence[~StoragePoolDeleteArg] + Returns -> ErrorResults + """ + if pools is not None and not isinstance(pools, (bytes, str, list)): + raise Exception(f"Expected pools to be a Sequence, received: {type(pools)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="RemovePool", version=6, params=_params) + _params["pools"] = pools + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StorageDetailsResults) + async def StorageDetails(self, entities=None): + """StorageDetails retrieves and returns detailed information about desired + storage identified by supplied tags. If specified storage cannot be + retrieved, individual error is returned instead of storage information. + + entities : typing.Sequence[~Entity] + Returns -> StorageDetailsResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="StorageDetails", version=6, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UpdatePool(self, pools=None): + """UpdatePool deletes the named pool + + pools : typing.Sequence[~StoragePool] + Returns -> ErrorResults + """ + if pools is not None and not isinstance(pools, (bytes, str, list)): + raise Exception(f"Expected pools to be a Sequence, received: {type(pools)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Storage", request="UpdatePool", version=6, params=_params) + _params["pools"] = pools + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client7.py b/build/lib/juju/client/_client7.py new file mode 100644 index 00000000..09efa972 --- /dev/null +++ b/build/lib/juju/client/_client7.py @@ -0,0 +1,1014 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class ActionFacade(Type): + name = "Action" + version = 7 + + @ReturnMapping(ActionResults) + async def Actions(self, entities=None): + """Actions takes a list of ActionTags, and returns the full Action for + each ID. + + entities : typing.Sequence[~Entity] + Returns -> ActionResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Action", request="Actions", version=7, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ApplicationsCharmActionsResults) + async def ApplicationsCharmsActions(self, entities=None): + """ApplicationsCharmsActions returns a slice of charm Actions for a slice of + services. + + entities : typing.Sequence[~Entity] + Returns -> ApplicationsCharmActionsResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Action", + request="ApplicationsCharmsActions", + version=7, + params=_params, + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ActionResults) + async def Cancel(self, entities=None): + """Cancel attempts to cancel enqueued Actions from running. + + entities : typing.Sequence[~Entity] + Returns -> ActionResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Action", request="Cancel", version=7, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(EnqueuedActions) + async def EnqueueOperation(self, actions=None): + """EnqueueOperation takes a list of Actions and queues them up to be executed as + an operation, each action running as a task on the designated ActionReceiver. + We return the ID of the overall operation and each individual task. + + actions : typing.Sequence[~Action] + Returns -> EnqueuedActions + """ + if actions is not None and not isinstance(actions, (bytes, str, list)): + raise Exception( + f"Expected actions to be a Sequence, received: {type(actions)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Action", request="EnqueueOperation", version=7, params=_params) + _params["actions"] = actions + reply = await self.rpc(msg) + return reply + + @ReturnMapping(OperationResults) + async def ListOperations( + self, + actions=None, + applications=None, + limit=None, + machines=None, + offset=None, + status=None, + units=None, + ): + """ListOperations fetches the called actions for specified apps/units. + + actions : typing.Sequence[str] + applications : typing.Sequence[str] + limit : int + machines : typing.Sequence[str] + offset : int + status : typing.Sequence[str] + units : typing.Sequence[str] + Returns -> OperationResults + """ + if actions is not None and not isinstance(actions, (bytes, str, list)): + raise Exception( + f"Expected actions to be a Sequence, received: {type(actions)}" + ) + + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + if limit is not None and not isinstance(limit, int): + raise Exception(f"Expected limit to be a int, received: {type(limit)}") + + if machines is not None and not isinstance(machines, (bytes, str, list)): + raise Exception( + f"Expected machines to be a Sequence, received: {type(machines)}" + ) + + if offset is not None and not isinstance(offset, int): + raise Exception(f"Expected offset to be a int, received: {type(offset)}") + + if status is not None and not isinstance(status, (bytes, str, list)): + raise Exception( + f"Expected status to be a Sequence, received: {type(status)}" + ) + + if units is not None and not isinstance(units, (bytes, str, list)): + raise Exception(f"Expected units to be a Sequence, received: {type(units)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Action", request="ListOperations", version=7, params=_params) + _params["actions"] = actions + _params["applications"] = applications + _params["limit"] = limit + _params["machines"] = machines + _params["offset"] = offset + _params["status"] = status + _params["units"] = units + reply = await self.rpc(msg) + return reply + + @ReturnMapping(OperationResults) + async def Operations(self, entities=None): + """Operations fetches the specified operation ids. + + entities : typing.Sequence[~Entity] + Returns -> OperationResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Action", request="Operations", version=7, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(EnqueuedActions) + async def Run( + self, + applications=None, + commands=None, + execution_group=None, + machines=None, + parallel=None, + timeout=None, + units=None, + workload_context=None, + ): + """Run the commands specified on the machines identified through the + list of machines, units and services. + + applications : typing.Sequence[str] + commands : str + execution_group : str + machines : typing.Sequence[str] + parallel : bool + timeout : int + units : typing.Sequence[str] + workload_context : bool + Returns -> EnqueuedActions + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + if commands is not None and not isinstance(commands, (bytes, str)): + raise Exception( + f"Expected commands to be a str, received: {type(commands)}" + ) + + if execution_group is not None and not isinstance( + execution_group, (bytes, str) + ): + raise Exception( + f"Expected execution_group to be a str, received: {type(execution_group)}" + ) + + if machines is not None and not isinstance(machines, (bytes, str, list)): + raise Exception( + f"Expected machines to be a Sequence, received: {type(machines)}" + ) + + if parallel is not None and not isinstance(parallel, bool): + raise Exception( + f"Expected parallel to be a bool, received: {type(parallel)}" + ) + + if timeout is not None and not isinstance(timeout, int): + raise Exception(f"Expected timeout to be a int, received: {type(timeout)}") + + if units is not None and not isinstance(units, (bytes, str, list)): + raise Exception(f"Expected units to be a Sequence, received: {type(units)}") + + if workload_context is not None and not isinstance(workload_context, bool): + raise Exception( + f"Expected workload_context to be a bool, received: {type(workload_context)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Action", request="Run", version=7, params=_params) + _params["applications"] = applications + _params["commands"] = commands + _params["execution-group"] = execution_group + _params["machines"] = machines + _params["parallel"] = parallel + _params["timeout"] = timeout + _params["units"] = units + _params["workload-context"] = workload_context + reply = await self.rpc(msg) + return reply + + @ReturnMapping(EnqueuedActions) + async def RunOnAllMachines( + self, + applications=None, + commands=None, + execution_group=None, + machines=None, + parallel=None, + timeout=None, + units=None, + workload_context=None, + ): + """RunOnAllMachines attempts to run the specified command on all the machines. + + applications : typing.Sequence[str] + commands : str + execution_group : str + machines : typing.Sequence[str] + parallel : bool + timeout : int + units : typing.Sequence[str] + workload_context : bool + Returns -> EnqueuedActions + """ + if applications is not None and not isinstance( + applications, (bytes, str, list) + ): + raise Exception( + f"Expected applications to be a Sequence, received: {type(applications)}" + ) + + if commands is not None and not isinstance(commands, (bytes, str)): + raise Exception( + f"Expected commands to be a str, received: {type(commands)}" + ) + + if execution_group is not None and not isinstance( + execution_group, (bytes, str) + ): + raise Exception( + f"Expected execution_group to be a str, received: {type(execution_group)}" + ) + + if machines is not None and not isinstance(machines, (bytes, str, list)): + raise Exception( + f"Expected machines to be a Sequence, received: {type(machines)}" + ) + + if parallel is not None and not isinstance(parallel, bool): + raise Exception( + f"Expected parallel to be a bool, received: {type(parallel)}" + ) + + if timeout is not None and not isinstance(timeout, int): + raise Exception(f"Expected timeout to be a int, received: {type(timeout)}") + + if units is not None and not isinstance(units, (bytes, str, list)): + raise Exception(f"Expected units to be a Sequence, received: {type(units)}") + + if workload_context is not None and not isinstance(workload_context, bool): + raise Exception( + f"Expected workload_context to be a bool, received: {type(workload_context)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Action", request="RunOnAllMachines", version=7, params=_params) + _params["applications"] = applications + _params["commands"] = commands + _params["execution-group"] = execution_group + _params["machines"] = machines + _params["parallel"] = parallel + _params["timeout"] = timeout + _params["units"] = units + _params["workload-context"] = workload_context + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringsWatchResults) + async def WatchActionsProgress(self, entities=None): + """WatchActionsProgress creates a watcher that reports on action log messages. + + entities : typing.Sequence[~Entity] + Returns -> StringsWatchResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Action", request="WatchActionsProgress", version=7, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + +class CharmsFacade(Type): + name = "Charms" + version = 7 + + @ReturnMapping(CharmOriginResult) + async def AddCharm(self, charm_origin=None, force=None, url=None): + """AddCharm adds the given charm URL (which must include revision) to the + environment, if it does not exist yet. Local charms are not supported, + only charm store and charm hub URLs. See also AddLocalCharm(). + + charm_origin : CharmOrigin + force : bool + url : str + Returns -> CharmOriginResult + """ + if charm_origin is not None and not isinstance( + charm_origin, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if url is not None and not isinstance(url, (bytes, str)): + raise Exception(f"Expected url to be a str, received: {type(url)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="AddCharm", version=7, params=_params) + _params["charm-origin"] = charm_origin + _params["force"] = force + _params["url"] = url + reply = await self.rpc(msg) + return reply + + @ReturnMapping(Charm) + async def CharmInfo(self, url=None): + """CharmInfo returns information about the requested charm. + + url : str + Returns -> Charm + """ + if url is not None and not isinstance(url, (bytes, str)): + raise Exception(f"Expected url to be a str, received: {type(url)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="CharmInfo", version=7, params=_params) + _params["url"] = url + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def CheckCharmPlacement(self, placements=None): + """CheckCharmPlacement checks if a charm is allowed to be placed with in a + given application. + + placements : typing.Sequence[~ApplicationCharmPlacement] + Returns -> ErrorResults + """ + if placements is not None and not isinstance(placements, (bytes, str, list)): + raise Exception( + f"Expected placements to be a Sequence, received: {type(placements)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Charms", request="CheckCharmPlacement", version=7, params=_params + ) + _params["placements"] = placements + reply = await self.rpc(msg) + return reply + + @ReturnMapping(DownloadInfoResults) + async def GetDownloadInfos(self, entities=None): + """GetDownloadInfos attempts to get the bundle corresponding to the charm url + and origin. + + entities : typing.Sequence[~CharmURLAndOrigin] + Returns -> DownloadInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="GetDownloadInfos", version=7, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(IsMeteredResult) + async def IsMetered(self, url=None): + """IsMetered returns whether or not the charm is metered. + TODO (cderici) only used for metered charms in cmd MeteredDeployAPI, + kept for client compatibility, remove in juju 4.0 + + url : str + Returns -> IsMeteredResult + """ + if url is not None and not isinstance(url, (bytes, str)): + raise Exception(f"Expected url to be a str, received: {type(url)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="IsMetered", version=7, params=_params) + _params["url"] = url + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CharmsListResult) + async def List(self, names=None): + """List returns a list of charm URLs currently in the state. + If supplied parameter contains any names, the result will + be filtered to return only the charms with supplied names. + + names : typing.Sequence[str] + Returns -> CharmsListResult + """ + if names is not None and not isinstance(names, (bytes, str, list)): + raise Exception(f"Expected names to be a Sequence, received: {type(names)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="List", version=7, params=_params) + _params["names"] = names + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CharmResourcesResults) + async def ListCharmResources(self, entities=None): + """ListCharmResources returns a series of resources for a given charm. + + entities : typing.Sequence[~CharmURLAndOrigin] + Returns -> CharmResourcesResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Charms", request="ListCharmResources", version=7, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ResolveCharmWithChannelResults) + async def ResolveCharms(self, macaroon=None, resolve=None): + """ResolveCharms resolves the given charm URLs with an optionally specified + preferred channel. Channel provided via CharmOrigin. + + macaroon : Macaroon + resolve : typing.Sequence[~ResolveCharmWithChannel] + Returns -> ResolveCharmWithChannelResults + """ + if macaroon is not None and not isinstance(macaroon, (dict, Macaroon)): + raise Exception( + f"Expected macaroon to be a Macaroon, received: {type(macaroon)}" + ) + + if resolve is not None and not isinstance(resolve, (bytes, str, list)): + raise Exception( + f"Expected resolve to be a Sequence, received: {type(resolve)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Charms", request="ResolveCharms", version=7, params=_params) + _params["macaroon"] = macaroon + _params["resolve"] = resolve + reply = await self.rpc(msg) + return reply + + +class ClientFacade(Type): + name = "Client" + version = 7 + + @ReturnMapping(FindToolsResult) + async def FindTools( + self, agentstream=None, arch=None, major=None, number=None, os_type=None + ): + """FindTools returns a List containing all tools matching the given parameters. + TODO(juju 3.1) - remove, used by 2.9 client only + + agentstream : str + arch : str + major : int + number : Number + os_type : str + Returns -> FindToolsResult + """ + if agentstream is not None and not isinstance(agentstream, (bytes, str)): + raise Exception( + f"Expected agentstream to be a str, received: {type(agentstream)}" + ) + + if arch is not None and not isinstance(arch, (bytes, str)): + raise Exception(f"Expected arch to be a str, received: {type(arch)}") + + if major is not None and not isinstance(major, int): + raise Exception(f"Expected major to be a int, received: {type(major)}") + + if number is not None and not isinstance(number, (dict, Number)): + raise Exception(f"Expected number to be a Number, received: {type(number)}") + + if os_type is not None and not isinstance(os_type, (bytes, str)): + raise Exception(f"Expected os_type to be a str, received: {type(os_type)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="FindTools", version=7, params=_params) + _params["agentstream"] = agentstream + _params["arch"] = arch + _params["major"] = major + _params["number"] = number + _params["os-type"] = os_type + reply = await self.rpc(msg) + return reply + + @ReturnMapping(FullStatus) + async def FullStatus(self, include_storage=None, patterns=None): + """FullStatus gives the information needed for juju status over the api + + include_storage : bool + patterns : typing.Sequence[str] + Returns -> FullStatus + """ + if include_storage is not None and not isinstance(include_storage, bool): + raise Exception( + f"Expected include_storage to be a bool, received: {type(include_storage)}" + ) + + if patterns is not None and not isinstance(patterns, (bytes, str, list)): + raise Exception( + f"Expected patterns to be a Sequence, received: {type(patterns)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="FullStatus", version=7, params=_params) + _params["include-storage"] = include_storage + _params["patterns"] = patterns + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StatusHistoryResults) + async def StatusHistory(self, requests=None): + """StatusHistory returns a slice of past statuses for several entities. + + requests : typing.Sequence[~StatusHistoryRequest] + Returns -> StatusHistoryResults + """ + if requests is not None and not isinstance(requests, (bytes, str, list)): + raise Exception( + f"Expected requests to be a Sequence, received: {type(requests)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="StatusHistory", version=7, params=_params) + _params["requests"] = requests + reply = await self.rpc(msg) + return reply + + @ReturnMapping(AllWatcherId) + async def WatchAll(self): + """WatchAll initiates a watcher for entities in the connected model. + + Returns -> AllWatcherId + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="WatchAll", version=7, params=_params) + + reply = await self.rpc(msg) + return reply + + +class CloudFacade(Type): + name = "Cloud" + version = 7 + + @ReturnMapping(None) + async def AddCloud(self, cloud=None, force=None, name=None): + """AddCloud adds a new cloud, different from the one managed by the controller. + + cloud : Cloud + force : bool + name : str + Returns -> None + """ + if cloud is not None and not isinstance(cloud, (dict, Cloud)): + raise Exception(f"Expected cloud to be a Cloud, received: {type(cloud)}") + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + if name is not None and not isinstance(name, (bytes, str)): + raise Exception(f"Expected name to be a str, received: {type(name)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="AddCloud", version=7, params=_params) + _params["cloud"] = cloud + _params["force"] = force + _params["name"] = name + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def AddCredentials(self, credentials=None): + """AddCredentials adds new credentials. + In contrast to UpdateCredentials() below, the new credentials can be + for a cloud that the controller does not manage (this is required + for CAAS models) + + credentials : typing.Sequence[~TaggedCredential] + Returns -> ErrorResults + """ + if credentials is not None and not isinstance(credentials, (bytes, str, list)): + raise Exception( + f"Expected credentials to be a Sequence, received: {type(credentials)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="AddCredentials", version=7, params=_params) + _params["credentials"] = credentials + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UpdateCredentialResults) + async def CheckCredentialsModels(self, credentials=None): + """CheckCredentialsModels validates supplied cloud credentials' content against + models that currently use these credentials. + If there are any models that are using a credential and these models or their + cloud instances are not going to be accessible with corresponding credential, + there will be detailed validation errors per model. + There's no Juju API client which uses this, but JAAS does, + + credentials : typing.Sequence[~TaggedCredential] + Returns -> UpdateCredentialResults + """ + if credentials is not None and not isinstance(credentials, (bytes, str, list)): + raise Exception( + f"Expected credentials to be a Sequence, received: {type(credentials)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Cloud", request="CheckCredentialsModels", version=7, params=_params + ) + _params["credentials"] = credentials + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CloudResults) + async def Cloud(self, entities=None): + """Cloud returns the cloud definitions for the specified clouds. + + entities : typing.Sequence[~Entity] + Returns -> CloudResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="Cloud", version=7, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CloudInfoResults) + async def CloudInfo(self, entities=None): + """CloudInfo returns information about the specified clouds. + + entities : typing.Sequence[~Entity] + Returns -> CloudInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="CloudInfo", version=7, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CloudsResult) + async def Clouds(self): + """Clouds returns the definitions of all clouds supported by the controller + that the logged in user can see. + + Returns -> CloudsResult + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="Clouds", version=7, params=_params) + + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CloudCredentialResults) + async def Credential(self, entities=None): + """Credential returns the specified cloud credential for each tag, minus secrets. + + entities : typing.Sequence[~Entity] + Returns -> CloudCredentialResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="Credential", version=7, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(CredentialContentResults) + async def CredentialContents(self, credentials=None, include_secrets=None): + """CredentialContents returns the specified cloud credentials, + including the secrets if requested. + If no specific credential name/cloud was passed in, all credentials for this user + are returned. + Only credential owner can see its contents as well as what models use it. + Controller admin has no special superpowers here and is treated the same as all other users. + + credentials : typing.Sequence[~CloudCredentialArg] + include_secrets : bool + Returns -> CredentialContentResults + """ + if credentials is not None and not isinstance(credentials, (bytes, str, list)): + raise Exception( + f"Expected credentials to be a Sequence, received: {type(credentials)}" + ) + + if include_secrets is not None and not isinstance(include_secrets, bool): + raise Exception( + f"Expected include_secrets to be a bool, received: {type(include_secrets)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Cloud", request="CredentialContents", version=7, params=_params + ) + _params["credentials"] = credentials + _params["include-secrets"] = include_secrets + reply = await self.rpc(msg) + return reply + + @ReturnMapping(InstanceTypesResults) + async def InstanceTypes(self, constraints=None): + """InstanceTypes returns instance type information for the cloud and region + in which the current model is deployed. + + constraints : typing.Sequence[~CloudInstanceTypesConstraint] + Returns -> InstanceTypesResults + """ + if constraints is not None and not isinstance(constraints, (bytes, str, list)): + raise Exception( + f"Expected constraints to be a Sequence, received: {type(constraints)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="InstanceTypes", version=7, params=_params) + _params["constraints"] = constraints + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ListCloudInfoResults) + async def ListCloudInfo(self, all_=None, user_tag=None): + """ListCloudInfo returns clouds that the specified user has access to. + Controller admins (superuser) can list clouds for any user. + Other users can only ask about their own clouds. + + all_ : bool + user_tag : str + Returns -> ListCloudInfoResults + """ + if all_ is not None and not isinstance(all_, bool): + raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") + + if user_tag is not None and not isinstance(user_tag, (bytes, str)): + raise Exception( + f"Expected user_tag to be a str, received: {type(user_tag)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="ListCloudInfo", version=7, params=_params) + _params["all"] = all_ + _params["user-tag"] = user_tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ModifyCloudAccess(self, changes=None): + """ModifyCloudAccess changes the model access granted to users. + + changes : typing.Sequence[~ModifyCloudAccess] + Returns -> ErrorResults + """ + if changes is not None and not isinstance(changes, (bytes, str, list)): + raise Exception( + f"Expected changes to be a Sequence, received: {type(changes)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="ModifyCloudAccess", version=7, params=_params) + _params["changes"] = changes + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def RemoveClouds(self, entities=None): + """RemoveClouds removes the specified clouds from the controller. + If a cloud is in use (has models deployed to it), the removal will fail. + + entities : typing.Sequence[~Entity] + Returns -> ErrorResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="RemoveClouds", version=7, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def RevokeCredentialsCheckModels(self, credentials=None): + """RevokeCredentialsCheckModels revokes a set of cloud credentials. + If the credentials are used by any of the models, the credential deletion will be aborted. + If credential-in-use needs to be revoked nonetheless, this method allows the use of force. + + credentials : typing.Sequence[~RevokeCredentialArg] + Returns -> ErrorResults + """ + if credentials is not None and not isinstance(credentials, (bytes, str, list)): + raise Exception( + f"Expected credentials to be a Sequence, received: {type(credentials)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Cloud", + request="RevokeCredentialsCheckModels", + version=7, + params=_params, + ) + _params["credentials"] = credentials + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UpdateCloud(self, clouds=None): + """UpdateCloud updates an existing cloud that the controller knows about. + + clouds : typing.Sequence[~AddCloudArgs] + Returns -> ErrorResults + """ + if clouds is not None and not isinstance(clouds, (bytes, str, list)): + raise Exception( + f"Expected clouds to be a Sequence, received: {type(clouds)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="UpdateCloud", version=7, params=_params) + _params["clouds"] = clouds + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UpdateCredentialResults) + async def UpdateCredentialsCheckModels(self, credentials=None, force=None): + """UpdateCredentialsCheckModels updates a set of cloud credentials' content. + If there are any models that are using a credential and these models + are not going to be visible with updated credential content, + there will be detailed validation errors per model. Such model errors are returned + separately and do not contribute to the overall method error status. + Controller admins can 'force' an update of the credential + regardless of whether it is deemed valid or not. + + credentials : typing.Sequence[~TaggedCredential] + force : bool + Returns -> UpdateCredentialResults + """ + if credentials is not None and not isinstance(credentials, (bytes, str, list)): + raise Exception( + f"Expected credentials to be a Sequence, received: {type(credentials)}" + ) + + if force is not None and not isinstance(force, bool): + raise Exception(f"Expected force to be a bool, received: {type(force)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="Cloud", + request="UpdateCredentialsCheckModels", + version=7, + params=_params, + ) + _params["credentials"] = credentials + _params["force"] = force + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringsResults) + async def UserCredentials(self, user_clouds=None): + """UserCredentials returns the cloud credentials for a set of users. + + user_clouds : typing.Sequence[~UserCloud] + Returns -> StringsResults + """ + if user_clouds is not None and not isinstance(user_clouds, (bytes, str, list)): + raise Exception( + f"Expected user_clouds to be a Sequence, received: {type(user_clouds)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Cloud", request="UserCredentials", version=7, params=_params) + _params["user-clouds"] = user_clouds + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client8.py b/build/lib/juju/client/_client8.py new file mode 100644 index 00000000..e1f061c4 --- /dev/null +++ b/build/lib/juju/client/_client8.py @@ -0,0 +1,68 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class ClientFacade(Type): + name = "Client" + version = 8 + + @ReturnMapping(FullStatus) + async def FullStatus(self, include_storage=None, patterns=None): + """FullStatus gives the information needed for juju status over the api + + include_storage : bool + patterns : typing.Sequence[str] + Returns -> FullStatus + """ + if include_storage is not None and not isinstance(include_storage, bool): + raise Exception( + f"Expected include_storage to be a bool, received: {type(include_storage)}" + ) + + if patterns is not None and not isinstance(patterns, (bytes, str, list)): + raise Exception( + f"Expected patterns to be a Sequence, received: {type(patterns)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="FullStatus", version=8, params=_params) + _params["include-storage"] = include_storage + _params["patterns"] = patterns + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StatusHistoryResults) + async def StatusHistory(self, requests=None): + """StatusHistory returns a slice of past statuses for several entities. + + requests : typing.Sequence[~StatusHistoryRequest] + Returns -> StatusHistoryResults + """ + if requests is not None and not isinstance(requests, (bytes, str, list)): + raise Exception( + f"Expected requests to be a Sequence, received: {type(requests)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="StatusHistory", version=8, params=_params) + _params["requests"] = requests + reply = await self.rpc(msg) + return reply + + @ReturnMapping(AllWatcherId) + async def WatchAll(self): + """WatchAll initiates a watcher for entities in the connected model. + + Returns -> AllWatcherId + """ + # map input types to rpc msg + _params = dict() + msg = dict(type="Client", request="WatchAll", version=8, params=_params) + + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_client9.py b/build/lib/juju/client/_client9.py new file mode 100644 index 00000000..7709e7e3 --- /dev/null +++ b/build/lib/juju/client/_client9.py @@ -0,0 +1,347 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client._definitions import * +from juju.client.facade import ReturnMapping, Type + + +class ModelManagerFacade(Type): + name = "ModelManager" + version = 9 + + @ReturnMapping(ErrorResults) + async def ChangeModelCredential(self, model_credentials=None): + """ChangeModelCredential changes cloud credential reference for models. + These new cloud credentials must already exist on the controller. + + model_credentials : typing.Sequence[~ChangeModelCredentialParams] + Returns -> ErrorResults + """ + if model_credentials is not None and not isinstance( + model_credentials, (bytes, str, list) + ): + raise Exception( + f"Expected model_credentials to be a Sequence, received: {type(model_credentials)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", + request="ChangeModelCredential", + version=9, + params=_params, + ) + _params["model-credentials"] = model_credentials + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelInfo) + async def CreateModel( + self, + cloud_tag=None, + config=None, + credential=None, + name=None, + owner_tag=None, + region=None, + ): + """CreateModel creates a new model using the account and + model config specified in the args. + + cloud_tag : str + config : typing.Mapping[str, typing.Any] + credential : str + name : str + owner_tag : str + region : str + Returns -> ModelInfo + """ + if cloud_tag is not None and not isinstance(cloud_tag, (bytes, str)): + raise Exception( + f"Expected cloud_tag to be a str, received: {type(cloud_tag)}" + ) + + if config is not None and not isinstance(config, dict): + raise Exception( + f"Expected config to be a Mapping, received: {type(config)}" + ) + + if credential is not None and not isinstance(credential, (bytes, str)): + raise Exception( + f"Expected credential to be a str, received: {type(credential)}" + ) + + if name is not None and not isinstance(name, (bytes, str)): + raise Exception(f"Expected name to be a str, received: {type(name)}") + + if owner_tag is not None and not isinstance(owner_tag, (bytes, str)): + raise Exception( + f"Expected owner_tag to be a str, received: {type(owner_tag)}" + ) + + if region is not None and not isinstance(region, (bytes, str)): + raise Exception(f"Expected region to be a str, received: {type(region)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="CreateModel", version=9, params=_params + ) + _params["cloud-tag"] = cloud_tag + _params["config"] = config + _params["credential"] = credential + _params["name"] = name + _params["owner-tag"] = owner_tag + _params["region"] = region + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def DestroyModels(self, models=None): + """DestroyModels will try to destroy the specified models. + If there is a block on destruction, this method will return an error. + From ModelManager v7 onwards, DestroyModels gains 'force' and 'max-wait' parameters. + + models : typing.Sequence[~DestroyModelParams] + Returns -> ErrorResults + """ + if models is not None and not isinstance(models, (bytes, str, list)): + raise Exception( + f"Expected models to be a Sequence, received: {type(models)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="DestroyModels", version=9, params=_params + ) + _params["models"] = models + reply = await self.rpc(msg) + return reply + + @ReturnMapping(StringResults) + async def DumpModels(self, entities=None, simplified=None): + """DumpModels will export the models into the database agnostic + representation. The user needs to either be a controller admin, or have + admin privileges on the model itself. + + entities : typing.Sequence[~Entity] + simplified : bool + Returns -> StringResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + if simplified is not None and not isinstance(simplified, bool): + raise Exception( + f"Expected simplified to be a bool, received: {type(simplified)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelManager", request="DumpModels", version=9, params=_params) + _params["entities"] = entities + _params["simplified"] = simplified + reply = await self.rpc(msg) + return reply + + @ReturnMapping(MapResults) + async def DumpModelsDB(self, entities=None): + """DumpModelsDB will gather all documents from all model collections + for the specified model. The map result contains a map of collection + names to lists of documents represented as maps. + + entities : typing.Sequence[~Entity] + Returns -> MapResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="DumpModelsDB", version=9, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelSummaryResults) + async def ListModelSummaries(self, all_=None, user_tag=None): + """ListModelSummaries returns models that the specified user + has access to in the current server. Controller admins (superuser) + can list models for any user. Other users + can only ask about their own models. + + all_ : bool + user_tag : str + Returns -> ModelSummaryResults + """ + if all_ is not None and not isinstance(all_, bool): + raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") + + if user_tag is not None and not isinstance(user_tag, (bytes, str)): + raise Exception( + f"Expected user_tag to be a str, received: {type(user_tag)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="ListModelSummaries", version=9, params=_params + ) + _params["all"] = all_ + _params["user-tag"] = user_tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(UserModelList) + async def ListModels(self, tag=None): + """ListModels returns the models that the specified user + has access to in the current server. Controller admins (superuser) + can list models for any user. Other users + can only ask about their own models. + + tag : str + Returns -> UserModelList + """ + if tag is not None and not isinstance(tag, (bytes, str)): + raise Exception(f"Expected tag to be a str, received: {type(tag)}") + + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelManager", request="ListModels", version=9, params=_params) + _params["tag"] = tag + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelDefaultsResults) + async def ModelDefaultsForClouds(self, entities=None): + """ModelDefaultsForClouds returns the default config values for the specified + clouds. + + entities : typing.Sequence[~Entity] + Returns -> ModelDefaultsResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", + request="ModelDefaultsForClouds", + version=9, + params=_params, + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelInfoResults) + async def ModelInfo(self, entities=None): + """ModelInfo returns information about the specified models. + + entities : typing.Sequence[~Entity] + Returns -> ModelInfoResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict(type="ModelManager", request="ModelInfo", version=9, params=_params) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ModelStatusResults) + async def ModelStatus(self, entities=None): + """ModelStatus returns a summary of the model. + + entities : typing.Sequence[~Entity] + Returns -> ModelStatusResults + """ + if entities is not None and not isinstance(entities, (bytes, str, list)): + raise Exception( + f"Expected entities to be a Sequence, received: {type(entities)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="ModelStatus", version=9, params=_params + ) + _params["entities"] = entities + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def ModifyModelAccess(self, changes=None): + """ModifyModelAccess changes the model access granted to users. + + changes : typing.Sequence[~ModifyModelAccess] + Returns -> ErrorResults + """ + if changes is not None and not isinstance(changes, (bytes, str, list)): + raise Exception( + f"Expected changes to be a Sequence, received: {type(changes)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="ModifyModelAccess", version=9, params=_params + ) + _params["changes"] = changes + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def SetModelDefaults(self, config=None): + """SetModelDefaults writes new values for the specified default model settings. + + config : typing.Sequence[~ModelDefaultValues] + Returns -> ErrorResults + """ + if config is not None and not isinstance(config, (bytes, str, list)): + raise Exception( + f"Expected config to be a Sequence, received: {type(config)}" + ) + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="SetModelDefaults", version=9, params=_params + ) + _params["config"] = config + reply = await self.rpc(msg) + return reply + + @ReturnMapping(ErrorResults) + async def UnsetModelDefaults(self, keys=None): + """UnsetModelDefaults removes the specified default model settings. + + keys : typing.Sequence[~ModelUnsetKeys] + Returns -> ErrorResults + """ + if keys is not None and not isinstance(keys, (bytes, str, list)): + raise Exception(f"Expected keys to be a Sequence, received: {type(keys)}") + + # map input types to rpc msg + _params = dict() + msg = dict( + type="ModelManager", request="UnsetModelDefaults", version=9, params=_params + ) + _params["keys"] = keys + reply = await self.rpc(msg) + return reply diff --git a/build/lib/juju/client/_definitions.py b/build/lib/juju/client/_definitions.py new file mode 100644 index 00000000..b01afc5e --- /dev/null +++ b/build/lib/juju/client/_definitions.py @@ -0,0 +1,22723 @@ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +from juju.client.facade import Type + + +class AccessInfo(Type): + _toSchema = {"role": "role", "scope_tag": "scope-tag", "target_tag": "target-tag"} + _toPy = {"role": "role", "scope-tag": "scope_tag", "target-tag": "target_tag"} + + def __init__(self, role=None, scope_tag=None, target_tag=None, **unknown_fields): + """Role : str + scope_tag : str + target_tag : str + """ + role_ = role + scope_tag_ = scope_tag + target_tag_ = target_tag + + # Validate arguments against known Juju API types. + if role_ is not None and not isinstance(role_, (bytes, str)): + raise Exception(f"Expected role_ to be a str, received: {type(role_)}") + + if scope_tag_ is not None and not isinstance(scope_tag_, (bytes, str)): + raise Exception( + f"Expected scope_tag_ to be a str, received: {type(scope_tag_)}" + ) + + if target_tag_ is not None and not isinstance(target_tag_, (bytes, str)): + raise Exception( + f"Expected target_tag_ to be a str, received: {type(target_tag_)}" + ) + + self.role = role_ + self.scope_tag = scope_tag_ + self.target_tag = target_tag_ + self.unknown_fields = unknown_fields + + +class Action(Type): + _toSchema = { + "execution_group": "execution-group", + "name": "name", + "parallel": "parallel", + "parameters": "parameters", + "receiver": "receiver", + "tag": "tag", + } + _toPy = { + "execution-group": "execution_group", + "name": "name", + "parallel": "parallel", + "parameters": "parameters", + "receiver": "receiver", + "tag": "tag", + } + + def __init__( + self, + execution_group=None, + name=None, + parallel=None, + parameters=None, + receiver=None, + tag=None, + **unknown_fields, + ): + """execution_group : str + name : str + parallel : bool + parameters : typing.Mapping[str, typing.Any] + receiver : str + tag : str + """ + execution_group_ = execution_group + name_ = name + parallel_ = parallel + parameters_ = parameters + receiver_ = receiver + tag_ = tag + + # Validate arguments against known Juju API types. + if execution_group_ is not None and not isinstance( + execution_group_, (bytes, str) + ): + raise Exception( + f"Expected execution_group_ to be a str, received: {type(execution_group_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if parallel_ is not None and not isinstance(parallel_, bool): + raise Exception( + f"Expected parallel_ to be a bool, received: {type(parallel_)}" + ) + + if parameters_ is not None and not isinstance(parameters_, dict): + raise Exception( + f"Expected parameters_ to be a Mapping, received: {type(parameters_)}" + ) + + if receiver_ is not None and not isinstance(receiver_, (bytes, str)): + raise Exception( + f"Expected receiver_ to be a str, received: {type(receiver_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.execution_group = execution_group_ + self.name = name_ + self.parallel = parallel_ + self.parameters = parameters_ + self.receiver = receiver_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class ActionMessage(Type): + _toSchema = {"message": "message", "timestamp": "timestamp"} + _toPy = {"message": "message", "timestamp": "timestamp"} + + def __init__(self, message=None, timestamp=None, **unknown_fields): + """Message : str + timestamp : str + """ + message_ = message + timestamp_ = timestamp + + # Validate arguments against known Juju API types. + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + if timestamp_ is not None and not isinstance(timestamp_, (bytes, str)): + raise Exception( + f"Expected timestamp_ to be a str, received: {type(timestamp_)}" + ) + + self.message = message_ + self.timestamp = timestamp_ + self.unknown_fields = unknown_fields + + +class ActionResult(Type): + _toSchema = { + "action": "action", + "completed": "completed", + "enqueued": "enqueued", + "error": "error", + "log": "log", + "message": "message", + "output": "output", + "started": "started", + "status": "status", + } + _toPy = { + "action": "action", + "completed": "completed", + "enqueued": "enqueued", + "error": "error", + "log": "log", + "message": "message", + "output": "output", + "started": "started", + "status": "status", + } + + def __init__( + self, + action=None, + completed=None, + enqueued=None, + error=None, + log=None, + message=None, + output=None, + started=None, + status=None, + **unknown_fields, + ): + """Action : Action + completed : str + enqueued : str + error : Error + log : typing.Sequence[~ActionMessage] + message : str + output : typing.Mapping[str, typing.Any] + started : str + status : str + """ + action_ = Action.from_json(action) if action else None + completed_ = completed + enqueued_ = enqueued + error_ = Error.from_json(error) if error else None + log_ = [ActionMessage.from_json(o) for o in log or []] + message_ = message + output_ = output + started_ = started + status_ = status + + # Validate arguments against known Juju API types. + if action_ is not None and not isinstance(action_, (dict, Action)): + raise Exception( + f"Expected action_ to be a Action, received: {type(action_)}" + ) + + if completed_ is not None and not isinstance(completed_, (bytes, str)): + raise Exception( + f"Expected completed_ to be a str, received: {type(completed_)}" + ) + + if enqueued_ is not None and not isinstance(enqueued_, (bytes, str)): + raise Exception( + f"Expected enqueued_ to be a str, received: {type(enqueued_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if log_ is not None and not isinstance(log_, (bytes, str, list)): + raise Exception(f"Expected log_ to be a Sequence, received: {type(log_)}") + + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + if output_ is not None and not isinstance(output_, dict): + raise Exception( + f"Expected output_ to be a Mapping, received: {type(output_)}" + ) + + if started_ is not None and not isinstance(started_, (bytes, str)): + raise Exception( + f"Expected started_ to be a str, received: {type(started_)}" + ) + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + self.action = action_ + self.completed = completed_ + self.enqueued = enqueued_ + self.error = error_ + self.log = log_ + self.message = message_ + self.output = output_ + self.started = started_ + self.status = status_ + self.unknown_fields = unknown_fields + + +class ActionResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ActionResult]""" + results_ = [ActionResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ActionSpec(Type): + _toSchema = {"description": "description", "params": "params"} + _toPy = {"description": "description", "params": "params"} + + def __init__(self, description=None, params=None, **unknown_fields): + """Description : str + params : typing.Mapping[str, typing.Any] + """ + description_ = description + params_ = params + + # Validate arguments against known Juju API types. + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if params_ is not None and not isinstance(params_, dict): + raise Exception( + f"Expected params_ to be a Mapping, received: {type(params_)}" + ) + + self.description = description_ + self.params = params_ + self.unknown_fields = unknown_fields + + +class Actions(Type): + _toSchema = {"actions": "actions"} + _toPy = {"actions": "actions"} + + def __init__(self, actions=None, **unknown_fields): + """Actions : typing.Sequence[~Action]""" + actions_ = [Action.from_json(o) for o in actions or []] + + # Validate arguments against known Juju API types. + if actions_ is not None and not isinstance(actions_, (bytes, str, list)): + raise Exception( + f"Expected actions_ to be a Sequence, received: {type(actions_)}" + ) + + self.actions = actions_ + self.unknown_fields = unknown_fields + + +class AddApplicationOffer(Type): + _toSchema = { + "application_description": "application-description", + "application_name": "application-name", + "endpoints": "endpoints", + "model_tag": "model-tag", + "offer_name": "offer-name", + "owner_tag": "owner-tag", + } + _toPy = { + "application-description": "application_description", + "application-name": "application_name", + "endpoints": "endpoints", + "model-tag": "model_tag", + "offer-name": "offer_name", + "owner-tag": "owner_tag", + } + + def __init__( + self, + application_description=None, + application_name=None, + endpoints=None, + model_tag=None, + offer_name=None, + owner_tag=None, + **unknown_fields, + ): + """application_description : str + application_name : str + endpoints : typing.Mapping[str, str] + model_tag : str + offer_name : str + owner_tag : str + """ + application_description_ = application_description + application_name_ = application_name + endpoints_ = endpoints + model_tag_ = model_tag + offer_name_ = offer_name + owner_tag_ = owner_tag + + # Validate arguments against known Juju API types. + if application_description_ is not None and not isinstance( + application_description_, (bytes, str) + ): + raise Exception( + f"Expected application_description_ to be a str, received: {type(application_description_)}" + ) + + if application_name_ is not None and not isinstance( + application_name_, (bytes, str) + ): + raise Exception( + f"Expected application_name_ to be a str, received: {type(application_name_)}" + ) + + if endpoints_ is not None and not isinstance(endpoints_, dict): + raise Exception( + f"Expected endpoints_ to be a Mapping, received: {type(endpoints_)}" + ) + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + self.application_description = application_description_ + self.application_name = application_name_ + self.endpoints = endpoints_ + self.model_tag = model_tag_ + self.offer_name = offer_name_ + self.owner_tag = owner_tag_ + self.unknown_fields = unknown_fields + + +class AddApplicationOffers(Type): + _toSchema = {"offers": "Offers"} + _toPy = {"Offers": "offers"} + + def __init__(self, offers=None, **unknown_fields): + """Offers : typing.Sequence[~AddApplicationOffer]""" + offers_ = [AddApplicationOffer.from_json(o) for o in offers or []] + + # Validate arguments against known Juju API types. + if offers_ is not None and not isinstance(offers_, (bytes, str, list)): + raise Exception( + f"Expected offers_ to be a Sequence, received: {type(offers_)}" + ) + + self.offers = offers_ + self.unknown_fields = unknown_fields + + +class AddApplicationUnits(Type): + _toSchema = { + "application": "application", + "attach_storage": "attach-storage", + "num_units": "num-units", + "placement": "placement", + "policy": "policy", + } + _toPy = { + "application": "application", + "attach-storage": "attach_storage", + "num-units": "num_units", + "placement": "placement", + "policy": "policy", + } + + def __init__( + self, + application=None, + attach_storage=None, + num_units=None, + placement=None, + policy=None, + **unknown_fields, + ): + """Application : str + attach_storage : typing.Sequence[str] + num_units : int + placement : typing.Sequence[~Placement] + policy : str + """ + application_ = application + attach_storage_ = attach_storage + num_units_ = num_units + placement_ = [Placement.from_json(o) for o in placement or []] + policy_ = policy + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if attach_storage_ is not None and not isinstance( + attach_storage_, (bytes, str, list) + ): + raise Exception( + f"Expected attach_storage_ to be a Sequence, received: {type(attach_storage_)}" + ) + + if num_units_ is not None and not isinstance(num_units_, int): + raise Exception( + f"Expected num_units_ to be a int, received: {type(num_units_)}" + ) + + if placement_ is not None and not isinstance(placement_, (bytes, str, list)): + raise Exception( + f"Expected placement_ to be a Sequence, received: {type(placement_)}" + ) + + if policy_ is not None and not isinstance(policy_, (bytes, str)): + raise Exception(f"Expected policy_ to be a str, received: {type(policy_)}") + + self.application = application_ + self.attach_storage = attach_storage_ + self.num_units = num_units_ + self.placement = placement_ + self.policy = policy_ + self.unknown_fields = unknown_fields + + +class AddApplicationUnitsResults(Type): + _toSchema = {"units": "units"} + _toPy = {"units": "units"} + + def __init__(self, units=None, **unknown_fields): + """Units : typing.Sequence[str]""" + units_ = units + + # Validate arguments against known Juju API types. + if units_ is not None and not isinstance(units_, (bytes, str, list)): + raise Exception( + f"Expected units_ to be a Sequence, received: {type(units_)}" + ) + + self.units = units_ + self.unknown_fields = unknown_fields + + +class AddCharmWithOrigin(Type): + _toSchema = {"charm_origin": "charm-origin", "force": "force", "url": "url"} + _toPy = {"charm-origin": "charm_origin", "force": "force", "url": "url"} + + def __init__(self, charm_origin=None, force=None, url=None, **unknown_fields): + """charm_origin : CharmOrigin + force : bool + url : str + """ + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + force_ = force + url_ = url + + # Validate arguments against known Juju API types. + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if url_ is not None and not isinstance(url_, (bytes, str)): + raise Exception(f"Expected url_ to be a str, received: {type(url_)}") + + self.charm_origin = charm_origin_ + self.force = force_ + self.url = url_ + self.unknown_fields = unknown_fields + + +class AddCloudArgs(Type): + _toSchema = {"cloud": "cloud", "force": "force", "name": "name"} + _toPy = {"cloud": "cloud", "force": "force", "name": "name"} + + def __init__(self, cloud=None, force=None, name=None, **unknown_fields): + """Cloud : Cloud + force : bool + name : str + """ + cloud_ = Cloud.from_json(cloud) if cloud else None + force_ = force + name_ = name + + # Validate arguments against known Juju API types. + if cloud_ is not None and not isinstance(cloud_, (dict, Cloud)): + raise Exception(f"Expected cloud_ to be a Cloud, received: {type(cloud_)}") + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + self.cloud = cloud_ + self.force = force_ + self.name = name_ + self.unknown_fields = unknown_fields + + +class AddMachineParams(Type): + _toSchema = { + "addresses": "addresses", + "base": "base", + "constraints": "constraints", + "container_type": "container-type", + "disks": "disks", + "hardware_characteristics": "hardware-characteristics", + "instance_id": "instance-id", + "jobs": "jobs", + "nonce": "nonce", + "parent_id": "parent-id", + "placement": "placement", + } + _toPy = { + "addresses": "addresses", + "base": "base", + "constraints": "constraints", + "container-type": "container_type", + "disks": "disks", + "hardware-characteristics": "hardware_characteristics", + "instance-id": "instance_id", + "jobs": "jobs", + "nonce": "nonce", + "parent-id": "parent_id", + "placement": "placement", + } + + def __init__( + self, + addresses=None, + base=None, + constraints=None, + container_type=None, + disks=None, + hardware_characteristics=None, + instance_id=None, + jobs=None, + nonce=None, + parent_id=None, + placement=None, + **unknown_fields, + ): + """Addresses : typing.Sequence[~Address] + base : Base + constraints : Value + container_type : str + disks : typing.Sequence[~Constraints] + hardware_characteristics : HardwareCharacteristics + instance_id : str + jobs : typing.Sequence[str] + nonce : str + parent_id : str + placement : Placement + """ + addresses_ = [Address.from_json(o) for o in addresses or []] + base_ = Base.from_json(base) if base else None + constraints_ = Value.from_json(constraints) if constraints else None + container_type_ = container_type + disks_ = [Constraints.from_json(o) for o in disks or []] + hardware_characteristics_ = ( + HardwareCharacteristics.from_json(hardware_characteristics) + if hardware_characteristics + else None + ) + instance_id_ = instance_id + jobs_ = jobs + nonce_ = nonce + parent_id_ = parent_id + placement_ = Placement.from_json(placement) if placement else None + + # Validate arguments against known Juju API types. + if addresses_ is not None and not isinstance(addresses_, (bytes, str, list)): + raise Exception( + f"Expected addresses_ to be a Sequence, received: {type(addresses_)}" + ) + + if base_ is not None and not isinstance(base_, (dict, Base)): + raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") + + if constraints_ is not None and not isinstance(constraints_, (dict, Value)): + raise Exception( + f"Expected constraints_ to be a Value, received: {type(constraints_)}" + ) + + if container_type_ is not None and not isinstance( + container_type_, (bytes, str) + ): + raise Exception( + f"Expected container_type_ to be a str, received: {type(container_type_)}" + ) + + if disks_ is not None and not isinstance(disks_, (bytes, str, list)): + raise Exception( + f"Expected disks_ to be a Sequence, received: {type(disks_)}" + ) + + if hardware_characteristics_ is not None and not isinstance( + hardware_characteristics_, (dict, HardwareCharacteristics) + ): + raise Exception( + f"Expected hardware_characteristics_ to be a HardwareCharacteristics, received: {type(hardware_characteristics_)}" + ) + + if instance_id_ is not None and not isinstance(instance_id_, (bytes, str)): + raise Exception( + f"Expected instance_id_ to be a str, received: {type(instance_id_)}" + ) + + if jobs_ is not None and not isinstance(jobs_, (bytes, str, list)): + raise Exception(f"Expected jobs_ to be a Sequence, received: {type(jobs_)}") + + if nonce_ is not None and not isinstance(nonce_, (bytes, str)): + raise Exception(f"Expected nonce_ to be a str, received: {type(nonce_)}") + + if parent_id_ is not None and not isinstance(parent_id_, (bytes, str)): + raise Exception( + f"Expected parent_id_ to be a str, received: {type(parent_id_)}" + ) + + if placement_ is not None and not isinstance(placement_, (dict, Placement)): + raise Exception( + f"Expected placement_ to be a Placement, received: {type(placement_)}" + ) + + self.addresses = addresses_ + self.base = base_ + self.constraints = constraints_ + self.container_type = container_type_ + self.disks = disks_ + self.hardware_characteristics = hardware_characteristics_ + self.instance_id = instance_id_ + self.jobs = jobs_ + self.nonce = nonce_ + self.parent_id = parent_id_ + self.placement = placement_ + self.unknown_fields = unknown_fields + + +class AddMachines(Type): + _toSchema = {"params": "params"} + _toPy = {"params": "params"} + + def __init__(self, params=None, **unknown_fields): + """Params : typing.Sequence[~AddMachineParams]""" + params_ = [AddMachineParams.from_json(o) for o in params or []] + + # Validate arguments against known Juju API types. + if params_ is not None and not isinstance(params_, (bytes, str, list)): + raise Exception( + f"Expected params_ to be a Sequence, received: {type(params_)}" + ) + + self.params = params_ + self.unknown_fields = unknown_fields + + +class AddMachinesResult(Type): + _toSchema = {"error": "error", "machine": "machine"} + _toPy = {"error": "error", "machine": "machine"} + + def __init__(self, error=None, machine=None, **unknown_fields): + """Error : Error + machine : str + """ + error_ = Error.from_json(error) if error else None + machine_ = machine + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if machine_ is not None and not isinstance(machine_, (bytes, str)): + raise Exception( + f"Expected machine_ to be a str, received: {type(machine_)}" + ) + + self.error = error_ + self.machine = machine_ + self.unknown_fields = unknown_fields + + +class AddMachinesResults(Type): + _toSchema = {"machines": "machines"} + _toPy = {"machines": "machines"} + + def __init__(self, machines=None, **unknown_fields): + """Machines : typing.Sequence[~AddMachinesResult]""" + machines_ = [AddMachinesResult.from_json(o) for o in machines or []] + + # Validate arguments against known Juju API types. + if machines_ is not None and not isinstance(machines_, (bytes, str, list)): + raise Exception( + f"Expected machines_ to be a Sequence, received: {type(machines_)}" + ) + + self.machines = machines_ + self.unknown_fields = unknown_fields + + +class AddPendingResourcesArgsV2(Type): + _toSchema = { + "charm_origin": "charm-origin", + "entity": "Entity", + "macaroon": "macaroon", + "resources": "resources", + "tag": "tag", + "url": "url", + } + _toPy = { + "Entity": "entity", + "charm-origin": "charm_origin", + "macaroon": "macaroon", + "resources": "resources", + "tag": "tag", + "url": "url", + } + + def __init__( + self, + entity=None, + charm_origin=None, + macaroon=None, + resources=None, + tag=None, + url=None, + **unknown_fields, + ): + """Entity : Entity + charm_origin : CharmOrigin + macaroon : Macaroon + resources : typing.Sequence[~CharmResource] + tag : str + url : str + """ + entity_ = Entity.from_json(entity) if entity else None + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + macaroon_ = Macaroon.from_json(macaroon) if macaroon else None + resources_ = [CharmResource.from_json(o) for o in resources or []] + tag_ = tag + url_ = url + + # Validate arguments against known Juju API types. + if entity_ is not None and not isinstance(entity_, (dict, Entity)): + raise Exception( + f"Expected entity_ to be a Entity, received: {type(entity_)}" + ) + + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): + raise Exception( + f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" + ) + + if resources_ is not None and not isinstance(resources_, (bytes, str, list)): + raise Exception( + f"Expected resources_ to be a Sequence, received: {type(resources_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + if url_ is not None and not isinstance(url_, (bytes, str)): + raise Exception(f"Expected url_ to be a str, received: {type(url_)}") + + self.entity = entity_ + self.charm_origin = charm_origin_ + self.macaroon = macaroon_ + self.resources = resources_ + self.tag = tag_ + self.url = url_ + self.unknown_fields = unknown_fields + + +class AddPendingResourcesResult(Type): + _toSchema = { + "error": "error", + "errorresult": "ErrorResult", + "pending_ids": "pending-ids", + } + _toPy = { + "ErrorResult": "errorresult", + "error": "error", + "pending-ids": "pending_ids", + } + + def __init__( + self, errorresult=None, error=None, pending_ids=None, **unknown_fields + ): + """Errorresult : ErrorResult + error : Error + pending_ids : typing.Sequence[str] + """ + errorresult_ = ErrorResult.from_json(errorresult) if errorresult else None + error_ = Error.from_json(error) if error else None + pending_ids_ = pending_ids + + # Validate arguments against known Juju API types. + if errorresult_ is not None and not isinstance( + errorresult_, (dict, ErrorResult) + ): + raise Exception( + f"Expected errorresult_ to be a ErrorResult, received: {type(errorresult_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if pending_ids_ is not None and not isinstance( + pending_ids_, (bytes, str, list) + ): + raise Exception( + f"Expected pending_ids_ to be a Sequence, received: {type(pending_ids_)}" + ) + + self.errorresult = errorresult_ + self.error = error_ + self.pending_ids = pending_ids_ + self.unknown_fields = unknown_fields + + +class AddRelation(Type): + _toSchema = {"endpoints": "endpoints", "via_cidrs": "via-cidrs"} + _toPy = {"endpoints": "endpoints", "via-cidrs": "via_cidrs"} + + def __init__(self, endpoints=None, via_cidrs=None, **unknown_fields): + """Endpoints : typing.Sequence[str] + via_cidrs : typing.Sequence[str] + """ + endpoints_ = endpoints + via_cidrs_ = via_cidrs + + # Validate arguments against known Juju API types. + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if via_cidrs_ is not None and not isinstance(via_cidrs_, (bytes, str, list)): + raise Exception( + f"Expected via_cidrs_ to be a Sequence, received: {type(via_cidrs_)}" + ) + + self.endpoints = endpoints_ + self.via_cidrs = via_cidrs_ + self.unknown_fields = unknown_fields + + +class AddRelationResults(Type): + _toSchema = {"endpoints": "endpoints"} + _toPy = {"endpoints": "endpoints"} + + def __init__(self, endpoints=None, **unknown_fields): + """Endpoints : typing.Mapping[str, ~CharmRelation]""" + endpoints_ = { + k: CharmRelation.from_json(v) for k, v in (endpoints or dict()).items() + } + + # Validate arguments against known Juju API types. + if endpoints_ is not None and not isinstance(endpoints_, dict): + raise Exception( + f"Expected endpoints_ to be a Mapping, received: {type(endpoints_)}" + ) + + self.endpoints = endpoints_ + self.unknown_fields = unknown_fields + + +class AddSecretBackendArg(Type): + _toSchema = { + "backend_type": "backend-type", + "config": "config", + "id_": "id", + "name": "name", + "secretbackend": "SecretBackend", + "token_rotate_interval": "token-rotate-interval", + } + _toPy = { + "SecretBackend": "secretbackend", + "backend-type": "backend_type", + "config": "config", + "id": "id_", + "name": "name", + "token-rotate-interval": "token_rotate_interval", + } + + def __init__( + self, + secretbackend=None, + backend_type=None, + config=None, + id_=None, + name=None, + token_rotate_interval=None, + **unknown_fields, + ): + """Secretbackend : SecretBackend + backend_type : str + config : typing.Mapping[str, typing.Any] + id_ : str + name : str + token_rotate_interval : int + """ + secretbackend_ = ( + SecretBackend.from_json(secretbackend) if secretbackend else None + ) + backend_type_ = backend_type + config_ = config + id__ = id_ + name_ = name + token_rotate_interval_ = token_rotate_interval + + # Validate arguments against known Juju API types. + if secretbackend_ is not None and not isinstance( + secretbackend_, (dict, SecretBackend) + ): + raise Exception( + f"Expected secretbackend_ to be a SecretBackend, received: {type(secretbackend_)}" + ) + + if backend_type_ is not None and not isinstance(backend_type_, (bytes, str)): + raise Exception( + f"Expected backend_type_ to be a str, received: {type(backend_type_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if token_rotate_interval_ is not None and not isinstance( + token_rotate_interval_, int + ): + raise Exception( + f"Expected token_rotate_interval_ to be a int, received: {type(token_rotate_interval_)}" + ) + + self.secretbackend = secretbackend_ + self.backend_type = backend_type_ + self.config = config_ + self.id_ = id__ + self.name = name_ + self.token_rotate_interval = token_rotate_interval_ + self.unknown_fields = unknown_fields + + +class AddSecretBackendArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~AddSecretBackendArg]""" + args_ = [AddSecretBackendArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class AddStorageDetails(Type): + _toSchema = {"storage_tags": "storage-tags"} + _toPy = {"storage-tags": "storage_tags"} + + def __init__(self, storage_tags=None, **unknown_fields): + """storage_tags : typing.Sequence[str]""" + storage_tags_ = storage_tags + + # Validate arguments against known Juju API types. + if storage_tags_ is not None and not isinstance( + storage_tags_, (bytes, str, list) + ): + raise Exception( + f"Expected storage_tags_ to be a Sequence, received: {type(storage_tags_)}" + ) + + self.storage_tags = storage_tags_ + self.unknown_fields = unknown_fields + + +class AddStorageResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : AddStorageDetails + """ + error_ = Error.from_json(error) if error else None + result_ = AddStorageDetails.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, AddStorageDetails)): + raise Exception( + f"Expected result_ to be a AddStorageDetails, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class AddStorageResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~AddStorageResult]""" + results_ = [AddStorageResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class AddUser(Type): + _toSchema = { + "display_name": "display-name", + "password": "password", + "username": "username", + } + _toPy = { + "display-name": "display_name", + "password": "password", + "username": "username", + } + + def __init__( + self, display_name=None, password=None, username=None, **unknown_fields + ): + """display_name : str + password : str + username : str + """ + display_name_ = display_name + password_ = password + username_ = username + + # Validate arguments against known Juju API types. + if display_name_ is not None and not isinstance(display_name_, (bytes, str)): + raise Exception( + f"Expected display_name_ to be a str, received: {type(display_name_)}" + ) + + if password_ is not None and not isinstance(password_, (bytes, str)): + raise Exception( + f"Expected password_ to be a str, received: {type(password_)}" + ) + + if username_ is not None and not isinstance(username_, (bytes, str)): + raise Exception( + f"Expected username_ to be a str, received: {type(username_)}" + ) + + self.display_name = display_name_ + self.password = password_ + self.username = username_ + self.unknown_fields = unknown_fields + + +class AddUserResult(Type): + _toSchema = {"error": "error", "secret_key": "secret-key", "tag": "tag"} + _toPy = {"error": "error", "secret-key": "secret_key", "tag": "tag"} + + def __init__(self, error=None, secret_key=None, tag=None, **unknown_fields): + """Error : Error + secret_key : typing.Sequence[int] + tag : str + """ + error_ = Error.from_json(error) if error else None + secret_key_ = secret_key + tag_ = tag + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if secret_key_ is not None and not isinstance(secret_key_, (bytes, str, list)): + raise Exception( + f"Expected secret_key_ to be a Sequence, received: {type(secret_key_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.error = error_ + self.secret_key = secret_key_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class AddUserResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~AddUserResult]""" + results_ = [AddUserResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class AddUsers(Type): + _toSchema = {"users": "users"} + _toPy = {"users": "users"} + + def __init__(self, users=None, **unknown_fields): + """Users : typing.Sequence[~AddUser]""" + users_ = [AddUser.from_json(o) for o in users or []] + + # Validate arguments against known Juju API types. + if users_ is not None and not isinstance(users_, (bytes, str, list)): + raise Exception( + f"Expected users_ to be a Sequence, received: {type(users_)}" + ) + + self.users = users_ + self.unknown_fields = unknown_fields + + +class Address(Type): + _toSchema = { + "cidr": "cidr", + "config_type": "config-type", + "is_secondary": "is-secondary", + "scope": "scope", + "space_id": "space-id", + "space_name": "space-name", + "type_": "type", + "value": "value", + } + _toPy = { + "cidr": "cidr", + "config-type": "config_type", + "is-secondary": "is_secondary", + "scope": "scope", + "space-id": "space_id", + "space-name": "space_name", + "type": "type_", + "value": "value", + } + + def __init__( + self, + cidr=None, + config_type=None, + is_secondary=None, + scope=None, + space_id=None, + space_name=None, + type_=None, + value=None, + **unknown_fields, + ): + """Cidr : str + config_type : str + is_secondary : bool + scope : str + space_id : str + space_name : str + type_ : str + value : str + """ + cidr_ = cidr + config_type_ = config_type + is_secondary_ = is_secondary + scope_ = scope + space_id_ = space_id + space_name_ = space_name + type__ = type_ + value_ = value + + # Validate arguments against known Juju API types. + if cidr_ is not None and not isinstance(cidr_, (bytes, str)): + raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") + + if config_type_ is not None and not isinstance(config_type_, (bytes, str)): + raise Exception( + f"Expected config_type_ to be a str, received: {type(config_type_)}" + ) + + if is_secondary_ is not None and not isinstance(is_secondary_, bool): + raise Exception( + f"Expected is_secondary_ to be a bool, received: {type(is_secondary_)}" + ) + + if scope_ is not None and not isinstance(scope_, (bytes, str)): + raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") + + if space_id_ is not None and not isinstance(space_id_, (bytes, str)): + raise Exception( + f"Expected space_id_ to be a str, received: {type(space_id_)}" + ) + + if space_name_ is not None and not isinstance(space_name_, (bytes, str)): + raise Exception( + f"Expected space_name_ to be a str, received: {type(space_name_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + if value_ is not None and not isinstance(value_, (bytes, str)): + raise Exception(f"Expected value_ to be a str, received: {type(value_)}") + + self.cidr = cidr_ + self.config_type = config_type_ + self.is_secondary = is_secondary_ + self.scope = scope_ + self.space_id = space_id_ + self.space_name = space_name_ + self.type_ = type__ + self.value = value_ + self.unknown_fields = unknown_fields + + +class AllWatcherId(Type): + _toSchema = {"watcher_id": "watcher-id"} + _toPy = {"watcher-id": "watcher_id"} + + def __init__(self, watcher_id=None, **unknown_fields): + """watcher_id : str""" + watcher_id_ = watcher_id + + # Validate arguments against known Juju API types. + if watcher_id_ is not None and not isinstance(watcher_id_, (bytes, str)): + raise Exception( + f"Expected watcher_id_ to be a str, received: {type(watcher_id_)}" + ) + + self.watcher_id = watcher_id_ + self.unknown_fields = unknown_fields + + +class AllWatcherNextResults(Type): + _toSchema = {"deltas": "deltas"} + _toPy = {"deltas": "deltas"} + + def __init__(self, deltas=None, **unknown_fields): + """Deltas : typing.Sequence[~Delta]""" + deltas_ = [Delta.from_json(o) for o in deltas or []] + + # Validate arguments against known Juju API types. + if deltas_ is not None and not isinstance(deltas_, (bytes, str, list)): + raise Exception( + f"Expected deltas_ to be a Sequence, received: {type(deltas_)}" + ) + + self.deltas = deltas_ + self.unknown_fields = unknown_fields + + +class AnnotationsGetResult(Type): + _toSchema = {"annotations": "annotations", "entity": "entity", "error": "error"} + _toPy = {"annotations": "annotations", "entity": "entity", "error": "error"} + + def __init__(self, annotations=None, entity=None, error=None, **unknown_fields): + """Annotations : typing.Mapping[str, str] + entity : str + error : ErrorResult + """ + annotations_ = annotations + entity_ = entity + error_ = ErrorResult.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if annotations_ is not None and not isinstance(annotations_, dict): + raise Exception( + f"Expected annotations_ to be a Mapping, received: {type(annotations_)}" + ) + + if entity_ is not None and not isinstance(entity_, (bytes, str)): + raise Exception(f"Expected entity_ to be a str, received: {type(entity_)}") + + if error_ is not None and not isinstance(error_, (dict, ErrorResult)): + raise Exception( + f"Expected error_ to be a ErrorResult, received: {type(error_)}" + ) + + self.annotations = annotations_ + self.entity = entity_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class AnnotationsGetResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~AnnotationsGetResult]""" + results_ = [AnnotationsGetResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class AnnotationsSet(Type): + _toSchema = {"annotations": "annotations"} + _toPy = {"annotations": "annotations"} + + def __init__(self, annotations=None, **unknown_fields): + """Annotations : typing.Sequence[~EntityAnnotations]""" + annotations_ = [EntityAnnotations.from_json(o) for o in annotations or []] + + # Validate arguments against known Juju API types. + if annotations_ is not None and not isinstance( + annotations_, (bytes, str, list) + ): + raise Exception( + f"Expected annotations_ to be a Sequence, received: {type(annotations_)}" + ) + + self.annotations = annotations_ + self.unknown_fields = unknown_fields + + +class ApplicationCharmActionsResult(Type): + _toSchema = { + "actions": "actions", + "application_tag": "application-tag", + "error": "error", + } + _toPy = { + "actions": "actions", + "application-tag": "application_tag", + "error": "error", + } + + def __init__( + self, actions=None, application_tag=None, error=None, **unknown_fields + ): + """Actions : typing.Mapping[str, ~ActionSpec] + application_tag : str + error : Error + """ + actions_ = {k: ActionSpec.from_json(v) for k, v in (actions or dict()).items()} + application_tag_ = application_tag + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if actions_ is not None and not isinstance(actions_, dict): + raise Exception( + f"Expected actions_ to be a Mapping, received: {type(actions_)}" + ) + + if application_tag_ is not None and not isinstance( + application_tag_, (bytes, str) + ): + raise Exception( + f"Expected application_tag_ to be a str, received: {type(application_tag_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.actions = actions_ + self.application_tag = application_tag_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class ApplicationCharmPlacement(Type): + _toSchema = {"application": "application", "charm_url": "charm-url"} + _toPy = {"application": "application", "charm-url": "charm_url"} + + def __init__(self, application=None, charm_url=None, **unknown_fields): + """Application : str + charm_url : str + """ + application_ = application + charm_url_ = charm_url + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): + raise Exception( + f"Expected charm_url_ to be a str, received: {type(charm_url_)}" + ) + + self.application = application_ + self.charm_url = charm_url_ + self.unknown_fields = unknown_fields + + +class ApplicationCharmPlacements(Type): + _toSchema = {"placements": "placements"} + _toPy = {"placements": "placements"} + + def __init__(self, placements=None, **unknown_fields): + """Placements : typing.Sequence[~ApplicationCharmPlacement]""" + placements_ = [ApplicationCharmPlacement.from_json(o) for o in placements or []] + + # Validate arguments against known Juju API types. + if placements_ is not None and not isinstance(placements_, (bytes, str, list)): + raise Exception( + f"Expected placements_ to be a Sequence, received: {type(placements_)}" + ) + + self.placements = placements_ + self.unknown_fields = unknown_fields + + +class ApplicationCharmRelations(Type): + _toSchema = {"application": "application"} + _toPy = {"application": "application"} + + def __init__(self, application=None, **unknown_fields): + """Application : str""" + application_ = application + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + self.application = application_ + self.unknown_fields = unknown_fields + + +class ApplicationCharmRelationsResults(Type): + _toSchema = {"charm_relations": "charm-relations"} + _toPy = {"charm-relations": "charm_relations"} + + def __init__(self, charm_relations=None, **unknown_fields): + """charm_relations : typing.Sequence[str]""" + charm_relations_ = charm_relations + + # Validate arguments against known Juju API types. + if charm_relations_ is not None and not isinstance( + charm_relations_, (bytes, str, list) + ): + raise Exception( + f"Expected charm_relations_ to be a Sequence, received: {type(charm_relations_)}" + ) + + self.charm_relations = charm_relations_ + self.unknown_fields = unknown_fields + + +class ApplicationConfigUnsetArgs(Type): + _toSchema = {"args": "Args"} + _toPy = {"Args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~ApplicationUnset]""" + args_ = [ApplicationUnset.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class ApplicationConstraint(Type): + _toSchema = {"constraints": "constraints", "error": "error"} + _toPy = {"constraints": "constraints", "error": "error"} + + def __init__(self, constraints=None, error=None, **unknown_fields): + """Constraints : Value + error : Error + """ + constraints_ = Value.from_json(constraints) if constraints else None + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if constraints_ is not None and not isinstance(constraints_, (dict, Value)): + raise Exception( + f"Expected constraints_ to be a Value, received: {type(constraints_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.constraints = constraints_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class ApplicationDeploy(Type): + _toSchema = { + "application": "application", + "attach_storage": "attach-storage", + "channel": "channel", + "charm_origin": "charm-origin", + "charm_url": "charm-url", + "config": "config", + "config_yaml": "config-yaml", + "constraints": "constraints", + "devices": "devices", + "endpoint_bindings": "endpoint-bindings", + "force": "Force", + "num_units": "num-units", + "placement": "placement", + "policy": "policy", + "resources": "resources", + "storage": "storage", + } + _toPy = { + "Force": "force", + "application": "application", + "attach-storage": "attach_storage", + "channel": "channel", + "charm-origin": "charm_origin", + "charm-url": "charm_url", + "config": "config", + "config-yaml": "config_yaml", + "constraints": "constraints", + "devices": "devices", + "endpoint-bindings": "endpoint_bindings", + "num-units": "num_units", + "placement": "placement", + "policy": "policy", + "resources": "resources", + "storage": "storage", + } + + def __init__( + self, + force=None, + application=None, + attach_storage=None, + channel=None, + charm_origin=None, + charm_url=None, + config=None, + config_yaml=None, + constraints=None, + devices=None, + endpoint_bindings=None, + num_units=None, + placement=None, + policy=None, + resources=None, + storage=None, + **unknown_fields, + ): + """Force : bool + application : str + attach_storage : typing.Sequence[str] + channel : str + charm_origin : CharmOrigin + charm_url : str + config : typing.Mapping[str, str] + config_yaml : str + constraints : Value + devices : typing.Mapping[str, ~Constraints] + endpoint_bindings : typing.Mapping[str, str] + num_units : int + placement : typing.Sequence[~Placement] + policy : str + resources : typing.Mapping[str, str] + storage : typing.Mapping[str, ~Constraints] + """ + force_ = force + application_ = application + attach_storage_ = attach_storage + channel_ = channel + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + charm_url_ = charm_url + config_ = config + config_yaml_ = config_yaml + constraints_ = Value.from_json(constraints) if constraints else None + devices_ = {k: Constraints.from_json(v) for k, v in (devices or dict()).items()} + endpoint_bindings_ = endpoint_bindings + num_units_ = num_units + placement_ = [Placement.from_json(o) for o in placement or []] + policy_ = policy + resources_ = resources + storage_ = {k: Constraints.from_json(v) for k, v in (storage or dict()).items()} + + # Validate arguments against known Juju API types. + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if attach_storage_ is not None and not isinstance( + attach_storage_, (bytes, str, list) + ): + raise Exception( + f"Expected attach_storage_ to be a Sequence, received: {type(attach_storage_)}" + ) + + if channel_ is not None and not isinstance(channel_, (bytes, str)): + raise Exception( + f"Expected channel_ to be a str, received: {type(channel_)}" + ) + + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): + raise Exception( + f"Expected charm_url_ to be a str, received: {type(charm_url_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if config_yaml_ is not None and not isinstance(config_yaml_, (bytes, str)): + raise Exception( + f"Expected config_yaml_ to be a str, received: {type(config_yaml_)}" + ) + + if constraints_ is not None and not isinstance(constraints_, (dict, Value)): + raise Exception( + f"Expected constraints_ to be a Value, received: {type(constraints_)}" + ) + + if devices_ is not None and not isinstance(devices_, dict): + raise Exception( + f"Expected devices_ to be a Mapping, received: {type(devices_)}" + ) + + if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): + raise Exception( + f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" + ) + + if num_units_ is not None and not isinstance(num_units_, int): + raise Exception( + f"Expected num_units_ to be a int, received: {type(num_units_)}" + ) + + if placement_ is not None and not isinstance(placement_, (bytes, str, list)): + raise Exception( + f"Expected placement_ to be a Sequence, received: {type(placement_)}" + ) + + if policy_ is not None and not isinstance(policy_, (bytes, str)): + raise Exception(f"Expected policy_ to be a str, received: {type(policy_)}") + + if resources_ is not None and not isinstance(resources_, dict): + raise Exception( + f"Expected resources_ to be a Mapping, received: {type(resources_)}" + ) + + if storage_ is not None and not isinstance(storage_, dict): + raise Exception( + f"Expected storage_ to be a Mapping, received: {type(storage_)}" + ) + + self.force = force_ + self.application = application_ + self.attach_storage = attach_storage_ + self.channel = channel_ + self.charm_origin = charm_origin_ + self.charm_url = charm_url_ + self.config = config_ + self.config_yaml = config_yaml_ + self.constraints = constraints_ + self.devices = devices_ + self.endpoint_bindings = endpoint_bindings_ + self.num_units = num_units_ + self.placement = placement_ + self.policy = policy_ + self.resources = resources_ + self.storage = storage_ + self.unknown_fields = unknown_fields + + +class ApplicationExpose(Type): + _toSchema = {"application": "application", "exposed_endpoints": "exposed-endpoints"} + _toPy = {"application": "application", "exposed-endpoints": "exposed_endpoints"} + + def __init__(self, application=None, exposed_endpoints=None, **unknown_fields): + """Application : str + exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] + """ + application_ = application + exposed_endpoints_ = { + k: ExposedEndpoint.from_json(v) + for k, v in (exposed_endpoints or dict()).items() + } + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if exposed_endpoints_ is not None and not isinstance(exposed_endpoints_, dict): + raise Exception( + f"Expected exposed_endpoints_ to be a Mapping, received: {type(exposed_endpoints_)}" + ) + + self.application = application_ + self.exposed_endpoints = exposed_endpoints_ + self.unknown_fields = unknown_fields + + +class ApplicationGet(Type): + _toSchema = {"application": "application", "branch": "branch"} + _toPy = {"application": "application", "branch": "branch"} + + def __init__(self, application=None, branch=None, **unknown_fields): + """Application : str + branch : str + """ + application_ = application + branch_ = branch + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if branch_ is not None and not isinstance(branch_, (bytes, str)): + raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") + + self.application = application_ + self.branch = branch_ + self.unknown_fields = unknown_fields + + +class ApplicationGetArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~ApplicationGet]""" + args_ = [ApplicationGet.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class ApplicationGetConfigResults(Type): + _toSchema = {"results": "Results"} + _toPy = {"Results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ConfigResult]""" + results_ = [ConfigResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ApplicationGetConstraintsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ApplicationConstraint]""" + results_ = [ApplicationConstraint.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ApplicationGetResults(Type): + _toSchema = { + "application": "application", + "application_config": "application-config", + "base": "base", + "channel": "channel", + "charm": "charm", + "config": "config", + "constraints": "constraints", + "endpoint_bindings": "endpoint-bindings", + } + _toPy = { + "application": "application", + "application-config": "application_config", + "base": "base", + "channel": "channel", + "charm": "charm", + "config": "config", + "constraints": "constraints", + "endpoint-bindings": "endpoint_bindings", + } + + def __init__( + self, + application=None, + application_config=None, + base=None, + channel=None, + charm=None, + config=None, + constraints=None, + endpoint_bindings=None, + **unknown_fields, + ): + """Application : str + application_config : typing.Mapping[str, typing.Any] + base : Base + channel : str + charm : str + config : typing.Mapping[str, typing.Any] + constraints : Value + endpoint_bindings : typing.Mapping[str, str] + """ + application_ = application + application_config_ = application_config + base_ = Base.from_json(base) if base else None + channel_ = channel + charm_ = charm + config_ = config + constraints_ = Value.from_json(constraints) if constraints else None + endpoint_bindings_ = endpoint_bindings + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if application_config_ is not None and not isinstance( + application_config_, dict + ): + raise Exception( + f"Expected application_config_ to be a Mapping, received: {type(application_config_)}" + ) + + if base_ is not None and not isinstance(base_, (dict, Base)): + raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") + + if channel_ is not None and not isinstance(channel_, (bytes, str)): + raise Exception( + f"Expected channel_ to be a str, received: {type(channel_)}" + ) + + if charm_ is not None and not isinstance(charm_, (bytes, str)): + raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if constraints_ is not None and not isinstance(constraints_, (dict, Value)): + raise Exception( + f"Expected constraints_ to be a Value, received: {type(constraints_)}" + ) + + if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): + raise Exception( + f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" + ) + + self.application = application_ + self.application_config = application_config_ + self.base = base_ + self.channel = channel_ + self.charm = charm_ + self.config = config_ + self.constraints = constraints_ + self.endpoint_bindings = endpoint_bindings_ + self.unknown_fields = unknown_fields + + +class ApplicationInfoResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : ApplicationResult + """ + error_ = Error.from_json(error) if error else None + result_ = ApplicationResult.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, ApplicationResult)): + raise Exception( + f"Expected result_ to be a ApplicationResult, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class ApplicationInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ApplicationInfoResult]""" + results_ = [ApplicationInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ApplicationMergeBindings(Type): + _toSchema = { + "application_tag": "application-tag", + "bindings": "bindings", + "force": "force", + } + _toPy = { + "application-tag": "application_tag", + "bindings": "bindings", + "force": "force", + } + + def __init__( + self, application_tag=None, bindings=None, force=None, **unknown_fields + ): + """application_tag : str + bindings : typing.Mapping[str, str] + force : bool + """ + application_tag_ = application_tag + bindings_ = bindings + force_ = force + + # Validate arguments against known Juju API types. + if application_tag_ is not None and not isinstance( + application_tag_, (bytes, str) + ): + raise Exception( + f"Expected application_tag_ to be a str, received: {type(application_tag_)}" + ) + + if bindings_ is not None and not isinstance(bindings_, dict): + raise Exception( + f"Expected bindings_ to be a Mapping, received: {type(bindings_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + self.application_tag = application_tag_ + self.bindings = bindings_ + self.force = force_ + self.unknown_fields = unknown_fields + + +class ApplicationMergeBindingsArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~ApplicationMergeBindings]""" + args_ = [ApplicationMergeBindings.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class ApplicationMetricCredential(Type): + _toSchema = { + "application": "application", + "metrics_credentials": "metrics-credentials", + } + _toPy = {"application": "application", "metrics-credentials": "metrics_credentials"} + + def __init__(self, application=None, metrics_credentials=None, **unknown_fields): + """Application : str + metrics_credentials : typing.Sequence[int] + """ + application_ = application + metrics_credentials_ = metrics_credentials + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if metrics_credentials_ is not None and not isinstance( + metrics_credentials_, (bytes, str, list) + ): + raise Exception( + f"Expected metrics_credentials_ to be a Sequence, received: {type(metrics_credentials_)}" + ) + + self.application = application_ + self.metrics_credentials = metrics_credentials_ + self.unknown_fields = unknown_fields + + +class ApplicationMetricCredentials(Type): + _toSchema = {"creds": "creds"} + _toPy = {"creds": "creds"} + + def __init__(self, creds=None, **unknown_fields): + """Creds : typing.Sequence[~ApplicationMetricCredential]""" + creds_ = [ApplicationMetricCredential.from_json(o) for o in creds or []] + + # Validate arguments against known Juju API types. + if creds_ is not None and not isinstance(creds_, (bytes, str, list)): + raise Exception( + f"Expected creds_ to be a Sequence, received: {type(creds_)}" + ) + + self.creds = creds_ + self.unknown_fields = unknown_fields + + +class ApplicationOfferAdminDetails(Type): + _toSchema = { + "application_description": "application-description", + "application_name": "application-name", + "applicationofferdetails": "ApplicationOfferDetails", + "bindings": "bindings", + "charm_url": "charm-url", + "connections": "connections", + "endpoints": "endpoints", + "offer_name": "offer-name", + "offer_url": "offer-url", + "offer_uuid": "offer-uuid", + "source_model_tag": "source-model-tag", + "spaces": "spaces", + "users": "users", + } + _toPy = { + "ApplicationOfferDetails": "applicationofferdetails", + "application-description": "application_description", + "application-name": "application_name", + "bindings": "bindings", + "charm-url": "charm_url", + "connections": "connections", + "endpoints": "endpoints", + "offer-name": "offer_name", + "offer-url": "offer_url", + "offer-uuid": "offer_uuid", + "source-model-tag": "source_model_tag", + "spaces": "spaces", + "users": "users", + } + + def __init__( + self, + applicationofferdetails=None, + application_description=None, + application_name=None, + bindings=None, + charm_url=None, + connections=None, + endpoints=None, + offer_name=None, + offer_url=None, + offer_uuid=None, + source_model_tag=None, + spaces=None, + users=None, + **unknown_fields, + ): + """Applicationofferdetails : ApplicationOfferDetails + application_description : str + application_name : str + bindings : typing.Mapping[str, str] + charm_url : str + connections : typing.Sequence[~OfferConnection] + endpoints : typing.Sequence[~RemoteEndpoint] + offer_name : str + offer_url : str + offer_uuid : str + source_model_tag : str + spaces : typing.Sequence[~RemoteSpace] + users : typing.Sequence[~OfferUserDetails] + """ + applicationofferdetails_ = ( + ApplicationOfferDetails.from_json(applicationofferdetails) + if applicationofferdetails + else None + ) + application_description_ = application_description + application_name_ = application_name + bindings_ = bindings + charm_url_ = charm_url + connections_ = [OfferConnection.from_json(o) for o in connections or []] + endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] + offer_name_ = offer_name + offer_url_ = offer_url + offer_uuid_ = offer_uuid + source_model_tag_ = source_model_tag + spaces_ = [RemoteSpace.from_json(o) for o in spaces or []] + users_ = [OfferUserDetails.from_json(o) for o in users or []] + + # Validate arguments against known Juju API types. + if applicationofferdetails_ is not None and not isinstance( + applicationofferdetails_, (dict, ApplicationOfferDetails) + ): + raise Exception( + f"Expected applicationofferdetails_ to be a ApplicationOfferDetails, received: {type(applicationofferdetails_)}" + ) + + if application_description_ is not None and not isinstance( + application_description_, (bytes, str) + ): + raise Exception( + f"Expected application_description_ to be a str, received: {type(application_description_)}" + ) + + if application_name_ is not None and not isinstance( + application_name_, (bytes, str) + ): + raise Exception( + f"Expected application_name_ to be a str, received: {type(application_name_)}" + ) + + if bindings_ is not None and not isinstance(bindings_, dict): + raise Exception( + f"Expected bindings_ to be a Mapping, received: {type(bindings_)}" + ) + + if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): + raise Exception( + f"Expected charm_url_ to be a str, received: {type(charm_url_)}" + ) + + if connections_ is not None and not isinstance( + connections_, (bytes, str, list) + ): + raise Exception( + f"Expected connections_ to be a Sequence, received: {type(connections_)}" + ) + + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): + raise Exception( + f"Expected offer_url_ to be a str, received: {type(offer_url_)}" + ) + + if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): + raise Exception( + f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" + ) + + if source_model_tag_ is not None and not isinstance( + source_model_tag_, (bytes, str) + ): + raise Exception( + f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" + ) + + if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): + raise Exception( + f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" + ) + + if users_ is not None and not isinstance(users_, (bytes, str, list)): + raise Exception( + f"Expected users_ to be a Sequence, received: {type(users_)}" + ) + + self.applicationofferdetails = applicationofferdetails_ + self.application_description = application_description_ + self.application_name = application_name_ + self.bindings = bindings_ + self.charm_url = charm_url_ + self.connections = connections_ + self.endpoints = endpoints_ + self.offer_name = offer_name_ + self.offer_url = offer_url_ + self.offer_uuid = offer_uuid_ + self.source_model_tag = source_model_tag_ + self.spaces = spaces_ + self.users = users_ + self.unknown_fields = unknown_fields + + +class ApplicationOfferAdminDetailsV5(Type): + _toSchema = { + "application_description": "application-description", + "application_name": "application-name", + "applicationofferdetailsv5": "ApplicationOfferDetailsV5", + "charm_url": "charm-url", + "connections": "connections", + "endpoints": "endpoints", + "offer_name": "offer-name", + "offer_url": "offer-url", + "offer_uuid": "offer-uuid", + "source_model_tag": "source-model-tag", + "users": "users", + } + _toPy = { + "ApplicationOfferDetailsV5": "applicationofferdetailsv5", + "application-description": "application_description", + "application-name": "application_name", + "charm-url": "charm_url", + "connections": "connections", + "endpoints": "endpoints", + "offer-name": "offer_name", + "offer-url": "offer_url", + "offer-uuid": "offer_uuid", + "source-model-tag": "source_model_tag", + "users": "users", + } + + def __init__( + self, + applicationofferdetailsv5=None, + application_description=None, + application_name=None, + charm_url=None, + connections=None, + endpoints=None, + offer_name=None, + offer_url=None, + offer_uuid=None, + source_model_tag=None, + users=None, + **unknown_fields, + ): + """applicationofferdetailsv5 : ApplicationOfferDetailsV5 + application_description : str + application_name : str + charm_url : str + connections : typing.Sequence[~OfferConnection] + endpoints : typing.Sequence[~RemoteEndpoint] + offer_name : str + offer_url : str + offer_uuid : str + source_model_tag : str + users : typing.Sequence[~OfferUserDetails] + """ + applicationofferdetailsv5_ = ( + ApplicationOfferDetailsV5.from_json(applicationofferdetailsv5) + if applicationofferdetailsv5 + else None + ) + application_description_ = application_description + application_name_ = application_name + charm_url_ = charm_url + connections_ = [OfferConnection.from_json(o) for o in connections or []] + endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] + offer_name_ = offer_name + offer_url_ = offer_url + offer_uuid_ = offer_uuid + source_model_tag_ = source_model_tag + users_ = [OfferUserDetails.from_json(o) for o in users or []] + + # Validate arguments against known Juju API types. + if applicationofferdetailsv5_ is not None and not isinstance( + applicationofferdetailsv5_, (dict, ApplicationOfferDetailsV5) + ): + raise Exception( + f"Expected applicationofferdetailsv5_ to be a ApplicationOfferDetailsV5, received: {type(applicationofferdetailsv5_)}" + ) + + if application_description_ is not None and not isinstance( + application_description_, (bytes, str) + ): + raise Exception( + f"Expected application_description_ to be a str, received: {type(application_description_)}" + ) + + if application_name_ is not None and not isinstance( + application_name_, (bytes, str) + ): + raise Exception( + f"Expected application_name_ to be a str, received: {type(application_name_)}" + ) + + if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): + raise Exception( + f"Expected charm_url_ to be a str, received: {type(charm_url_)}" + ) + + if connections_ is not None and not isinstance( + connections_, (bytes, str, list) + ): + raise Exception( + f"Expected connections_ to be a Sequence, received: {type(connections_)}" + ) + + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): + raise Exception( + f"Expected offer_url_ to be a str, received: {type(offer_url_)}" + ) + + if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): + raise Exception( + f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" + ) + + if source_model_tag_ is not None and not isinstance( + source_model_tag_, (bytes, str) + ): + raise Exception( + f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" + ) + + if users_ is not None and not isinstance(users_, (bytes, str, list)): + raise Exception( + f"Expected users_ to be a Sequence, received: {type(users_)}" + ) + + self.applicationofferdetailsv5 = applicationofferdetailsv5_ + self.application_description = application_description_ + self.application_name = application_name_ + self.charm_url = charm_url_ + self.connections = connections_ + self.endpoints = endpoints_ + self.offer_name = offer_name_ + self.offer_url = offer_url_ + self.offer_uuid = offer_uuid_ + self.source_model_tag = source_model_tag_ + self.users = users_ + self.unknown_fields = unknown_fields + + +class ApplicationOfferDetails(Type): + _toSchema = { + "application_description": "application-description", + "bindings": "bindings", + "endpoints": "endpoints", + "offer_name": "offer-name", + "offer_url": "offer-url", + "offer_uuid": "offer-uuid", + "source_model_tag": "source-model-tag", + "spaces": "spaces", + "users": "users", + } + _toPy = { + "application-description": "application_description", + "bindings": "bindings", + "endpoints": "endpoints", + "offer-name": "offer_name", + "offer-url": "offer_url", + "offer-uuid": "offer_uuid", + "source-model-tag": "source_model_tag", + "spaces": "spaces", + "users": "users", + } + + def __init__( + self, + application_description=None, + bindings=None, + endpoints=None, + offer_name=None, + offer_url=None, + offer_uuid=None, + source_model_tag=None, + spaces=None, + users=None, + **unknown_fields, + ): + """application_description : str + bindings : typing.Mapping[str, str] + endpoints : typing.Sequence[~RemoteEndpoint] + offer_name : str + offer_url : str + offer_uuid : str + source_model_tag : str + spaces : typing.Sequence[~RemoteSpace] + users : typing.Sequence[~OfferUserDetails] + """ + application_description_ = application_description + bindings_ = bindings + endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] + offer_name_ = offer_name + offer_url_ = offer_url + offer_uuid_ = offer_uuid + source_model_tag_ = source_model_tag + spaces_ = [RemoteSpace.from_json(o) for o in spaces or []] + users_ = [OfferUserDetails.from_json(o) for o in users or []] + + # Validate arguments against known Juju API types. + if application_description_ is not None and not isinstance( + application_description_, (bytes, str) + ): + raise Exception( + f"Expected application_description_ to be a str, received: {type(application_description_)}" + ) + + if bindings_ is not None and not isinstance(bindings_, dict): + raise Exception( + f"Expected bindings_ to be a Mapping, received: {type(bindings_)}" + ) + + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): + raise Exception( + f"Expected offer_url_ to be a str, received: {type(offer_url_)}" + ) + + if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): + raise Exception( + f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" + ) + + if source_model_tag_ is not None and not isinstance( + source_model_tag_, (bytes, str) + ): + raise Exception( + f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" + ) + + if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): + raise Exception( + f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" + ) + + if users_ is not None and not isinstance(users_, (bytes, str, list)): + raise Exception( + f"Expected users_ to be a Sequence, received: {type(users_)}" + ) + + self.application_description = application_description_ + self.bindings = bindings_ + self.endpoints = endpoints_ + self.offer_name = offer_name_ + self.offer_url = offer_url_ + self.offer_uuid = offer_uuid_ + self.source_model_tag = source_model_tag_ + self.spaces = spaces_ + self.users = users_ + self.unknown_fields = unknown_fields + + +class ApplicationOfferDetailsV5(Type): + _toSchema = { + "application_description": "application-description", + "endpoints": "endpoints", + "offer_name": "offer-name", + "offer_url": "offer-url", + "offer_uuid": "offer-uuid", + "source_model_tag": "source-model-tag", + "users": "users", + } + _toPy = { + "application-description": "application_description", + "endpoints": "endpoints", + "offer-name": "offer_name", + "offer-url": "offer_url", + "offer-uuid": "offer_uuid", + "source-model-tag": "source_model_tag", + "users": "users", + } + + def __init__( + self, + application_description=None, + endpoints=None, + offer_name=None, + offer_url=None, + offer_uuid=None, + source_model_tag=None, + users=None, + **unknown_fields, + ): + """application_description : str + endpoints : typing.Sequence[~RemoteEndpoint] + offer_name : str + offer_url : str + offer_uuid : str + source_model_tag : str + users : typing.Sequence[~OfferUserDetails] + """ + application_description_ = application_description + endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] + offer_name_ = offer_name + offer_url_ = offer_url + offer_uuid_ = offer_uuid + source_model_tag_ = source_model_tag + users_ = [OfferUserDetails.from_json(o) for o in users or []] + + # Validate arguments against known Juju API types. + if application_description_ is not None and not isinstance( + application_description_, (bytes, str) + ): + raise Exception( + f"Expected application_description_ to be a str, received: {type(application_description_)}" + ) + + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): + raise Exception( + f"Expected offer_url_ to be a str, received: {type(offer_url_)}" + ) + + if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): + raise Exception( + f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" + ) + + if source_model_tag_ is not None and not isinstance( + source_model_tag_, (bytes, str) + ): + raise Exception( + f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" + ) + + if users_ is not None and not isinstance(users_, (bytes, str, list)): + raise Exception( + f"Expected users_ to be a Sequence, received: {type(users_)}" + ) + + self.application_description = application_description_ + self.endpoints = endpoints_ + self.offer_name = offer_name_ + self.offer_url = offer_url_ + self.offer_uuid = offer_uuid_ + self.source_model_tag = source_model_tag_ + self.users = users_ + self.unknown_fields = unknown_fields + + +class ApplicationOfferResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : ApplicationOfferAdminDetailsV5 + """ + error_ = Error.from_json(error) if error else None + result_ = ApplicationOfferAdminDetailsV5.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance( + result_, (dict, ApplicationOfferAdminDetailsV5) + ): + raise Exception( + f"Expected result_ to be a ApplicationOfferAdminDetailsV5, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class ApplicationOfferStatus(Type): + _toSchema = { + "active_connected_count": "active-connected-count", + "application_name": "application-name", + "charm": "charm", + "endpoints": "endpoints", + "err": "err", + "offer_name": "offer-name", + "total_connected_count": "total-connected-count", + } + _toPy = { + "active-connected-count": "active_connected_count", + "application-name": "application_name", + "charm": "charm", + "endpoints": "endpoints", + "err": "err", + "offer-name": "offer_name", + "total-connected-count": "total_connected_count", + } + + def __init__( + self, + active_connected_count=None, + application_name=None, + charm=None, + endpoints=None, + err=None, + offer_name=None, + total_connected_count=None, + **unknown_fields, + ): + """active_connected_count : int + application_name : str + charm : str + endpoints : typing.Mapping[str, ~RemoteEndpoint] + err : Error + offer_name : str + total_connected_count : int + """ + active_connected_count_ = active_connected_count + application_name_ = application_name + charm_ = charm + endpoints_ = { + k: RemoteEndpoint.from_json(v) for k, v in (endpoints or dict()).items() + } + err_ = Error.from_json(err) if err else None + offer_name_ = offer_name + total_connected_count_ = total_connected_count + + # Validate arguments against known Juju API types. + if active_connected_count_ is not None and not isinstance( + active_connected_count_, int + ): + raise Exception( + f"Expected active_connected_count_ to be a int, received: {type(active_connected_count_)}" + ) + + if application_name_ is not None and not isinstance( + application_name_, (bytes, str) + ): + raise Exception( + f"Expected application_name_ to be a str, received: {type(application_name_)}" + ) + + if charm_ is not None and not isinstance(charm_, (bytes, str)): + raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") + + if endpoints_ is not None and not isinstance(endpoints_, dict): + raise Exception( + f"Expected endpoints_ to be a Mapping, received: {type(endpoints_)}" + ) + + if err_ is not None and not isinstance(err_, (dict, Error)): + raise Exception(f"Expected err_ to be a Error, received: {type(err_)}") + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if total_connected_count_ is not None and not isinstance( + total_connected_count_, int + ): + raise Exception( + f"Expected total_connected_count_ to be a int, received: {type(total_connected_count_)}" + ) + + self.active_connected_count = active_connected_count_ + self.application_name = application_name_ + self.charm = charm_ + self.endpoints = endpoints_ + self.err = err_ + self.offer_name = offer_name_ + self.total_connected_count = total_connected_count_ + self.unknown_fields = unknown_fields + + +class ApplicationOffersResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ApplicationOfferResult]""" + results_ = [ApplicationOfferResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ApplicationResult(Type): + _toSchema = { + "base": "base", + "channel": "channel", + "charm": "charm", + "constraints": "constraints", + "endpoint_bindings": "endpoint-bindings", + "exposed": "exposed", + "exposed_endpoints": "exposed-endpoints", + "life": "life", + "principal": "principal", + "remote": "remote", + "tag": "tag", + } + _toPy = { + "base": "base", + "channel": "channel", + "charm": "charm", + "constraints": "constraints", + "endpoint-bindings": "endpoint_bindings", + "exposed": "exposed", + "exposed-endpoints": "exposed_endpoints", + "life": "life", + "principal": "principal", + "remote": "remote", + "tag": "tag", + } + + def __init__( + self, + base=None, + channel=None, + charm=None, + constraints=None, + endpoint_bindings=None, + exposed=None, + exposed_endpoints=None, + life=None, + principal=None, + remote=None, + tag=None, + **unknown_fields, + ): + """Base : Base + channel : str + charm : str + constraints : Value + endpoint_bindings : typing.Mapping[str, str] + exposed : bool + exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] + life : str + principal : bool + remote : bool + tag : str + """ + base_ = Base.from_json(base) if base else None + channel_ = channel + charm_ = charm + constraints_ = Value.from_json(constraints) if constraints else None + endpoint_bindings_ = endpoint_bindings + exposed_ = exposed + exposed_endpoints_ = { + k: ExposedEndpoint.from_json(v) + for k, v in (exposed_endpoints or dict()).items() + } + life_ = life + principal_ = principal + remote_ = remote + tag_ = tag + + # Validate arguments against known Juju API types. + if base_ is not None and not isinstance(base_, (dict, Base)): + raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") + + if channel_ is not None and not isinstance(channel_, (bytes, str)): + raise Exception( + f"Expected channel_ to be a str, received: {type(channel_)}" + ) + + if charm_ is not None and not isinstance(charm_, (bytes, str)): + raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") + + if constraints_ is not None and not isinstance(constraints_, (dict, Value)): + raise Exception( + f"Expected constraints_ to be a Value, received: {type(constraints_)}" + ) + + if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): + raise Exception( + f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" + ) + + if exposed_ is not None and not isinstance(exposed_, bool): + raise Exception( + f"Expected exposed_ to be a bool, received: {type(exposed_)}" + ) + + if exposed_endpoints_ is not None and not isinstance(exposed_endpoints_, dict): + raise Exception( + f"Expected exposed_endpoints_ to be a Mapping, received: {type(exposed_endpoints_)}" + ) + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if principal_ is not None and not isinstance(principal_, bool): + raise Exception( + f"Expected principal_ to be a bool, received: {type(principal_)}" + ) + + if remote_ is not None and not isinstance(remote_, bool): + raise Exception(f"Expected remote_ to be a bool, received: {type(remote_)}") + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.base = base_ + self.channel = channel_ + self.charm = charm_ + self.constraints = constraints_ + self.endpoint_bindings = endpoint_bindings_ + self.exposed = exposed_ + self.exposed_endpoints = exposed_endpoints_ + self.life = life_ + self.principal = principal_ + self.remote = remote_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class ApplicationSetCharm(Type): + _toSchema = { + "application": "application", + "channel": "channel", + "charm_origin": "charm-origin", + "charm_url": "charm-url", + "config_settings": "config-settings", + "config_settings_yaml": "config-settings-yaml", + "endpoint_bindings": "endpoint-bindings", + "force": "force", + "force_base": "force-base", + "force_units": "force-units", + "generation": "generation", + "resource_ids": "resource-ids", + "storage_constraints": "storage-constraints", + } + _toPy = { + "application": "application", + "channel": "channel", + "charm-origin": "charm_origin", + "charm-url": "charm_url", + "config-settings": "config_settings", + "config-settings-yaml": "config_settings_yaml", + "endpoint-bindings": "endpoint_bindings", + "force": "force", + "force-base": "force_base", + "force-units": "force_units", + "generation": "generation", + "resource-ids": "resource_ids", + "storage-constraints": "storage_constraints", + } + + def __init__( + self, + application=None, + channel=None, + charm_origin=None, + charm_url=None, + config_settings=None, + config_settings_yaml=None, + endpoint_bindings=None, + force=None, + force_base=None, + force_units=None, + generation=None, + resource_ids=None, + storage_constraints=None, + **unknown_fields, + ): + """Application : str + channel : str + charm_origin : CharmOrigin + charm_url : str + config_settings : typing.Mapping[str, str] + config_settings_yaml : str + endpoint_bindings : typing.Mapping[str, str] + force : bool + force_base : bool + force_units : bool + generation : str + resource_ids : typing.Mapping[str, str] + storage_constraints : typing.Mapping[str, ~StorageConstraints] + """ + application_ = application + channel_ = channel + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + charm_url_ = charm_url + config_settings_ = config_settings + config_settings_yaml_ = config_settings_yaml + endpoint_bindings_ = endpoint_bindings + force_ = force + force_base_ = force_base + force_units_ = force_units + generation_ = generation + resource_ids_ = resource_ids + storage_constraints_ = { + k: StorageConstraints.from_json(v) + for k, v in (storage_constraints or dict()).items() + } + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if channel_ is not None and not isinstance(channel_, (bytes, str)): + raise Exception( + f"Expected channel_ to be a str, received: {type(channel_)}" + ) + + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): + raise Exception( + f"Expected charm_url_ to be a str, received: {type(charm_url_)}" + ) + + if config_settings_ is not None and not isinstance(config_settings_, dict): + raise Exception( + f"Expected config_settings_ to be a Mapping, received: {type(config_settings_)}" + ) + + if config_settings_yaml_ is not None and not isinstance( + config_settings_yaml_, (bytes, str) + ): + raise Exception( + f"Expected config_settings_yaml_ to be a str, received: {type(config_settings_yaml_)}" + ) + + if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): + raise Exception( + f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if force_base_ is not None and not isinstance(force_base_, bool): + raise Exception( + f"Expected force_base_ to be a bool, received: {type(force_base_)}" + ) + + if force_units_ is not None and not isinstance(force_units_, bool): + raise Exception( + f"Expected force_units_ to be a bool, received: {type(force_units_)}" + ) + + if generation_ is not None and not isinstance(generation_, (bytes, str)): + raise Exception( + f"Expected generation_ to be a str, received: {type(generation_)}" + ) + + if resource_ids_ is not None and not isinstance(resource_ids_, dict): + raise Exception( + f"Expected resource_ids_ to be a Mapping, received: {type(resource_ids_)}" + ) + + if storage_constraints_ is not None and not isinstance( + storage_constraints_, dict + ): + raise Exception( + f"Expected storage_constraints_ to be a Mapping, received: {type(storage_constraints_)}" + ) + + self.application = application_ + self.channel = channel_ + self.charm_origin = charm_origin_ + self.charm_url = charm_url_ + self.config_settings = config_settings_ + self.config_settings_yaml = config_settings_yaml_ + self.endpoint_bindings = endpoint_bindings_ + self.force = force_ + self.force_base = force_base_ + self.force_units = force_units_ + self.generation = generation_ + self.resource_ids = resource_ids_ + self.storage_constraints = storage_constraints_ + self.unknown_fields = unknown_fields + + +class ApplicationStatus(Type): + _toSchema = { + "base": "base", + "can_upgrade_to": "can-upgrade-to", + "charm": "charm", + "charm_channel": "charm-channel", + "charm_profile": "charm-profile", + "charm_rev": "charm-rev", + "charm_version": "charm-version", + "endpoint_bindings": "endpoint-bindings", + "err": "err", + "exposed": "exposed", + "exposed_endpoints": "exposed-endpoints", + "int_": "int", + "life": "life", + "meter_statuses": "meter-statuses", + "provider_id": "provider-id", + "public_address": "public-address", + "relations": "relations", + "status": "status", + "subordinate_to": "subordinate-to", + "units": "units", + "workload_version": "workload-version", + } + _toPy = { + "base": "base", + "can-upgrade-to": "can_upgrade_to", + "charm": "charm", + "charm-channel": "charm_channel", + "charm-profile": "charm_profile", + "charm-rev": "charm_rev", + "charm-version": "charm_version", + "endpoint-bindings": "endpoint_bindings", + "err": "err", + "exposed": "exposed", + "exposed-endpoints": "exposed_endpoints", + "int": "int_", + "life": "life", + "meter-statuses": "meter_statuses", + "provider-id": "provider_id", + "public-address": "public_address", + "relations": "relations", + "status": "status", + "subordinate-to": "subordinate_to", + "units": "units", + "workload-version": "workload_version", + } + + def __init__( + self, + base=None, + can_upgrade_to=None, + charm=None, + charm_channel=None, + charm_profile=None, + charm_rev=None, + charm_version=None, + endpoint_bindings=None, + err=None, + exposed=None, + exposed_endpoints=None, + int_=None, + life=None, + meter_statuses=None, + provider_id=None, + public_address=None, + relations=None, + status=None, + subordinate_to=None, + units=None, + workload_version=None, + **unknown_fields, + ): + """Base : Base + can_upgrade_to : str + charm : str + charm_channel : str + charm_profile : str + charm_rev : int + charm_version : str + endpoint_bindings : typing.Mapping[str, str] + err : Error + exposed : bool + exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] + int_ : int + life : str + meter_statuses : typing.Mapping[str, ~MeterStatus] + provider_id : str + public_address : str + relations : typing.Mapping[str, typing.Sequence[str]] + status : DetailedStatus + subordinate_to : typing.Sequence[str] + units : typing.Mapping[str, ~UnitStatus] + workload_version : str + """ + base_ = Base.from_json(base) if base else None + can_upgrade_to_ = can_upgrade_to + charm_ = charm + charm_channel_ = charm_channel + charm_profile_ = charm_profile + charm_rev_ = charm_rev + charm_version_ = charm_version + endpoint_bindings_ = endpoint_bindings + err_ = Error.from_json(err) if err else None + exposed_ = exposed + exposed_endpoints_ = { + k: ExposedEndpoint.from_json(v) + for k, v in (exposed_endpoints or dict()).items() + } + int__ = int_ + life_ = life + meter_statuses_ = { + k: MeterStatus.from_json(v) for k, v in (meter_statuses or dict()).items() + } + provider_id_ = provider_id + public_address_ = public_address + relations_ = relations + status_ = DetailedStatus.from_json(status) if status else None + subordinate_to_ = subordinate_to + units_ = {k: UnitStatus.from_json(v) for k, v in (units or dict()).items()} + workload_version_ = workload_version + + # Validate arguments against known Juju API types. + if base_ is not None and not isinstance(base_, (dict, Base)): + raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") + + if can_upgrade_to_ is not None and not isinstance( + can_upgrade_to_, (bytes, str) + ): + raise Exception( + f"Expected can_upgrade_to_ to be a str, received: {type(can_upgrade_to_)}" + ) + + if charm_ is not None and not isinstance(charm_, (bytes, str)): + raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") + + if charm_channel_ is not None and not isinstance(charm_channel_, (bytes, str)): + raise Exception( + f"Expected charm_channel_ to be a str, received: {type(charm_channel_)}" + ) + + if charm_profile_ is not None and not isinstance(charm_profile_, (bytes, str)): + raise Exception( + f"Expected charm_profile_ to be a str, received: {type(charm_profile_)}" + ) + + if charm_rev_ is not None and not isinstance(charm_rev_, int): + raise Exception( + f"Expected charm_rev_ to be a int, received: {type(charm_rev_)}" + ) + + if charm_version_ is not None and not isinstance(charm_version_, (bytes, str)): + raise Exception( + f"Expected charm_version_ to be a str, received: {type(charm_version_)}" + ) + + if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): + raise Exception( + f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" + ) + + if err_ is not None and not isinstance(err_, (dict, Error)): + raise Exception(f"Expected err_ to be a Error, received: {type(err_)}") + + if exposed_ is not None and not isinstance(exposed_, bool): + raise Exception( + f"Expected exposed_ to be a bool, received: {type(exposed_)}" + ) + + if exposed_endpoints_ is not None and not isinstance(exposed_endpoints_, dict): + raise Exception( + f"Expected exposed_endpoints_ to be a Mapping, received: {type(exposed_endpoints_)}" + ) + + if int__ is not None and not isinstance(int__, int): + raise Exception(f"Expected int__ to be a int, received: {type(int__)}") + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if meter_statuses_ is not None and not isinstance(meter_statuses_, dict): + raise Exception( + f"Expected meter_statuses_ to be a Mapping, received: {type(meter_statuses_)}" + ) + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if public_address_ is not None and not isinstance( + public_address_, (bytes, str) + ): + raise Exception( + f"Expected public_address_ to be a str, received: {type(public_address_)}" + ) + + if relations_ is not None and not isinstance(relations_, dict): + raise Exception( + f"Expected relations_ to be a Mapping, received: {type(relations_)}" + ) + + if status_ is not None and not isinstance(status_, (dict, DetailedStatus)): + raise Exception( + f"Expected status_ to be a DetailedStatus, received: {type(status_)}" + ) + + if subordinate_to_ is not None and not isinstance( + subordinate_to_, (bytes, str, list) + ): + raise Exception( + f"Expected subordinate_to_ to be a Sequence, received: {type(subordinate_to_)}" + ) + + if units_ is not None and not isinstance(units_, dict): + raise Exception( + f"Expected units_ to be a Mapping, received: {type(units_)}" + ) + + if workload_version_ is not None and not isinstance( + workload_version_, (bytes, str) + ): + raise Exception( + f"Expected workload_version_ to be a str, received: {type(workload_version_)}" + ) + + self.base = base_ + self.can_upgrade_to = can_upgrade_to_ + self.charm = charm_ + self.charm_channel = charm_channel_ + self.charm_profile = charm_profile_ + self.charm_rev = charm_rev_ + self.charm_version = charm_version_ + self.endpoint_bindings = endpoint_bindings_ + self.err = err_ + self.exposed = exposed_ + self.exposed_endpoints = exposed_endpoints_ + self.int_ = int__ + self.life = life_ + self.meter_statuses = meter_statuses_ + self.provider_id = provider_id_ + self.public_address = public_address_ + self.relations = relations_ + self.status = status_ + self.subordinate_to = subordinate_to_ + self.units = units_ + self.workload_version = workload_version_ + self.unknown_fields = unknown_fields + + +class ApplicationUnexpose(Type): + _toSchema = {"application": "application", "exposed_endpoints": "exposed-endpoints"} + _toPy = {"application": "application", "exposed-endpoints": "exposed_endpoints"} + + def __init__(self, application=None, exposed_endpoints=None, **unknown_fields): + """Application : str + exposed_endpoints : typing.Sequence[str] + """ + application_ = application + exposed_endpoints_ = exposed_endpoints + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if exposed_endpoints_ is not None and not isinstance( + exposed_endpoints_, (bytes, str, list) + ): + raise Exception( + f"Expected exposed_endpoints_ to be a Sequence, received: {type(exposed_endpoints_)}" + ) + + self.application = application_ + self.exposed_endpoints = exposed_endpoints_ + self.unknown_fields = unknown_fields + + +class ApplicationUnset(Type): + _toSchema = {"application": "application", "branch": "branch", "options": "options"} + _toPy = {"application": "application", "branch": "branch", "options": "options"} + + def __init__(self, application=None, branch=None, options=None, **unknown_fields): + """Application : str + branch : str + options : typing.Sequence[str] + """ + application_ = application + branch_ = branch + options_ = options + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if branch_ is not None and not isinstance(branch_, (bytes, str)): + raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") + + if options_ is not None and not isinstance(options_, (bytes, str, list)): + raise Exception( + f"Expected options_ to be a Sequence, received: {type(options_)}" + ) + + self.application = application_ + self.branch = branch_ + self.options = options_ + self.unknown_fields = unknown_fields + + +class ApplicationsCharmActionsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ApplicationCharmActionsResult]""" + results_ = [ApplicationCharmActionsResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ApplicationsDeploy(Type): + _toSchema = {"applications": "applications"} + _toPy = {"applications": "applications"} + + def __init__(self, applications=None, **unknown_fields): + """Applications : typing.Sequence[~ApplicationDeploy]""" + applications_ = [ApplicationDeploy.from_json(o) for o in applications or []] + + # Validate arguments against known Juju API types. + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + self.applications = applications_ + self.unknown_fields = unknown_fields + + +class AuthUserInfo(Type): + _toSchema = { + "controller_access": "controller-access", + "credentials": "credentials", + "display_name": "display-name", + "identity": "identity", + "last_connection": "last-connection", + "model_access": "model-access", + } + _toPy = { + "controller-access": "controller_access", + "credentials": "credentials", + "display-name": "display_name", + "identity": "identity", + "last-connection": "last_connection", + "model-access": "model_access", + } + + def __init__( + self, + controller_access=None, + credentials=None, + display_name=None, + identity=None, + last_connection=None, + model_access=None, + **unknown_fields, + ): + """controller_access : str + credentials : str + display_name : str + identity : str + last_connection : str + model_access : str + """ + controller_access_ = controller_access + credentials_ = credentials + display_name_ = display_name + identity_ = identity + last_connection_ = last_connection + model_access_ = model_access + + # Validate arguments against known Juju API types. + if controller_access_ is not None and not isinstance( + controller_access_, (bytes, str) + ): + raise Exception( + f"Expected controller_access_ to be a str, received: {type(controller_access_)}" + ) + + if credentials_ is not None and not isinstance(credentials_, (bytes, str)): + raise Exception( + f"Expected credentials_ to be a str, received: {type(credentials_)}" + ) + + if display_name_ is not None and not isinstance(display_name_, (bytes, str)): + raise Exception( + f"Expected display_name_ to be a str, received: {type(display_name_)}" + ) + + if identity_ is not None and not isinstance(identity_, (bytes, str)): + raise Exception( + f"Expected identity_ to be a str, received: {type(identity_)}" + ) + + if last_connection_ is not None and not isinstance( + last_connection_, (bytes, str) + ): + raise Exception( + f"Expected last_connection_ to be a str, received: {type(last_connection_)}" + ) + + if model_access_ is not None and not isinstance(model_access_, (bytes, str)): + raise Exception( + f"Expected model_access_ to be a str, received: {type(model_access_)}" + ) + + self.controller_access = controller_access_ + self.credentials = credentials_ + self.display_name = display_name_ + self.identity = identity_ + self.last_connection = last_connection_ + self.model_access = model_access_ + self.unknown_fields = unknown_fields + + +class BackupsCreateArgs(Type): + _toSchema = {"no_download": "no-download", "notes": "notes"} + _toPy = {"no-download": "no_download", "notes": "notes"} + + def __init__(self, no_download=None, notes=None, **unknown_fields): + """no_download : bool + notes : str + """ + no_download_ = no_download + notes_ = notes + + # Validate arguments against known Juju API types. + if no_download_ is not None and not isinstance(no_download_, bool): + raise Exception( + f"Expected no_download_ to be a bool, received: {type(no_download_)}" + ) + + if notes_ is not None and not isinstance(notes_, (bytes, str)): + raise Exception(f"Expected notes_ to be a str, received: {type(notes_)}") + + self.no_download = no_download_ + self.notes = notes_ + self.unknown_fields = unknown_fields + + +class BackupsMetadataResult(Type): + _toSchema = { + "base": "base", + "checksum": "checksum", + "checksum_format": "checksum-format", + "controller_machine_id": "controller-machine-id", + "controller_machine_inst_id": "controller-machine-inst-id", + "controller_uuid": "controller-uuid", + "filename": "filename", + "finished": "finished", + "format_version": "format-version", + "ha_nodes": "ha-nodes", + "hostname": "hostname", + "id_": "id", + "machine": "machine", + "model": "model", + "notes": "notes", + "size": "size", + "started": "started", + "stored": "stored", + "version": "version", + } + _toPy = { + "base": "base", + "checksum": "checksum", + "checksum-format": "checksum_format", + "controller-machine-id": "controller_machine_id", + "controller-machine-inst-id": "controller_machine_inst_id", + "controller-uuid": "controller_uuid", + "filename": "filename", + "finished": "finished", + "format-version": "format_version", + "ha-nodes": "ha_nodes", + "hostname": "hostname", + "id": "id_", + "machine": "machine", + "model": "model", + "notes": "notes", + "size": "size", + "started": "started", + "stored": "stored", + "version": "version", + } + + def __init__( + self, + base=None, + checksum=None, + checksum_format=None, + controller_machine_id=None, + controller_machine_inst_id=None, + controller_uuid=None, + filename=None, + finished=None, + format_version=None, + ha_nodes=None, + hostname=None, + id_=None, + machine=None, + model=None, + notes=None, + size=None, + started=None, + stored=None, + version=None, + **unknown_fields, + ): + """Base : str + checksum : str + checksum_format : str + controller_machine_id : str + controller_machine_inst_id : str + controller_uuid : str + filename : str + finished : str + format_version : int + ha_nodes : int + hostname : str + id_ : str + machine : str + model : str + notes : str + size : int + started : str + stored : str + version : Number + """ + base_ = base + checksum_ = checksum + checksum_format_ = checksum_format + controller_machine_id_ = controller_machine_id + controller_machine_inst_id_ = controller_machine_inst_id + controller_uuid_ = controller_uuid + filename_ = filename + finished_ = finished + format_version_ = format_version + ha_nodes_ = ha_nodes + hostname_ = hostname + id__ = id_ + machine_ = machine + model_ = model + notes_ = notes + size_ = size + started_ = started + stored_ = stored + version_ = Number.from_json(version) if version else None + + # Validate arguments against known Juju API types. + if base_ is not None and not isinstance(base_, (bytes, str)): + raise Exception(f"Expected base_ to be a str, received: {type(base_)}") + + if checksum_ is not None and not isinstance(checksum_, (bytes, str)): + raise Exception( + f"Expected checksum_ to be a str, received: {type(checksum_)}" + ) + + if checksum_format_ is not None and not isinstance( + checksum_format_, (bytes, str) + ): + raise Exception( + f"Expected checksum_format_ to be a str, received: {type(checksum_format_)}" + ) + + if controller_machine_id_ is not None and not isinstance( + controller_machine_id_, (bytes, str) + ): + raise Exception( + f"Expected controller_machine_id_ to be a str, received: {type(controller_machine_id_)}" + ) + + if controller_machine_inst_id_ is not None and not isinstance( + controller_machine_inst_id_, (bytes, str) + ): + raise Exception( + f"Expected controller_machine_inst_id_ to be a str, received: {type(controller_machine_inst_id_)}" + ) + + if controller_uuid_ is not None and not isinstance( + controller_uuid_, (bytes, str) + ): + raise Exception( + f"Expected controller_uuid_ to be a str, received: {type(controller_uuid_)}" + ) + + if filename_ is not None and not isinstance(filename_, (bytes, str)): + raise Exception( + f"Expected filename_ to be a str, received: {type(filename_)}" + ) + + if finished_ is not None and not isinstance(finished_, (bytes, str)): + raise Exception( + f"Expected finished_ to be a str, received: {type(finished_)}" + ) + + if format_version_ is not None and not isinstance(format_version_, int): + raise Exception( + f"Expected format_version_ to be a int, received: {type(format_version_)}" + ) + + if ha_nodes_ is not None and not isinstance(ha_nodes_, int): + raise Exception( + f"Expected ha_nodes_ to be a int, received: {type(ha_nodes_)}" + ) + + if hostname_ is not None and not isinstance(hostname_, (bytes, str)): + raise Exception( + f"Expected hostname_ to be a str, received: {type(hostname_)}" + ) + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if machine_ is not None and not isinstance(machine_, (bytes, str)): + raise Exception( + f"Expected machine_ to be a str, received: {type(machine_)}" + ) + + if model_ is not None and not isinstance(model_, (bytes, str)): + raise Exception(f"Expected model_ to be a str, received: {type(model_)}") + + if notes_ is not None and not isinstance(notes_, (bytes, str)): + raise Exception(f"Expected notes_ to be a str, received: {type(notes_)}") + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + if started_ is not None and not isinstance(started_, (bytes, str)): + raise Exception( + f"Expected started_ to be a str, received: {type(started_)}" + ) + + if stored_ is not None and not isinstance(stored_, (bytes, str)): + raise Exception(f"Expected stored_ to be a str, received: {type(stored_)}") + + if version_ is not None and not isinstance(version_, (dict, Number)): + raise Exception( + f"Expected version_ to be a Number, received: {type(version_)}" + ) + + self.base = base_ + self.checksum = checksum_ + self.checksum_format = checksum_format_ + self.controller_machine_id = controller_machine_id_ + self.controller_machine_inst_id = controller_machine_inst_id_ + self.controller_uuid = controller_uuid_ + self.filename = filename_ + self.finished = finished_ + self.format_version = format_version_ + self.ha_nodes = ha_nodes_ + self.hostname = hostname_ + self.id_ = id__ + self.machine = machine_ + self.model = model_ + self.notes = notes_ + self.size = size_ + self.started = started_ + self.stored = stored_ + self.version = version_ + self.unknown_fields = unknown_fields + + +class Base(Type): + _toSchema = {"channel": "channel", "name": "name"} + _toPy = {"channel": "channel", "name": "name"} + + def __init__(self, channel=None, name=None, **unknown_fields): + """Channel : str + name : str + """ + channel_ = channel + name_ = name + + # Validate arguments against known Juju API types. + if channel_ is not None and not isinstance(channel_, (bytes, str)): + raise Exception( + f"Expected channel_ to be a str, received: {type(channel_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + self.channel = channel_ + self.name = name_ + self.unknown_fields = unknown_fields + + +class Binary(Type): + _toSchema = { + "arch": "Arch", + "build": "Build", + "major": "Major", + "minor": "Minor", + "number": "Number", + "patch": "Patch", + "release": "Release", + "tag": "Tag", + } + _toPy = { + "Arch": "arch", + "Build": "build", + "Major": "major", + "Minor": "minor", + "Number": "number", + "Patch": "patch", + "Release": "release", + "Tag": "tag", + } + + def __init__( + self, + arch=None, + build=None, + major=None, + minor=None, + number=None, + patch=None, + release=None, + tag=None, + **unknown_fields, + ): + """Arch : str + build : int + major : int + minor : int + number : Number + patch : int + release : str + tag : str + """ + arch_ = arch + build_ = build + major_ = major + minor_ = minor + number_ = Number.from_json(number) if number else None + patch_ = patch + release_ = release + tag_ = tag + + # Validate arguments against known Juju API types. + if arch_ is not None and not isinstance(arch_, (bytes, str)): + raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") + + if build_ is not None and not isinstance(build_, int): + raise Exception(f"Expected build_ to be a int, received: {type(build_)}") + + if major_ is not None and not isinstance(major_, int): + raise Exception(f"Expected major_ to be a int, received: {type(major_)}") + + if minor_ is not None and not isinstance(minor_, int): + raise Exception(f"Expected minor_ to be a int, received: {type(minor_)}") + + if number_ is not None and not isinstance(number_, (dict, Number)): + raise Exception( + f"Expected number_ to be a Number, received: {type(number_)}" + ) + + if patch_ is not None and not isinstance(patch_, int): + raise Exception(f"Expected patch_ to be a int, received: {type(patch_)}") + + if release_ is not None and not isinstance(release_, (bytes, str)): + raise Exception( + f"Expected release_ to be a str, received: {type(release_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.arch = arch_ + self.build = build_ + self.major = major_ + self.minor = minor_ + self.number = number_ + self.patch = patch_ + self.release = release_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class Block(Type): + _toSchema = {"id_": "id", "message": "message", "tag": "tag", "type_": "type"} + _toPy = {"id": "id_", "message": "message", "tag": "tag", "type": "type_"} + + def __init__(self, id_=None, message=None, tag=None, type_=None, **unknown_fields): + """id_ : str + message : str + tag : str + type_ : str + """ + id__ = id_ + message_ = message + tag_ = tag + type__ = type_ + + # Validate arguments against known Juju API types. + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.id_ = id__ + self.message = message_ + self.tag = tag_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class BlockResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : Block + """ + error_ = Error.from_json(error) if error else None + result_ = Block.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, Block)): + raise Exception( + f"Expected result_ to be a Block, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class BlockResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~BlockResult]""" + results_ = [BlockResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class BlockSwitchParams(Type): + _toSchema = {"message": "message", "type_": "type"} + _toPy = {"message": "message", "type": "type_"} + + def __init__(self, message=None, type_=None, **unknown_fields): + """Message : str + type_ : str + """ + message_ = message + type__ = type_ + + # Validate arguments against known Juju API types. + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.message = message_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class BoolResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : bool + """ + error_ = Error.from_json(error) if error else None + result_ = result + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, bool): + raise Exception(f"Expected result_ to be a bool, received: {type(result_)}") + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class BranchArg(Type): + _toSchema = {"branch": "branch"} + _toPy = {"branch": "branch"} + + def __init__(self, branch=None, **unknown_fields): + """Branch : str""" + branch_ = branch + + # Validate arguments against known Juju API types. + if branch_ is not None and not isinstance(branch_, (bytes, str)): + raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") + + self.branch = branch_ + self.unknown_fields = unknown_fields + + +class BranchInfoArgs(Type): + _toSchema = {"branches": "branches", "detailed": "detailed"} + _toPy = {"branches": "branches", "detailed": "detailed"} + + def __init__(self, branches=None, detailed=None, **unknown_fields): + """Branches : typing.Sequence[str] + detailed : bool + """ + branches_ = branches + detailed_ = detailed + + # Validate arguments against known Juju API types. + if branches_ is not None and not isinstance(branches_, (bytes, str, list)): + raise Exception( + f"Expected branches_ to be a Sequence, received: {type(branches_)}" + ) + + if detailed_ is not None and not isinstance(detailed_, bool): + raise Exception( + f"Expected detailed_ to be a bool, received: {type(detailed_)}" + ) + + self.branches = branches_ + self.detailed = detailed_ + self.unknown_fields = unknown_fields + + +class BranchResults(Type): + _toSchema = {"error": "error", "generations": "generations"} + _toPy = {"error": "error", "generations": "generations"} + + def __init__(self, error=None, generations=None, **unknown_fields): + """Error : Error + generations : typing.Sequence[~Generation] + """ + error_ = Error.from_json(error) if error else None + generations_ = [Generation.from_json(o) for o in generations or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if generations_ is not None and not isinstance( + generations_, (bytes, str, list) + ): + raise Exception( + f"Expected generations_ to be a Sequence, received: {type(generations_)}" + ) + + self.error = error_ + self.generations = generations_ + self.unknown_fields = unknown_fields + + +class BranchStatus(Type): + _toSchema = { + "assigned_units": "assigned-units", + "created": "created", + "created_by": "created-by", + } + _toPy = { + "assigned-units": "assigned_units", + "created": "created", + "created-by": "created_by", + } + + def __init__( + self, assigned_units=None, created=None, created_by=None, **unknown_fields + ): + """assigned_units : typing.Mapping[str, typing.Sequence[str]] + created : int + created_by : str + """ + assigned_units_ = assigned_units + created_ = created + created_by_ = created_by + + # Validate arguments against known Juju API types. + if assigned_units_ is not None and not isinstance(assigned_units_, dict): + raise Exception( + f"Expected assigned_units_ to be a Mapping, received: {type(assigned_units_)}" + ) + + if created_ is not None and not isinstance(created_, int): + raise Exception( + f"Expected created_ to be a int, received: {type(created_)}" + ) + + if created_by_ is not None and not isinstance(created_by_, (bytes, str)): + raise Exception( + f"Expected created_by_ to be a str, received: {type(created_by_)}" + ) + + self.assigned_units = assigned_units_ + self.created = created_ + self.created_by = created_by_ + self.unknown_fields = unknown_fields + + +class BranchTrackArg(Type): + _toSchema = {"branch": "branch", "entities": "entities", "num_units": "num-units"} + _toPy = {"branch": "branch", "entities": "entities", "num-units": "num_units"} + + def __init__(self, branch=None, entities=None, num_units=None, **unknown_fields): + """Branch : str + entities : typing.Sequence[~Entity] + num_units : int + """ + branch_ = branch + entities_ = [Entity.from_json(o) for o in entities or []] + num_units_ = num_units + + # Validate arguments against known Juju API types. + if branch_ is not None and not isinstance(branch_, (bytes, str)): + raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") + + if entities_ is not None and not isinstance(entities_, (bytes, str, list)): + raise Exception( + f"Expected entities_ to be a Sequence, received: {type(entities_)}" + ) + + if num_units_ is not None and not isinstance(num_units_, int): + raise Exception( + f"Expected num_units_ to be a int, received: {type(num_units_)}" + ) + + self.branch = branch_ + self.entities = entities_ + self.num_units = num_units_ + self.unknown_fields = unknown_fields + + +class BulkImportStorageParams(Type): + _toSchema = {"storage": "storage"} + _toPy = {"storage": "storage"} + + def __init__(self, storage=None, **unknown_fields): + """Storage : typing.Sequence[~ImportStorageParams]""" + storage_ = [ImportStorageParams.from_json(o) for o in storage or []] + + # Validate arguments against known Juju API types. + if storage_ is not None and not isinstance(storage_, (bytes, str, list)): + raise Exception( + f"Expected storage_ to be a Sequence, received: {type(storage_)}" + ) + + self.storage = storage_ + self.unknown_fields = unknown_fields + + +class BundleChange(Type): + _toSchema = { + "args": "args", + "id_": "id", + "method": "method", + "requires": "requires", + } + _toPy = {"args": "args", "id": "id_", "method": "method", "requires": "requires"} + + def __init__( + self, args=None, id_=None, method=None, requires=None, **unknown_fields + ): + """Args : typing.Sequence[typing.Any] + id_ : str + method : str + requires : typing.Sequence[str] + """ + args_ = args + id__ = id_ + method_ = method + requires_ = requires + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if method_ is not None and not isinstance(method_, (bytes, str)): + raise Exception(f"Expected method_ to be a str, received: {type(method_)}") + + if requires_ is not None and not isinstance(requires_, (bytes, str, list)): + raise Exception( + f"Expected requires_ to be a Sequence, received: {type(requires_)}" + ) + + self.args = args_ + self.id_ = id__ + self.method = method_ + self.requires = requires_ + self.unknown_fields = unknown_fields + + +class BundleChangesMapArgs(Type): + _toSchema = { + "args": "args", + "id_": "id", + "method": "method", + "requires": "requires", + } + _toPy = {"args": "args", "id": "id_", "method": "method", "requires": "requires"} + + def __init__( + self, args=None, id_=None, method=None, requires=None, **unknown_fields + ): + """Args : typing.Mapping[str, typing.Any] + id_ : str + method : str + requires : typing.Sequence[str] + """ + args_ = args + id__ = id_ + method_ = method + requires_ = requires + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, dict): + raise Exception(f"Expected args_ to be a Mapping, received: {type(args_)}") + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if method_ is not None and not isinstance(method_, (bytes, str)): + raise Exception(f"Expected method_ to be a str, received: {type(method_)}") + + if requires_ is not None and not isinstance(requires_, (bytes, str, list)): + raise Exception( + f"Expected requires_ to be a Sequence, received: {type(requires_)}" + ) + + self.args = args_ + self.id_ = id__ + self.method = method_ + self.requires = requires_ + self.unknown_fields = unknown_fields + + +class BundleChangesMapArgsResults(Type): + _toSchema = {"changes": "changes", "errors": "errors"} + _toPy = {"changes": "changes", "errors": "errors"} + + def __init__(self, changes=None, errors=None, **unknown_fields): + """Changes : typing.Sequence[~BundleChangesMapArgs] + errors : typing.Sequence[str] + """ + changes_ = [BundleChangesMapArgs.from_json(o) for o in changes or []] + errors_ = errors + + # Validate arguments against known Juju API types. + if changes_ is not None and not isinstance(changes_, (bytes, str, list)): + raise Exception( + f"Expected changes_ to be a Sequence, received: {type(changes_)}" + ) + + if errors_ is not None and not isinstance(errors_, (bytes, str, list)): + raise Exception( + f"Expected errors_ to be a Sequence, received: {type(errors_)}" + ) + + self.changes = changes_ + self.errors = errors_ + self.unknown_fields = unknown_fields + + +class BundleChangesParams(Type): + _toSchema = {"bundleurl": "bundleURL", "yaml": "yaml"} + _toPy = {"bundleURL": "bundleurl", "yaml": "yaml"} + + def __init__(self, bundleurl=None, yaml=None, **unknown_fields): + """Bundleurl : str + yaml : str + """ + bundleurl_ = bundleurl + yaml_ = yaml + + # Validate arguments against known Juju API types. + if bundleurl_ is not None and not isinstance(bundleurl_, (bytes, str)): + raise Exception( + f"Expected bundleurl_ to be a str, received: {type(bundleurl_)}" + ) + + if yaml_ is not None and not isinstance(yaml_, (bytes, str)): + raise Exception(f"Expected yaml_ to be a str, received: {type(yaml_)}") + + self.bundleurl = bundleurl_ + self.yaml = yaml_ + self.unknown_fields = unknown_fields + + +class BundleChangesResults(Type): + _toSchema = {"changes": "changes", "errors": "errors"} + _toPy = {"changes": "changes", "errors": "errors"} + + def __init__(self, changes=None, errors=None, **unknown_fields): + """Changes : typing.Sequence[~BundleChange] + errors : typing.Sequence[str] + """ + changes_ = [BundleChange.from_json(o) for o in changes or []] + errors_ = errors + + # Validate arguments against known Juju API types. + if changes_ is not None and not isinstance(changes_, (bytes, str, list)): + raise Exception( + f"Expected changes_ to be a Sequence, received: {type(changes_)}" + ) + + if errors_ is not None and not isinstance(errors_, (bytes, str, list)): + raise Exception( + f"Expected errors_ to be a Sequence, received: {type(errors_)}" + ) + + self.changes = changes_ + self.errors = errors_ + self.unknown_fields = unknown_fields + + +class CIDRParams(Type): + _toSchema = {"cidrs": "cidrs"} + _toPy = {"cidrs": "cidrs"} + + def __init__(self, cidrs=None, **unknown_fields): + """Cidrs : typing.Sequence[str]""" + cidrs_ = cidrs + + # Validate arguments against known Juju API types. + if cidrs_ is not None and not isinstance(cidrs_, (bytes, str, list)): + raise Exception( + f"Expected cidrs_ to be a Sequence, received: {type(cidrs_)}" + ) + + self.cidrs = cidrs_ + self.unknown_fields = unknown_fields + + +class ChangeModelCredentialParams(Type): + _toSchema = {"credential_tag": "credential-tag", "model_tag": "model-tag"} + _toPy = {"credential-tag": "credential_tag", "model-tag": "model_tag"} + + def __init__(self, credential_tag=None, model_tag=None, **unknown_fields): + """credential_tag : str + model_tag : str + """ + credential_tag_ = credential_tag + model_tag_ = model_tag + + # Validate arguments against known Juju API types. + if credential_tag_ is not None and not isinstance( + credential_tag_, (bytes, str) + ): + raise Exception( + f"Expected credential_tag_ to be a str, received: {type(credential_tag_)}" + ) + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + self.credential_tag = credential_tag_ + self.model_tag = model_tag_ + self.unknown_fields = unknown_fields + + +class ChangeModelCredentialsParams(Type): + _toSchema = {"model_credentials": "model-credentials"} + _toPy = {"model-credentials": "model_credentials"} + + def __init__(self, model_credentials=None, **unknown_fields): + """model_credentials : typing.Sequence[~ChangeModelCredentialParams]""" + model_credentials_ = [ + ChangeModelCredentialParams.from_json(o) for o in model_credentials or [] + ] + + # Validate arguments against known Juju API types. + if model_credentials_ is not None and not isinstance( + model_credentials_, (bytes, str, list) + ): + raise Exception( + f"Expected model_credentials_ to be a Sequence, received: {type(model_credentials_)}" + ) + + self.model_credentials = model_credentials_ + self.unknown_fields = unknown_fields + + +class Charm(Type): + _toSchema = { + "actions": "actions", + "config": "config", + "lxd_profile": "lxd-profile", + "manifest": "manifest", + "meta": "meta", + "metrics": "metrics", + "revision": "revision", + "url": "url", + } + _toPy = { + "actions": "actions", + "config": "config", + "lxd-profile": "lxd_profile", + "manifest": "manifest", + "meta": "meta", + "metrics": "metrics", + "revision": "revision", + "url": "url", + } + + def __init__( + self, + actions=None, + config=None, + lxd_profile=None, + manifest=None, + meta=None, + metrics=None, + revision=None, + url=None, + **unknown_fields, + ): + """Actions : CharmActions + config : typing.Mapping[str, ~CharmOption] + lxd_profile : CharmLXDProfile + manifest : CharmManifest + meta : CharmMeta + metrics : CharmMetrics + revision : int + url : str + """ + actions_ = CharmActions.from_json(actions) if actions else None + config_ = {k: CharmOption.from_json(v) for k, v in (config or dict()).items()} + lxd_profile_ = CharmLXDProfile.from_json(lxd_profile) if lxd_profile else None + manifest_ = CharmManifest.from_json(manifest) if manifest else None + meta_ = CharmMeta.from_json(meta) if meta else None + metrics_ = CharmMetrics.from_json(metrics) if metrics else None + revision_ = revision + url_ = url + + # Validate arguments against known Juju API types. + if actions_ is not None and not isinstance(actions_, (dict, CharmActions)): + raise Exception( + f"Expected actions_ to be a CharmActions, received: {type(actions_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if lxd_profile_ is not None and not isinstance( + lxd_profile_, (dict, CharmLXDProfile) + ): + raise Exception( + f"Expected lxd_profile_ to be a CharmLXDProfile, received: {type(lxd_profile_)}" + ) + + if manifest_ is not None and not isinstance(manifest_, (dict, CharmManifest)): + raise Exception( + f"Expected manifest_ to be a CharmManifest, received: {type(manifest_)}" + ) + + if meta_ is not None and not isinstance(meta_, (dict, CharmMeta)): + raise Exception( + f"Expected meta_ to be a CharmMeta, received: {type(meta_)}" + ) + + if metrics_ is not None and not isinstance(metrics_, (dict, CharmMetrics)): + raise Exception( + f"Expected metrics_ to be a CharmMetrics, received: {type(metrics_)}" + ) + + if revision_ is not None and not isinstance(revision_, int): + raise Exception( + f"Expected revision_ to be a int, received: {type(revision_)}" + ) + + if url_ is not None and not isinstance(url_, (bytes, str)): + raise Exception(f"Expected url_ to be a str, received: {type(url_)}") + + self.actions = actions_ + self.config = config_ + self.lxd_profile = lxd_profile_ + self.manifest = manifest_ + self.meta = meta_ + self.metrics = metrics_ + self.revision = revision_ + self.url = url_ + self.unknown_fields = unknown_fields + + +class CharmActionSpec(Type): + _toSchema = {"description": "description", "params": "params"} + _toPy = {"description": "description", "params": "params"} + + def __init__(self, description=None, params=None, **unknown_fields): + """Description : str + params : typing.Mapping[str, typing.Any] + """ + description_ = description + params_ = params + + # Validate arguments against known Juju API types. + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if params_ is not None and not isinstance(params_, dict): + raise Exception( + f"Expected params_ to be a Mapping, received: {type(params_)}" + ) + + self.description = description_ + self.params = params_ + self.unknown_fields = unknown_fields + + +class CharmActions(Type): + _toSchema = {"specs": "specs"} + _toPy = {"specs": "specs"} + + def __init__(self, specs=None, **unknown_fields): + """Specs : typing.Mapping[str, ~CharmActionSpec]""" + specs_ = {k: CharmActionSpec.from_json(v) for k, v in (specs or dict()).items()} + + # Validate arguments against known Juju API types. + if specs_ is not None and not isinstance(specs_, dict): + raise Exception( + f"Expected specs_ to be a Mapping, received: {type(specs_)}" + ) + + self.specs = specs_ + self.unknown_fields = unknown_fields + + +class CharmBase(Type): + _toSchema = {"architectures": "architectures", "channel": "channel", "name": "name"} + _toPy = {"architectures": "architectures", "channel": "channel", "name": "name"} + + def __init__(self, architectures=None, channel=None, name=None, **unknown_fields): + """Architectures : typing.Sequence[str] + channel : str + name : str + """ + architectures_ = architectures + channel_ = channel + name_ = name + + # Validate arguments against known Juju API types. + if architectures_ is not None and not isinstance( + architectures_, (bytes, str, list) + ): + raise Exception( + f"Expected architectures_ to be a Sequence, received: {type(architectures_)}" + ) + + if channel_ is not None and not isinstance(channel_, (bytes, str)): + raise Exception( + f"Expected channel_ to be a str, received: {type(channel_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + self.architectures = architectures_ + self.channel = channel_ + self.name = name_ + self.unknown_fields = unknown_fields + + +class CharmContainer(Type): + _toSchema = {"gid": "gid", "mounts": "mounts", "resource": "resource", "uid": "uid"} + _toPy = {"gid": "gid", "mounts": "mounts", "resource": "resource", "uid": "uid"} + + def __init__( + self, gid=None, mounts=None, resource=None, uid=None, **unknown_fields + ): + """Gid : int + mounts : typing.Sequence[~CharmMount] + resource : str + uid : int + """ + gid_ = gid + mounts_ = [CharmMount.from_json(o) for o in mounts or []] + resource_ = resource + uid_ = uid + + # Validate arguments against known Juju API types. + if gid_ is not None and not isinstance(gid_, int): + raise Exception(f"Expected gid_ to be a int, received: {type(gid_)}") + + if mounts_ is not None and not isinstance(mounts_, (bytes, str, list)): + raise Exception( + f"Expected mounts_ to be a Sequence, received: {type(mounts_)}" + ) + + if resource_ is not None and not isinstance(resource_, (bytes, str)): + raise Exception( + f"Expected resource_ to be a str, received: {type(resource_)}" + ) + + if uid_ is not None and not isinstance(uid_, int): + raise Exception(f"Expected uid_ to be a int, received: {type(uid_)}") + + self.gid = gid_ + self.mounts = mounts_ + self.resource = resource_ + self.uid = uid_ + self.unknown_fields = unknown_fields + + +class CharmDeployment(Type): + _toSchema = { + "min_version": "min-version", + "mode": "mode", + "service": "service", + "type_": "type", + } + _toPy = { + "min-version": "min_version", + "mode": "mode", + "service": "service", + "type": "type_", + } + + def __init__( + self, min_version=None, mode=None, service=None, type_=None, **unknown_fields + ): + """min_version : str + mode : str + service : str + type_ : str + """ + min_version_ = min_version + mode_ = mode + service_ = service + type__ = type_ + + # Validate arguments against known Juju API types. + if min_version_ is not None and not isinstance(min_version_, (bytes, str)): + raise Exception( + f"Expected min_version_ to be a str, received: {type(min_version_)}" + ) + + if mode_ is not None and not isinstance(mode_, (bytes, str)): + raise Exception(f"Expected mode_ to be a str, received: {type(mode_)}") + + if service_ is not None and not isinstance(service_, (bytes, str)): + raise Exception( + f"Expected service_ to be a str, received: {type(service_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.min_version = min_version_ + self.mode = mode_ + self.service = service_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmDevice(Type): + _toSchema = { + "countmax": "CountMax", + "countmin": "CountMin", + "description": "Description", + "name": "Name", + "type_": "Type", + } + _toPy = { + "CountMax": "countmax", + "CountMin": "countmin", + "Description": "description", + "Name": "name", + "Type": "type_", + } + + def __init__( + self, + countmax=None, + countmin=None, + description=None, + name=None, + type_=None, + **unknown_fields, + ): + """Countmax : int + countmin : int + description : str + name : str + type_ : str + """ + countmax_ = countmax + countmin_ = countmin + description_ = description + name_ = name + type__ = type_ + + # Validate arguments against known Juju API types. + if countmax_ is not None and not isinstance(countmax_, int): + raise Exception( + f"Expected countmax_ to be a int, received: {type(countmax_)}" + ) + + if countmin_ is not None and not isinstance(countmin_, int): + raise Exception( + f"Expected countmin_ to be a int, received: {type(countmin_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.countmax = countmax_ + self.countmin = countmin_ + self.description = description_ + self.name = name_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmLXDProfile(Type): + _toSchema = {"config": "config", "description": "description", "devices": "devices"} + _toPy = {"config": "config", "description": "description", "devices": "devices"} + + def __init__(self, config=None, description=None, devices=None, **unknown_fields): + """Config : typing.Mapping[str, str] + description : str + devices : typing.Mapping[str, typing.Any] + """ + config_ = config + description_ = description + devices_ = devices + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if devices_ is not None and not isinstance(devices_, dict): + raise Exception( + f"Expected devices_ to be a Mapping, received: {type(devices_)}" + ) + + self.config = config_ + self.description = description_ + self.devices = devices_ + self.unknown_fields = unknown_fields + + +class CharmManifest(Type): + _toSchema = {"bases": "bases"} + _toPy = {"bases": "bases"} + + def __init__(self, bases=None, **unknown_fields): + """Bases : typing.Sequence[~CharmBase]""" + bases_ = [CharmBase.from_json(o) for o in bases or []] + + # Validate arguments against known Juju API types. + if bases_ is not None and not isinstance(bases_, (bytes, str, list)): + raise Exception( + f"Expected bases_ to be a Sequence, received: {type(bases_)}" + ) + + self.bases = bases_ + self.unknown_fields = unknown_fields + + +class CharmMeta(Type): + _toSchema = { + "assumes_expr": "assumes-expr", + "categories": "categories", + "charm_user": "charm-user", + "containers": "containers", + "deployment": "deployment", + "description": "description", + "devices": "devices", + "extra_bindings": "extra-bindings", + "min_juju_version": "min-juju-version", + "name": "name", + "payload_classes": "payload-classes", + "peers": "peers", + "provides": "provides", + "requires": "requires", + "resources": "resources", + "series": "series", + "storage": "storage", + "subordinate": "subordinate", + "summary": "summary", + "tags": "tags", + "terms": "terms", + } + _toPy = { + "assumes-expr": "assumes_expr", + "categories": "categories", + "charm-user": "charm_user", + "containers": "containers", + "deployment": "deployment", + "description": "description", + "devices": "devices", + "extra-bindings": "extra_bindings", + "min-juju-version": "min_juju_version", + "name": "name", + "payload-classes": "payload_classes", + "peers": "peers", + "provides": "provides", + "requires": "requires", + "resources": "resources", + "series": "series", + "storage": "storage", + "subordinate": "subordinate", + "summary": "summary", + "tags": "tags", + "terms": "terms", + } + + def __init__( + self, + assumes_expr=None, + categories=None, + charm_user=None, + containers=None, + deployment=None, + description=None, + devices=None, + extra_bindings=None, + min_juju_version=None, + name=None, + payload_classes=None, + peers=None, + provides=None, + requires=None, + resources=None, + series=None, + storage=None, + subordinate=None, + summary=None, + tags=None, + terms=None, + **unknown_fields, + ): + """assumes_expr : ExpressionTree + categories : typing.Sequence[str] + charm_user : str + containers : typing.Mapping[str, ~CharmContainer] + deployment : CharmDeployment + description : str + devices : typing.Mapping[str, ~CharmDevice] + extra_bindings : typing.Mapping[str, str] + min_juju_version : str + name : str + payload_classes : typing.Mapping[str, ~CharmPayloadClass] + peers : typing.Mapping[str, ~CharmRelation] + provides : typing.Mapping[str, ~CharmRelation] + requires : typing.Mapping[str, ~CharmRelation] + resources : typing.Mapping[str, ~CharmResourceMeta] + series : typing.Sequence[str] + storage : typing.Mapping[str, ~CharmStorage] + subordinate : bool + summary : str + tags : typing.Sequence[str] + terms : typing.Sequence[str] + """ + assumes_expr_ = ExpressionTree.from_json(assumes_expr) if assumes_expr else None + categories_ = categories + charm_user_ = charm_user + containers_ = { + k: CharmContainer.from_json(v) for k, v in (containers or dict()).items() + } + deployment_ = CharmDeployment.from_json(deployment) if deployment else None + description_ = description + devices_ = {k: CharmDevice.from_json(v) for k, v in (devices or dict()).items()} + extra_bindings_ = extra_bindings + min_juju_version_ = min_juju_version + name_ = name + payload_classes_ = { + k: CharmPayloadClass.from_json(v) + for k, v in (payload_classes or dict()).items() + } + peers_ = {k: CharmRelation.from_json(v) for k, v in (peers or dict()).items()} + provides_ = { + k: CharmRelation.from_json(v) for k, v in (provides or dict()).items() + } + requires_ = { + k: CharmRelation.from_json(v) for k, v in (requires or dict()).items() + } + resources_ = { + k: CharmResourceMeta.from_json(v) for k, v in (resources or dict()).items() + } + series_ = series + storage_ = { + k: CharmStorage.from_json(v) for k, v in (storage or dict()).items() + } + subordinate_ = subordinate + summary_ = summary + tags_ = tags + terms_ = terms + + # Validate arguments against known Juju API types. + if assumes_expr_ is not None and not isinstance( + assumes_expr_, (dict, ExpressionTree) + ): + raise Exception( + f"Expected assumes_expr_ to be a ExpressionTree, received: {type(assumes_expr_)}" + ) + + if categories_ is not None and not isinstance(categories_, (bytes, str, list)): + raise Exception( + f"Expected categories_ to be a Sequence, received: {type(categories_)}" + ) + + if charm_user_ is not None and not isinstance(charm_user_, (bytes, str)): + raise Exception( + f"Expected charm_user_ to be a str, received: {type(charm_user_)}" + ) + + if containers_ is not None and not isinstance(containers_, dict): + raise Exception( + f"Expected containers_ to be a Mapping, received: {type(containers_)}" + ) + + if deployment_ is not None and not isinstance( + deployment_, (dict, CharmDeployment) + ): + raise Exception( + f"Expected deployment_ to be a CharmDeployment, received: {type(deployment_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if devices_ is not None and not isinstance(devices_, dict): + raise Exception( + f"Expected devices_ to be a Mapping, received: {type(devices_)}" + ) + + if extra_bindings_ is not None and not isinstance(extra_bindings_, dict): + raise Exception( + f"Expected extra_bindings_ to be a Mapping, received: {type(extra_bindings_)}" + ) + + if min_juju_version_ is not None and not isinstance( + min_juju_version_, (bytes, str) + ): + raise Exception( + f"Expected min_juju_version_ to be a str, received: {type(min_juju_version_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if payload_classes_ is not None and not isinstance(payload_classes_, dict): + raise Exception( + f"Expected payload_classes_ to be a Mapping, received: {type(payload_classes_)}" + ) + + if peers_ is not None and not isinstance(peers_, dict): + raise Exception( + f"Expected peers_ to be a Mapping, received: {type(peers_)}" + ) + + if provides_ is not None and not isinstance(provides_, dict): + raise Exception( + f"Expected provides_ to be a Mapping, received: {type(provides_)}" + ) + + if requires_ is not None and not isinstance(requires_, dict): + raise Exception( + f"Expected requires_ to be a Mapping, received: {type(requires_)}" + ) + + if resources_ is not None and not isinstance(resources_, dict): + raise Exception( + f"Expected resources_ to be a Mapping, received: {type(resources_)}" + ) + + if series_ is not None and not isinstance(series_, (bytes, str, list)): + raise Exception( + f"Expected series_ to be a Sequence, received: {type(series_)}" + ) + + if storage_ is not None and not isinstance(storage_, dict): + raise Exception( + f"Expected storage_ to be a Mapping, received: {type(storage_)}" + ) + + if subordinate_ is not None and not isinstance(subordinate_, bool): + raise Exception( + f"Expected subordinate_ to be a bool, received: {type(subordinate_)}" + ) + + if summary_ is not None and not isinstance(summary_, (bytes, str)): + raise Exception( + f"Expected summary_ to be a str, received: {type(summary_)}" + ) + + if tags_ is not None and not isinstance(tags_, (bytes, str, list)): + raise Exception(f"Expected tags_ to be a Sequence, received: {type(tags_)}") + + if terms_ is not None and not isinstance(terms_, (bytes, str, list)): + raise Exception( + f"Expected terms_ to be a Sequence, received: {type(terms_)}" + ) + + self.assumes_expr = assumes_expr_ + self.categories = categories_ + self.charm_user = charm_user_ + self.containers = containers_ + self.deployment = deployment_ + self.description = description_ + self.devices = devices_ + self.extra_bindings = extra_bindings_ + self.min_juju_version = min_juju_version_ + self.name = name_ + self.payload_classes = payload_classes_ + self.peers = peers_ + self.provides = provides_ + self.requires = requires_ + self.resources = resources_ + self.series = series_ + self.storage = storage_ + self.subordinate = subordinate_ + self.summary = summary_ + self.tags = tags_ + self.terms = terms_ + self.unknown_fields = unknown_fields + + +class CharmMetric(Type): + _toSchema = {"description": "description", "type_": "type"} + _toPy = {"description": "description", "type": "type_"} + + def __init__(self, description=None, type_=None, **unknown_fields): + """Description : str + type_ : str + """ + description_ = description + type__ = type_ + + # Validate arguments against known Juju API types. + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.description = description_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmMetrics(Type): + _toSchema = {"metrics": "metrics", "plan": "plan"} + _toPy = {"metrics": "metrics", "plan": "plan"} + + def __init__(self, metrics=None, plan=None, **unknown_fields): + """Metrics : typing.Mapping[str, ~CharmMetric] + plan : CharmPlan + """ + metrics_ = {k: CharmMetric.from_json(v) for k, v in (metrics or dict()).items()} + plan_ = CharmPlan.from_json(plan) if plan else None + + # Validate arguments against known Juju API types. + if metrics_ is not None and not isinstance(metrics_, dict): + raise Exception( + f"Expected metrics_ to be a Mapping, received: {type(metrics_)}" + ) + + if plan_ is not None and not isinstance(plan_, (dict, CharmPlan)): + raise Exception( + f"Expected plan_ to be a CharmPlan, received: {type(plan_)}" + ) + + self.metrics = metrics_ + self.plan = plan_ + self.unknown_fields = unknown_fields + + +class CharmMount(Type): + _toSchema = {"location": "location", "storage": "storage"} + _toPy = {"location": "location", "storage": "storage"} + + def __init__(self, location=None, storage=None, **unknown_fields): + """Location : str + storage : str + """ + location_ = location + storage_ = storage + + # Validate arguments against known Juju API types. + if location_ is not None and not isinstance(location_, (bytes, str)): + raise Exception( + f"Expected location_ to be a str, received: {type(location_)}" + ) + + if storage_ is not None and not isinstance(storage_, (bytes, str)): + raise Exception( + f"Expected storage_ to be a str, received: {type(storage_)}" + ) + + self.location = location_ + self.storage = storage_ + self.unknown_fields = unknown_fields + + +class CharmOption(Type): + _toSchema = {"default": "default", "description": "description", "type_": "type"} + _toPy = {"default": "default", "description": "description", "type": "type_"} + + def __init__(self, default=None, description=None, type_=None, **unknown_fields): + """Default : Any + description : str + type_ : str + """ + default_ = default + description_ = description + type__ = type_ + + # Validate arguments against known Juju API types. + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.default = default_ + self.description = description_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmOrigin(Type): + _toSchema = { + "architecture": "architecture", + "base": "base", + "branch": "branch", + "hash_": "hash", + "id_": "id", + "instance_key": "instance-key", + "revision": "revision", + "risk": "risk", + "source": "source", + "track": "track", + "type_": "type", + } + _toPy = { + "architecture": "architecture", + "base": "base", + "branch": "branch", + "hash": "hash_", + "id": "id_", + "instance-key": "instance_key", + "revision": "revision", + "risk": "risk", + "source": "source", + "track": "track", + "type": "type_", + } + + def __init__( + self, + architecture=None, + base=None, + branch=None, + hash_=None, + id_=None, + instance_key=None, + revision=None, + risk=None, + source=None, + track=None, + type_=None, + **unknown_fields, + ): + """Architecture : str + base : Base + branch : str + hash_ : str + id_ : str + instance_key : str + revision : int + risk : str + source : str + track : str + type_ : str + """ + architecture_ = architecture + base_ = Base.from_json(base) if base else None + branch_ = branch + hash__ = hash_ + id__ = id_ + instance_key_ = instance_key + revision_ = revision + risk_ = risk + source_ = source + track_ = track + type__ = type_ + + # Validate arguments against known Juju API types. + if architecture_ is not None and not isinstance(architecture_, (bytes, str)): + raise Exception( + f"Expected architecture_ to be a str, received: {type(architecture_)}" + ) + + if base_ is not None and not isinstance(base_, (dict, Base)): + raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") + + if branch_ is not None and not isinstance(branch_, (bytes, str)): + raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") + + if hash__ is not None and not isinstance(hash__, (bytes, str)): + raise Exception(f"Expected hash__ to be a str, received: {type(hash__)}") + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if instance_key_ is not None and not isinstance(instance_key_, (bytes, str)): + raise Exception( + f"Expected instance_key_ to be a str, received: {type(instance_key_)}" + ) + + if revision_ is not None and not isinstance(revision_, int): + raise Exception( + f"Expected revision_ to be a int, received: {type(revision_)}" + ) + + if risk_ is not None and not isinstance(risk_, (bytes, str)): + raise Exception(f"Expected risk_ to be a str, received: {type(risk_)}") + + if source_ is not None and not isinstance(source_, (bytes, str)): + raise Exception(f"Expected source_ to be a str, received: {type(source_)}") + + if track_ is not None and not isinstance(track_, (bytes, str)): + raise Exception(f"Expected track_ to be a str, received: {type(track_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.architecture = architecture_ + self.base = base_ + self.branch = branch_ + self.hash_ = hash__ + self.id_ = id__ + self.instance_key = instance_key_ + self.revision = revision_ + self.risk = risk_ + self.source = source_ + self.track = track_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmOriginResult(Type): + _toSchema = {"charm_origin": "charm-origin", "error": "error"} + _toPy = {"charm-origin": "charm_origin", "error": "error"} + + def __init__(self, charm_origin=None, error=None, **unknown_fields): + """charm_origin : CharmOrigin + error : Error + """ + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.charm_origin = charm_origin_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class CharmPayloadClass(Type): + _toSchema = {"name": "name", "type_": "type"} + _toPy = {"name": "name", "type": "type_"} + + def __init__(self, name=None, type_=None, **unknown_fields): + """Name : str + type_ : str + """ + name_ = name + type__ = type_ + + # Validate arguments against known Juju API types. + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.name = name_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmPlan(Type): + _toSchema = {"required": "required"} + _toPy = {"required": "required"} + + def __init__(self, required=None, **unknown_fields): + """Required : bool""" + required_ = required + + # Validate arguments against known Juju API types. + if required_ is not None and not isinstance(required_, bool): + raise Exception( + f"Expected required_ to be a bool, received: {type(required_)}" + ) + + self.required = required_ + self.unknown_fields = unknown_fields + + +class CharmRelation(Type): + _toSchema = { + "interface": "interface", + "limit": "limit", + "name": "name", + "optional": "optional", + "role": "role", + "scope": "scope", + } + _toPy = { + "interface": "interface", + "limit": "limit", + "name": "name", + "optional": "optional", + "role": "role", + "scope": "scope", + } + + def __init__( + self, + interface=None, + limit=None, + name=None, + optional=None, + role=None, + scope=None, + **unknown_fields, + ): + """Interface : str + limit : int + name : str + optional : bool + role : str + scope : str + """ + interface_ = interface + limit_ = limit + name_ = name + optional_ = optional + role_ = role + scope_ = scope + + # Validate arguments against known Juju API types. + if interface_ is not None and not isinstance(interface_, (bytes, str)): + raise Exception( + f"Expected interface_ to be a str, received: {type(interface_)}" + ) + + if limit_ is not None and not isinstance(limit_, int): + raise Exception(f"Expected limit_ to be a int, received: {type(limit_)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if optional_ is not None and not isinstance(optional_, bool): + raise Exception( + f"Expected optional_ to be a bool, received: {type(optional_)}" + ) + + if role_ is not None and not isinstance(role_, (bytes, str)): + raise Exception(f"Expected role_ to be a str, received: {type(role_)}") + + if scope_ is not None and not isinstance(scope_, (bytes, str)): + raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") + + self.interface = interface_ + self.limit = limit_ + self.name = name_ + self.optional = optional_ + self.role = role_ + self.scope = scope_ + self.unknown_fields = unknown_fields + + +class CharmResource(Type): + _toSchema = { + "description": "description", + "fingerprint": "fingerprint", + "name": "name", + "origin": "origin", + "path": "path", + "revision": "revision", + "size": "size", + "type_": "type", + } + _toPy = { + "description": "description", + "fingerprint": "fingerprint", + "name": "name", + "origin": "origin", + "path": "path", + "revision": "revision", + "size": "size", + "type": "type_", + } + + def __init__( + self, + description=None, + fingerprint=None, + name=None, + origin=None, + path=None, + revision=None, + size=None, + type_=None, + **unknown_fields, + ): + """Description : str + fingerprint : typing.Sequence[int] + name : str + origin : str + path : str + revision : int + size : int + type_ : str + """ + description_ = description + fingerprint_ = fingerprint + name_ = name + origin_ = origin + path_ = path + revision_ = revision + size_ = size + type__ = type_ + + # Validate arguments against known Juju API types. + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if fingerprint_ is not None and not isinstance( + fingerprint_, (bytes, str, list) + ): + raise Exception( + f"Expected fingerprint_ to be a Sequence, received: {type(fingerprint_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if origin_ is not None and not isinstance(origin_, (bytes, str)): + raise Exception(f"Expected origin_ to be a str, received: {type(origin_)}") + + if path_ is not None and not isinstance(path_, (bytes, str)): + raise Exception(f"Expected path_ to be a str, received: {type(path_)}") + + if revision_ is not None and not isinstance(revision_, int): + raise Exception( + f"Expected revision_ to be a int, received: {type(revision_)}" + ) + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.description = description_ + self.fingerprint = fingerprint_ + self.name = name_ + self.origin = origin_ + self.path = path_ + self.revision = revision_ + self.size = size_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmResourceMeta(Type): + _toSchema = { + "description": "description", + "name": "name", + "path": "path", + "type_": "type", + } + _toPy = { + "description": "description", + "name": "name", + "path": "path", + "type": "type_", + } + + def __init__( + self, description=None, name=None, path=None, type_=None, **unknown_fields + ): + """Description : str + name : str + path : str + type_ : str + """ + description_ = description + name_ = name + path_ = path + type__ = type_ + + # Validate arguments against known Juju API types. + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if path_ is not None and not isinstance(path_, (bytes, str)): + raise Exception(f"Expected path_ to be a str, received: {type(path_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.description = description_ + self.name = name_ + self.path = path_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmResourceResult(Type): + _toSchema = { + "charmresource": "CharmResource", + "description": "description", + "error": "error", + "errorresult": "ErrorResult", + "fingerprint": "fingerprint", + "name": "name", + "origin": "origin", + "path": "path", + "revision": "revision", + "size": "size", + "type_": "type", + } + _toPy = { + "CharmResource": "charmresource", + "ErrorResult": "errorresult", + "description": "description", + "error": "error", + "fingerprint": "fingerprint", + "name": "name", + "origin": "origin", + "path": "path", + "revision": "revision", + "size": "size", + "type": "type_", + } + + def __init__( + self, + charmresource=None, + errorresult=None, + description=None, + error=None, + fingerprint=None, + name=None, + origin=None, + path=None, + revision=None, + size=None, + type_=None, + **unknown_fields, + ): + """Charmresource : CharmResource + errorresult : ErrorResult + description : str + error : Error + fingerprint : typing.Sequence[int] + name : str + origin : str + path : str + revision : int + size : int + type_ : str + """ + charmresource_ = ( + CharmResource.from_json(charmresource) if charmresource else None + ) + errorresult_ = ErrorResult.from_json(errorresult) if errorresult else None + description_ = description + error_ = Error.from_json(error) if error else None + fingerprint_ = fingerprint + name_ = name + origin_ = origin + path_ = path + revision_ = revision + size_ = size + type__ = type_ + + # Validate arguments against known Juju API types. + if charmresource_ is not None and not isinstance( + charmresource_, (dict, CharmResource) + ): + raise Exception( + f"Expected charmresource_ to be a CharmResource, received: {type(charmresource_)}" + ) + + if errorresult_ is not None and not isinstance( + errorresult_, (dict, ErrorResult) + ): + raise Exception( + f"Expected errorresult_ to be a ErrorResult, received: {type(errorresult_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if fingerprint_ is not None and not isinstance( + fingerprint_, (bytes, str, list) + ): + raise Exception( + f"Expected fingerprint_ to be a Sequence, received: {type(fingerprint_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if origin_ is not None and not isinstance(origin_, (bytes, str)): + raise Exception(f"Expected origin_ to be a str, received: {type(origin_)}") + + if path_ is not None and not isinstance(path_, (bytes, str)): + raise Exception(f"Expected path_ to be a str, received: {type(path_)}") + + if revision_ is not None and not isinstance(revision_, int): + raise Exception( + f"Expected revision_ to be a int, received: {type(revision_)}" + ) + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.charmresource = charmresource_ + self.errorresult = errorresult_ + self.description = description_ + self.error = error_ + self.fingerprint = fingerprint_ + self.name = name_ + self.origin = origin_ + self.path = path_ + self.revision = revision_ + self.size = size_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmResourcesResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~CharmResourceResult]""" + results_ = [CharmResourceResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class CharmStorage(Type): + _toSchema = { + "count_max": "count-max", + "count_min": "count-min", + "description": "description", + "location": "location", + "minimum_size": "minimum-size", + "name": "name", + "properties": "properties", + "read_only": "read-only", + "shared": "shared", + "type_": "type", + } + _toPy = { + "count-max": "count_max", + "count-min": "count_min", + "description": "description", + "location": "location", + "minimum-size": "minimum_size", + "name": "name", + "properties": "properties", + "read-only": "read_only", + "shared": "shared", + "type": "type_", + } + + def __init__( + self, + count_max=None, + count_min=None, + description=None, + location=None, + minimum_size=None, + name=None, + properties=None, + read_only=None, + shared=None, + type_=None, + **unknown_fields, + ): + """count_max : int + count_min : int + description : str + location : str + minimum_size : int + name : str + properties : typing.Sequence[str] + read_only : bool + shared : bool + type_ : str + """ + count_max_ = count_max + count_min_ = count_min + description_ = description + location_ = location + minimum_size_ = minimum_size + name_ = name + properties_ = properties + read_only_ = read_only + shared_ = shared + type__ = type_ + + # Validate arguments against known Juju API types. + if count_max_ is not None and not isinstance(count_max_, int): + raise Exception( + f"Expected count_max_ to be a int, received: {type(count_max_)}" + ) + + if count_min_ is not None and not isinstance(count_min_, int): + raise Exception( + f"Expected count_min_ to be a int, received: {type(count_min_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if location_ is not None and not isinstance(location_, (bytes, str)): + raise Exception( + f"Expected location_ to be a str, received: {type(location_)}" + ) + + if minimum_size_ is not None and not isinstance(minimum_size_, int): + raise Exception( + f"Expected minimum_size_ to be a int, received: {type(minimum_size_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if properties_ is not None and not isinstance(properties_, (bytes, str, list)): + raise Exception( + f"Expected properties_ to be a Sequence, received: {type(properties_)}" + ) + + if read_only_ is not None and not isinstance(read_only_, bool): + raise Exception( + f"Expected read_only_ to be a bool, received: {type(read_only_)}" + ) + + if shared_ is not None and not isinstance(shared_, bool): + raise Exception(f"Expected shared_ to be a bool, received: {type(shared_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.count_max = count_max_ + self.count_min = count_min_ + self.description = description_ + self.location = location_ + self.minimum_size = minimum_size_ + self.name = name_ + self.properties = properties_ + self.read_only = read_only_ + self.shared = shared_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CharmURL(Type): + _toSchema = {"url": "url"} + _toPy = {"url": "url"} + + def __init__(self, url=None, **unknown_fields): + """Url : str""" + url_ = url + + # Validate arguments against known Juju API types. + if url_ is not None and not isinstance(url_, (bytes, str)): + raise Exception(f"Expected url_ to be a str, received: {type(url_)}") + + self.url = url_ + self.unknown_fields = unknown_fields + + +class CharmURLAndOrigin(Type): + _toSchema = { + "charm_origin": "charm-origin", + "charm_url": "charm-url", + "macaroon": "macaroon", + } + _toPy = { + "charm-origin": "charm_origin", + "charm-url": "charm_url", + "macaroon": "macaroon", + } + + def __init__( + self, charm_origin=None, charm_url=None, macaroon=None, **unknown_fields + ): + """charm_origin : CharmOrigin + charm_url : str + macaroon : Macaroon + """ + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + charm_url_ = charm_url + macaroon_ = Macaroon.from_json(macaroon) if macaroon else None + + # Validate arguments against known Juju API types. + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): + raise Exception( + f"Expected charm_url_ to be a str, received: {type(charm_url_)}" + ) + + if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): + raise Exception( + f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" + ) + + self.charm_origin = charm_origin_ + self.charm_url = charm_url_ + self.macaroon = macaroon_ + self.unknown_fields = unknown_fields + + +class CharmURLAndOrigins(Type): + _toSchema = {"entities": "entities"} + _toPy = {"entities": "entities"} + + def __init__(self, entities=None, **unknown_fields): + """Entities : typing.Sequence[~CharmURLAndOrigin]""" + entities_ = [CharmURLAndOrigin.from_json(o) for o in entities or []] + + # Validate arguments against known Juju API types. + if entities_ is not None and not isinstance(entities_, (bytes, str, list)): + raise Exception( + f"Expected entities_ to be a Sequence, received: {type(entities_)}" + ) + + self.entities = entities_ + self.unknown_fields = unknown_fields + + +class CharmURLOriginResult(Type): + _toSchema = {"charm_origin": "charm-origin", "error": "error", "url": "url"} + _toPy = {"charm-origin": "charm_origin", "error": "error", "url": "url"} + + def __init__(self, charm_origin=None, error=None, url=None, **unknown_fields): + """charm_origin : CharmOrigin + error : Error + url : str + """ + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + error_ = Error.from_json(error) if error else None + url_ = url + + # Validate arguments against known Juju API types. + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if url_ is not None and not isinstance(url_, (bytes, str)): + raise Exception(f"Expected url_ to be a str, received: {type(url_)}") + + self.charm_origin = charm_origin_ + self.error = error_ + self.url = url_ + self.unknown_fields = unknown_fields + + +class CharmsList(Type): + _toSchema = {"names": "names"} + _toPy = {"names": "names"} + + def __init__(self, names=None, **unknown_fields): + """Names : typing.Sequence[str]""" + names_ = names + + # Validate arguments against known Juju API types. + if names_ is not None and not isinstance(names_, (bytes, str, list)): + raise Exception( + f"Expected names_ to be a Sequence, received: {type(names_)}" + ) + + self.names = names_ + self.unknown_fields = unknown_fields + + +class CharmsListResult(Type): + _toSchema = {"charm_urls": "charm-urls"} + _toPy = {"charm-urls": "charm_urls"} + + def __init__(self, charm_urls=None, **unknown_fields): + """charm_urls : typing.Sequence[str]""" + charm_urls_ = charm_urls + + # Validate arguments against known Juju API types. + if charm_urls_ is not None and not isinstance(charm_urls_, (bytes, str, list)): + raise Exception( + f"Expected charm_urls_ to be a Sequence, received: {type(charm_urls_)}" + ) + + self.charm_urls = charm_urls_ + self.unknown_fields = unknown_fields + + +class Cloud(Type): + _toSchema = { + "auth_types": "auth-types", + "ca_certificates": "ca-certificates", + "config": "config", + "endpoint": "endpoint", + "host_cloud_region": "host-cloud-region", + "identity_endpoint": "identity-endpoint", + "is_controller_cloud": "is-controller-cloud", + "region_config": "region-config", + "regions": "regions", + "skip_tls_verify": "skip-tls-verify", + "storage_endpoint": "storage-endpoint", + "type_": "type", + } + _toPy = { + "auth-types": "auth_types", + "ca-certificates": "ca_certificates", + "config": "config", + "endpoint": "endpoint", + "host-cloud-region": "host_cloud_region", + "identity-endpoint": "identity_endpoint", + "is-controller-cloud": "is_controller_cloud", + "region-config": "region_config", + "regions": "regions", + "skip-tls-verify": "skip_tls_verify", + "storage-endpoint": "storage_endpoint", + "type": "type_", + } + + def __init__( + self, + auth_types=None, + ca_certificates=None, + config=None, + endpoint=None, + host_cloud_region=None, + identity_endpoint=None, + is_controller_cloud=None, + region_config=None, + regions=None, + skip_tls_verify=None, + storage_endpoint=None, + type_=None, + **unknown_fields, + ): + """auth_types : typing.Sequence[str] + ca_certificates : typing.Sequence[str] + config : typing.Mapping[str, typing.Any] + endpoint : str + host_cloud_region : str + identity_endpoint : str + is_controller_cloud : bool + region_config : typing.Mapping[str, typing.Any] + regions : typing.Sequence[~CloudRegion] + skip_tls_verify : bool + storage_endpoint : str + type_ : str + """ + auth_types_ = auth_types + ca_certificates_ = ca_certificates + config_ = config + endpoint_ = endpoint + host_cloud_region_ = host_cloud_region + identity_endpoint_ = identity_endpoint + is_controller_cloud_ = is_controller_cloud + region_config_ = region_config + regions_ = [CloudRegion.from_json(o) for o in regions or []] + skip_tls_verify_ = skip_tls_verify + storage_endpoint_ = storage_endpoint + type__ = type_ + + # Validate arguments against known Juju API types. + if auth_types_ is not None and not isinstance(auth_types_, (bytes, str, list)): + raise Exception( + f"Expected auth_types_ to be a Sequence, received: {type(auth_types_)}" + ) + + if ca_certificates_ is not None and not isinstance( + ca_certificates_, (bytes, str, list) + ): + raise Exception( + f"Expected ca_certificates_ to be a Sequence, received: {type(ca_certificates_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): + raise Exception( + f"Expected endpoint_ to be a str, received: {type(endpoint_)}" + ) + + if host_cloud_region_ is not None and not isinstance( + host_cloud_region_, (bytes, str) + ): + raise Exception( + f"Expected host_cloud_region_ to be a str, received: {type(host_cloud_region_)}" + ) + + if identity_endpoint_ is not None and not isinstance( + identity_endpoint_, (bytes, str) + ): + raise Exception( + f"Expected identity_endpoint_ to be a str, received: {type(identity_endpoint_)}" + ) + + if is_controller_cloud_ is not None and not isinstance( + is_controller_cloud_, bool + ): + raise Exception( + f"Expected is_controller_cloud_ to be a bool, received: {type(is_controller_cloud_)}" + ) + + if region_config_ is not None and not isinstance(region_config_, dict): + raise Exception( + f"Expected region_config_ to be a Mapping, received: {type(region_config_)}" + ) + + if regions_ is not None and not isinstance(regions_, (bytes, str, list)): + raise Exception( + f"Expected regions_ to be a Sequence, received: {type(regions_)}" + ) + + if skip_tls_verify_ is not None and not isinstance(skip_tls_verify_, bool): + raise Exception( + f"Expected skip_tls_verify_ to be a bool, received: {type(skip_tls_verify_)}" + ) + + if storage_endpoint_ is not None and not isinstance( + storage_endpoint_, (bytes, str) + ): + raise Exception( + f"Expected storage_endpoint_ to be a str, received: {type(storage_endpoint_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.auth_types = auth_types_ + self.ca_certificates = ca_certificates_ + self.config = config_ + self.endpoint = endpoint_ + self.host_cloud_region = host_cloud_region_ + self.identity_endpoint = identity_endpoint_ + self.is_controller_cloud = is_controller_cloud_ + self.region_config = region_config_ + self.regions = regions_ + self.skip_tls_verify = skip_tls_verify_ + self.storage_endpoint = storage_endpoint_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CloudCredential(Type): + _toSchema = {"attrs": "attrs", "auth_type": "auth-type", "redacted": "redacted"} + _toPy = {"attrs": "attrs", "auth-type": "auth_type", "redacted": "redacted"} + + def __init__(self, attrs=None, auth_type=None, redacted=None, **unknown_fields): + """Attrs : typing.Mapping[str, str] + auth_type : str + redacted : typing.Sequence[str] + """ + attrs_ = attrs + auth_type_ = auth_type + redacted_ = redacted + + # Validate arguments against known Juju API types. + if attrs_ is not None and not isinstance(attrs_, dict): + raise Exception( + f"Expected attrs_ to be a Mapping, received: {type(attrs_)}" + ) + + if auth_type_ is not None and not isinstance(auth_type_, (bytes, str)): + raise Exception( + f"Expected auth_type_ to be a str, received: {type(auth_type_)}" + ) + + if redacted_ is not None and not isinstance(redacted_, (bytes, str, list)): + raise Exception( + f"Expected redacted_ to be a Sequence, received: {type(redacted_)}" + ) + + self.attrs = attrs_ + self.auth_type = auth_type_ + self.redacted = redacted_ + self.unknown_fields = unknown_fields + + +class CloudCredentialArg(Type): + _toSchema = {"cloud_name": "cloud-name", "credential_name": "credential-name"} + _toPy = {"cloud-name": "cloud_name", "credential-name": "credential_name"} + + def __init__(self, cloud_name=None, credential_name=None, **unknown_fields): + """cloud_name : str + credential_name : str + """ + cloud_name_ = cloud_name + credential_name_ = credential_name + + # Validate arguments against known Juju API types. + if cloud_name_ is not None and not isinstance(cloud_name_, (bytes, str)): + raise Exception( + f"Expected cloud_name_ to be a str, received: {type(cloud_name_)}" + ) + + if credential_name_ is not None and not isinstance( + credential_name_, (bytes, str) + ): + raise Exception( + f"Expected credential_name_ to be a str, received: {type(credential_name_)}" + ) + + self.cloud_name = cloud_name_ + self.credential_name = credential_name_ + self.unknown_fields = unknown_fields + + +class CloudCredentialArgs(Type): + _toSchema = {"credentials": "credentials", "include_secrets": "include-secrets"} + _toPy = {"credentials": "credentials", "include-secrets": "include_secrets"} + + def __init__(self, credentials=None, include_secrets=None, **unknown_fields): + """Credentials : typing.Sequence[~CloudCredentialArg] + include_secrets : bool + """ + credentials_ = [CloudCredentialArg.from_json(o) for o in credentials or []] + include_secrets_ = include_secrets + + # Validate arguments against known Juju API types. + if credentials_ is not None and not isinstance( + credentials_, (bytes, str, list) + ): + raise Exception( + f"Expected credentials_ to be a Sequence, received: {type(credentials_)}" + ) + + if include_secrets_ is not None and not isinstance(include_secrets_, bool): + raise Exception( + f"Expected include_secrets_ to be a bool, received: {type(include_secrets_)}" + ) + + self.credentials = credentials_ + self.include_secrets = include_secrets_ + self.unknown_fields = unknown_fields + + +class CloudCredentialResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : CloudCredential + """ + error_ = Error.from_json(error) if error else None + result_ = CloudCredential.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, CloudCredential)): + raise Exception( + f"Expected result_ to be a CloudCredential, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class CloudCredentialResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~CloudCredentialResult]""" + results_ = [CloudCredentialResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class CloudDetails(Type): + _toSchema = { + "auth_types": "auth-types", + "endpoint": "endpoint", + "identity_endpoint": "identity-endpoint", + "regions": "regions", + "storage_endpoint": "storage-endpoint", + "type_": "type", + } + _toPy = { + "auth-types": "auth_types", + "endpoint": "endpoint", + "identity-endpoint": "identity_endpoint", + "regions": "regions", + "storage-endpoint": "storage_endpoint", + "type": "type_", + } + + def __init__( + self, + auth_types=None, + endpoint=None, + identity_endpoint=None, + regions=None, + storage_endpoint=None, + type_=None, + **unknown_fields, + ): + """auth_types : typing.Sequence[str] + endpoint : str + identity_endpoint : str + regions : typing.Sequence[~CloudRegion] + storage_endpoint : str + type_ : str + """ + auth_types_ = auth_types + endpoint_ = endpoint + identity_endpoint_ = identity_endpoint + regions_ = [CloudRegion.from_json(o) for o in regions or []] + storage_endpoint_ = storage_endpoint + type__ = type_ + + # Validate arguments against known Juju API types. + if auth_types_ is not None and not isinstance(auth_types_, (bytes, str, list)): + raise Exception( + f"Expected auth_types_ to be a Sequence, received: {type(auth_types_)}" + ) + + if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): + raise Exception( + f"Expected endpoint_ to be a str, received: {type(endpoint_)}" + ) + + if identity_endpoint_ is not None and not isinstance( + identity_endpoint_, (bytes, str) + ): + raise Exception( + f"Expected identity_endpoint_ to be a str, received: {type(identity_endpoint_)}" + ) + + if regions_ is not None and not isinstance(regions_, (bytes, str, list)): + raise Exception( + f"Expected regions_ to be a Sequence, received: {type(regions_)}" + ) + + if storage_endpoint_ is not None and not isinstance( + storage_endpoint_, (bytes, str) + ): + raise Exception( + f"Expected storage_endpoint_ to be a str, received: {type(storage_endpoint_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.auth_types = auth_types_ + self.endpoint = endpoint_ + self.identity_endpoint = identity_endpoint_ + self.regions = regions_ + self.storage_endpoint = storage_endpoint_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CloudImageMetadata(Type): + _toSchema = { + "arch": "arch", + "image_id": "image-id", + "priority": "priority", + "region": "region", + "root_storage_size": "root-storage-size", + "root_storage_type": "root-storage-type", + "source": "source", + "stream": "stream", + "version": "version", + "virt_type": "virt-type", + } + _toPy = { + "arch": "arch", + "image-id": "image_id", + "priority": "priority", + "region": "region", + "root-storage-size": "root_storage_size", + "root-storage-type": "root_storage_type", + "source": "source", + "stream": "stream", + "version": "version", + "virt-type": "virt_type", + } + + def __init__( + self, + arch=None, + image_id=None, + priority=None, + region=None, + root_storage_size=None, + root_storage_type=None, + source=None, + stream=None, + version=None, + virt_type=None, + **unknown_fields, + ): + """Arch : str + image_id : str + priority : int + region : str + root_storage_size : int + root_storage_type : str + source : str + stream : str + version : str + virt_type : str + """ + arch_ = arch + image_id_ = image_id + priority_ = priority + region_ = region + root_storage_size_ = root_storage_size + root_storage_type_ = root_storage_type + source_ = source + stream_ = stream + version_ = version + virt_type_ = virt_type + + # Validate arguments against known Juju API types. + if arch_ is not None and not isinstance(arch_, (bytes, str)): + raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") + + if image_id_ is not None and not isinstance(image_id_, (bytes, str)): + raise Exception( + f"Expected image_id_ to be a str, received: {type(image_id_)}" + ) + + if priority_ is not None and not isinstance(priority_, int): + raise Exception( + f"Expected priority_ to be a int, received: {type(priority_)}" + ) + + if region_ is not None and not isinstance(region_, (bytes, str)): + raise Exception(f"Expected region_ to be a str, received: {type(region_)}") + + if root_storage_size_ is not None and not isinstance(root_storage_size_, int): + raise Exception( + f"Expected root_storage_size_ to be a int, received: {type(root_storage_size_)}" + ) + + if root_storage_type_ is not None and not isinstance( + root_storage_type_, (bytes, str) + ): + raise Exception( + f"Expected root_storage_type_ to be a str, received: {type(root_storage_type_)}" + ) + + if source_ is not None and not isinstance(source_, (bytes, str)): + raise Exception(f"Expected source_ to be a str, received: {type(source_)}") + + if stream_ is not None and not isinstance(stream_, (bytes, str)): + raise Exception(f"Expected stream_ to be a str, received: {type(stream_)}") + + if version_ is not None and not isinstance(version_, (bytes, str)): + raise Exception( + f"Expected version_ to be a str, received: {type(version_)}" + ) + + if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): + raise Exception( + f"Expected virt_type_ to be a str, received: {type(virt_type_)}" + ) + + self.arch = arch_ + self.image_id = image_id_ + self.priority = priority_ + self.region = region_ + self.root_storage_size = root_storage_size_ + self.root_storage_type = root_storage_type_ + self.source = source_ + self.stream = stream_ + self.version = version_ + self.virt_type = virt_type_ + self.unknown_fields = unknown_fields + + +class CloudImageMetadataList(Type): + _toSchema = {"metadata": "metadata"} + _toPy = {"metadata": "metadata"} + + def __init__(self, metadata=None, **unknown_fields): + """Metadata : typing.Sequence[~CloudImageMetadata]""" + metadata_ = [CloudImageMetadata.from_json(o) for o in metadata or []] + + # Validate arguments against known Juju API types. + if metadata_ is not None and not isinstance(metadata_, (bytes, str, list)): + raise Exception( + f"Expected metadata_ to be a Sequence, received: {type(metadata_)}" + ) + + self.metadata = metadata_ + self.unknown_fields = unknown_fields + + +class CloudInfo(Type): + _toSchema = {"clouddetails": "CloudDetails", "users": "users"} + _toPy = {"CloudDetails": "clouddetails", "users": "users"} + + def __init__(self, clouddetails=None, users=None, **unknown_fields): + """Clouddetails : CloudDetails + users : typing.Sequence[~CloudUserInfo] + """ + clouddetails_ = CloudDetails.from_json(clouddetails) if clouddetails else None + users_ = [CloudUserInfo.from_json(o) for o in users or []] + + # Validate arguments against known Juju API types. + if clouddetails_ is not None and not isinstance( + clouddetails_, (dict, CloudDetails) + ): + raise Exception( + f"Expected clouddetails_ to be a CloudDetails, received: {type(clouddetails_)}" + ) + + if users_ is not None and not isinstance(users_, (bytes, str, list)): + raise Exception( + f"Expected users_ to be a Sequence, received: {type(users_)}" + ) + + self.clouddetails = clouddetails_ + self.users = users_ + self.unknown_fields = unknown_fields + + +class CloudInfoResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : CloudInfo + """ + error_ = Error.from_json(error) if error else None + result_ = CloudInfo.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, CloudInfo)): + raise Exception( + f"Expected result_ to be a CloudInfo, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class CloudInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~CloudInfoResult]""" + results_ = [CloudInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class CloudInstanceTypesConstraint(Type): + _toSchema = { + "cloud_tag": "cloud-tag", + "constraints": "constraints", + "region": "region", + } + _toPy = {"cloud-tag": "cloud_tag", "constraints": "constraints", "region": "region"} + + def __init__(self, cloud_tag=None, constraints=None, region=None, **unknown_fields): + """cloud_tag : str + constraints : Value + region : str + """ + cloud_tag_ = cloud_tag + constraints_ = Value.from_json(constraints) if constraints else None + region_ = region + + # Validate arguments against known Juju API types. + if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): + raise Exception( + f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" + ) + + if constraints_ is not None and not isinstance(constraints_, (dict, Value)): + raise Exception( + f"Expected constraints_ to be a Value, received: {type(constraints_)}" + ) + + if region_ is not None and not isinstance(region_, (bytes, str)): + raise Exception(f"Expected region_ to be a str, received: {type(region_)}") + + self.cloud_tag = cloud_tag_ + self.constraints = constraints_ + self.region = region_ + self.unknown_fields = unknown_fields + + +class CloudInstanceTypesConstraints(Type): + _toSchema = {"constraints": "constraints"} + _toPy = {"constraints": "constraints"} + + def __init__(self, constraints=None, **unknown_fields): + """Constraints : typing.Sequence[~CloudInstanceTypesConstraint]""" + constraints_ = [ + CloudInstanceTypesConstraint.from_json(o) for o in constraints or [] + ] + + # Validate arguments against known Juju API types. + if constraints_ is not None and not isinstance( + constraints_, (bytes, str, list) + ): + raise Exception( + f"Expected constraints_ to be a Sequence, received: {type(constraints_)}" + ) + + self.constraints = constraints_ + self.unknown_fields = unknown_fields + + +class CloudRegion(Type): + _toSchema = { + "endpoint": "endpoint", + "identity_endpoint": "identity-endpoint", + "name": "name", + "storage_endpoint": "storage-endpoint", + } + _toPy = { + "endpoint": "endpoint", + "identity-endpoint": "identity_endpoint", + "name": "name", + "storage-endpoint": "storage_endpoint", + } + + def __init__( + self, + endpoint=None, + identity_endpoint=None, + name=None, + storage_endpoint=None, + **unknown_fields, + ): + """Endpoint : str + identity_endpoint : str + name : str + storage_endpoint : str + """ + endpoint_ = endpoint + identity_endpoint_ = identity_endpoint + name_ = name + storage_endpoint_ = storage_endpoint + + # Validate arguments against known Juju API types. + if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): + raise Exception( + f"Expected endpoint_ to be a str, received: {type(endpoint_)}" + ) + + if identity_endpoint_ is not None and not isinstance( + identity_endpoint_, (bytes, str) + ): + raise Exception( + f"Expected identity_endpoint_ to be a str, received: {type(identity_endpoint_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if storage_endpoint_ is not None and not isinstance( + storage_endpoint_, (bytes, str) + ): + raise Exception( + f"Expected storage_endpoint_ to be a str, received: {type(storage_endpoint_)}" + ) + + self.endpoint = endpoint_ + self.identity_endpoint = identity_endpoint_ + self.name = name_ + self.storage_endpoint = storage_endpoint_ + self.unknown_fields = unknown_fields + + +class CloudResult(Type): + _toSchema = {"cloud": "cloud", "error": "error"} + _toPy = {"cloud": "cloud", "error": "error"} + + def __init__(self, cloud=None, error=None, **unknown_fields): + """Cloud : Cloud + error : Error + """ + cloud_ = Cloud.from_json(cloud) if cloud else None + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if cloud_ is not None and not isinstance(cloud_, (dict, Cloud)): + raise Exception(f"Expected cloud_ to be a Cloud, received: {type(cloud_)}") + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.cloud = cloud_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class CloudResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~CloudResult]""" + results_ = [CloudResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class CloudSpec(Type): + _toSchema = { + "cacertificates": "cacertificates", + "credential": "credential", + "endpoint": "endpoint", + "identity_endpoint": "identity-endpoint", + "is_controller_cloud": "is-controller-cloud", + "name": "name", + "region": "region", + "skip_tls_verify": "skip-tls-verify", + "storage_endpoint": "storage-endpoint", + "type_": "type", + } + _toPy = { + "cacertificates": "cacertificates", + "credential": "credential", + "endpoint": "endpoint", + "identity-endpoint": "identity_endpoint", + "is-controller-cloud": "is_controller_cloud", + "name": "name", + "region": "region", + "skip-tls-verify": "skip_tls_verify", + "storage-endpoint": "storage_endpoint", + "type": "type_", + } + + def __init__( + self, + cacertificates=None, + credential=None, + endpoint=None, + identity_endpoint=None, + is_controller_cloud=None, + name=None, + region=None, + skip_tls_verify=None, + storage_endpoint=None, + type_=None, + **unknown_fields, + ): + """Cacertificates : typing.Sequence[str] + credential : CloudCredential + endpoint : str + identity_endpoint : str + is_controller_cloud : bool + name : str + region : str + skip_tls_verify : bool + storage_endpoint : str + type_ : str + """ + cacertificates_ = cacertificates + credential_ = CloudCredential.from_json(credential) if credential else None + endpoint_ = endpoint + identity_endpoint_ = identity_endpoint + is_controller_cloud_ = is_controller_cloud + name_ = name + region_ = region + skip_tls_verify_ = skip_tls_verify + storage_endpoint_ = storage_endpoint + type__ = type_ + + # Validate arguments against known Juju API types. + if cacertificates_ is not None and not isinstance( + cacertificates_, (bytes, str, list) + ): + raise Exception( + f"Expected cacertificates_ to be a Sequence, received: {type(cacertificates_)}" + ) + + if credential_ is not None and not isinstance( + credential_, (dict, CloudCredential) + ): + raise Exception( + f"Expected credential_ to be a CloudCredential, received: {type(credential_)}" + ) + + if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): + raise Exception( + f"Expected endpoint_ to be a str, received: {type(endpoint_)}" + ) + + if identity_endpoint_ is not None and not isinstance( + identity_endpoint_, (bytes, str) + ): + raise Exception( + f"Expected identity_endpoint_ to be a str, received: {type(identity_endpoint_)}" + ) + + if is_controller_cloud_ is not None and not isinstance( + is_controller_cloud_, bool + ): + raise Exception( + f"Expected is_controller_cloud_ to be a bool, received: {type(is_controller_cloud_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if region_ is not None and not isinstance(region_, (bytes, str)): + raise Exception(f"Expected region_ to be a str, received: {type(region_)}") + + if skip_tls_verify_ is not None and not isinstance(skip_tls_verify_, bool): + raise Exception( + f"Expected skip_tls_verify_ to be a bool, received: {type(skip_tls_verify_)}" + ) + + if storage_endpoint_ is not None and not isinstance( + storage_endpoint_, (bytes, str) + ): + raise Exception( + f"Expected storage_endpoint_ to be a str, received: {type(storage_endpoint_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.cacertificates = cacertificates_ + self.credential = credential_ + self.endpoint = endpoint_ + self.identity_endpoint = identity_endpoint_ + self.is_controller_cloud = is_controller_cloud_ + self.name = name_ + self.region = region_ + self.skip_tls_verify = skip_tls_verify_ + self.storage_endpoint = storage_endpoint_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class CloudSpecResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : CloudSpec + """ + error_ = Error.from_json(error) if error else None + result_ = CloudSpec.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, CloudSpec)): + raise Exception( + f"Expected result_ to be a CloudSpec, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class CloudSpecResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~CloudSpecResult]""" + results_ = [CloudSpecResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class CloudUserInfo(Type): + _toSchema = {"access": "access", "display_name": "display-name", "user": "user"} + _toPy = {"access": "access", "display-name": "display_name", "user": "user"} + + def __init__(self, access=None, display_name=None, user=None, **unknown_fields): + """Access : str + display_name : str + user : str + """ + access_ = access + display_name_ = display_name + user_ = user + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if display_name_ is not None and not isinstance(display_name_, (bytes, str)): + raise Exception( + f"Expected display_name_ to be a str, received: {type(display_name_)}" + ) + + if user_ is not None and not isinstance(user_, (bytes, str)): + raise Exception(f"Expected user_ to be a str, received: {type(user_)}") + + self.access = access_ + self.display_name = display_name_ + self.user = user_ + self.unknown_fields = unknown_fields + + +class CloudsResult(Type): + _toSchema = {"clouds": "clouds"} + _toPy = {"clouds": "clouds"} + + def __init__(self, clouds=None, **unknown_fields): + """Clouds : typing.Mapping[str, ~Cloud]""" + clouds_ = {k: Cloud.from_json(v) for k, v in (clouds or dict()).items()} + + # Validate arguments against known Juju API types. + if clouds_ is not None and not isinstance(clouds_, dict): + raise Exception( + f"Expected clouds_ to be a Mapping, received: {type(clouds_)}" + ) + + self.clouds = clouds_ + self.unknown_fields = unknown_fields + + +class ConfigResult(Type): + _toSchema = {"config": "config", "error": "error"} + _toPy = {"config": "config", "error": "error"} + + def __init__(self, config=None, error=None, **unknown_fields): + """Config : typing.Mapping[str, typing.Any] + error : Error + """ + config_ = config + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.config = config_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class ConfigSet(Type): + _toSchema = { + "application": "application", + "config": "config", + "config_yaml": "config-yaml", + "generation": "generation", + } + _toPy = { + "application": "application", + "config": "config", + "config-yaml": "config_yaml", + "generation": "generation", + } + + def __init__( + self, + application=None, + config=None, + config_yaml=None, + generation=None, + **unknown_fields, + ): + """Application : str + config : typing.Mapping[str, str] + config_yaml : str + generation : str + """ + application_ = application + config_ = config + config_yaml_ = config_yaml + generation_ = generation + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if config_yaml_ is not None and not isinstance(config_yaml_, (bytes, str)): + raise Exception( + f"Expected config_yaml_ to be a str, received: {type(config_yaml_)}" + ) + + if generation_ is not None and not isinstance(generation_, (bytes, str)): + raise Exception( + f"Expected generation_ to be a str, received: {type(generation_)}" + ) + + self.application = application_ + self.config = config_ + self.config_yaml = config_yaml_ + self.generation = generation_ + self.unknown_fields = unknown_fields + + +class ConfigSetArgs(Type): + _toSchema = {"args": "Args"} + _toPy = {"Args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~ConfigSet]""" + args_ = [ConfigSet.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class ConfigValue(Type): + _toSchema = {"source": "source", "value": "value"} + _toPy = {"source": "source", "value": "value"} + + def __init__(self, source=None, value=None, **unknown_fields): + """Source : str + value : Any + """ + source_ = source + value_ = value + + # Validate arguments against known Juju API types. + if source_ is not None and not isinstance(source_, (bytes, str)): + raise Exception(f"Expected source_ to be a str, received: {type(source_)}") + + self.source = source_ + self.value = value_ + self.unknown_fields = unknown_fields + + +class Constraints(Type): + _toSchema = {"count": "Count", "pool": "Pool", "size": "Size"} + _toPy = {"Count": "count", "Pool": "pool", "Size": "size"} + + def __init__(self, count=None, pool=None, size=None, **unknown_fields): + """Count : int + pool : str + size : int + """ + count_ = count + pool_ = pool + size_ = size + + # Validate arguments against known Juju API types. + if count_ is not None and not isinstance(count_, int): + raise Exception(f"Expected count_ to be a int, received: {type(count_)}") + + if pool_ is not None and not isinstance(pool_, (bytes, str)): + raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + self.count = count_ + self.pool = pool_ + self.size = size_ + self.unknown_fields = unknown_fields + + +class ConsumeApplicationArg(Type): + _toSchema = { + "application_alias": "application-alias", + "application_description": "application-description", + "applicationofferdetails": "ApplicationOfferDetails", + "bindings": "bindings", + "endpoints": "endpoints", + "external_controller": "external-controller", + "macaroon": "macaroon", + "offer_name": "offer-name", + "offer_url": "offer-url", + "offer_uuid": "offer-uuid", + "source_model_tag": "source-model-tag", + "spaces": "spaces", + "users": "users", + } + _toPy = { + "ApplicationOfferDetails": "applicationofferdetails", + "application-alias": "application_alias", + "application-description": "application_description", + "bindings": "bindings", + "endpoints": "endpoints", + "external-controller": "external_controller", + "macaroon": "macaroon", + "offer-name": "offer_name", + "offer-url": "offer_url", + "offer-uuid": "offer_uuid", + "source-model-tag": "source_model_tag", + "spaces": "spaces", + "users": "users", + } + + def __init__( + self, + applicationofferdetails=None, + application_alias=None, + application_description=None, + bindings=None, + endpoints=None, + external_controller=None, + macaroon=None, + offer_name=None, + offer_url=None, + offer_uuid=None, + source_model_tag=None, + spaces=None, + users=None, + **unknown_fields, + ): + """Applicationofferdetails : ApplicationOfferDetails + application_alias : str + application_description : str + bindings : typing.Mapping[str, str] + endpoints : typing.Sequence[~RemoteEndpoint] + external_controller : ExternalControllerInfo + macaroon : Macaroon + offer_name : str + offer_url : str + offer_uuid : str + source_model_tag : str + spaces : typing.Sequence[~RemoteSpace] + users : typing.Sequence[~OfferUserDetails] + """ + applicationofferdetails_ = ( + ApplicationOfferDetails.from_json(applicationofferdetails) + if applicationofferdetails + else None + ) + application_alias_ = application_alias + application_description_ = application_description + bindings_ = bindings + endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] + external_controller_ = ( + ExternalControllerInfo.from_json(external_controller) + if external_controller + else None + ) + macaroon_ = Macaroon.from_json(macaroon) if macaroon else None + offer_name_ = offer_name + offer_url_ = offer_url + offer_uuid_ = offer_uuid + source_model_tag_ = source_model_tag + spaces_ = [RemoteSpace.from_json(o) for o in spaces or []] + users_ = [OfferUserDetails.from_json(o) for o in users or []] + + # Validate arguments against known Juju API types. + if applicationofferdetails_ is not None and not isinstance( + applicationofferdetails_, (dict, ApplicationOfferDetails) + ): + raise Exception( + f"Expected applicationofferdetails_ to be a ApplicationOfferDetails, received: {type(applicationofferdetails_)}" + ) + + if application_alias_ is not None and not isinstance( + application_alias_, (bytes, str) + ): + raise Exception( + f"Expected application_alias_ to be a str, received: {type(application_alias_)}" + ) + + if application_description_ is not None and not isinstance( + application_description_, (bytes, str) + ): + raise Exception( + f"Expected application_description_ to be a str, received: {type(application_description_)}" + ) + + if bindings_ is not None and not isinstance(bindings_, dict): + raise Exception( + f"Expected bindings_ to be a Mapping, received: {type(bindings_)}" + ) + + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if external_controller_ is not None and not isinstance( + external_controller_, (dict, ExternalControllerInfo) + ): + raise Exception( + f"Expected external_controller_ to be a ExternalControllerInfo, received: {type(external_controller_)}" + ) + + if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): + raise Exception( + f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" + ) + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): + raise Exception( + f"Expected offer_url_ to be a str, received: {type(offer_url_)}" + ) + + if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): + raise Exception( + f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" + ) + + if source_model_tag_ is not None and not isinstance( + source_model_tag_, (bytes, str) + ): + raise Exception( + f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" + ) + + if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): + raise Exception( + f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" + ) + + if users_ is not None and not isinstance(users_, (bytes, str, list)): + raise Exception( + f"Expected users_ to be a Sequence, received: {type(users_)}" + ) + + self.applicationofferdetails = applicationofferdetails_ + self.application_alias = application_alias_ + self.application_description = application_description_ + self.bindings = bindings_ + self.endpoints = endpoints_ + self.external_controller = external_controller_ + self.macaroon = macaroon_ + self.offer_name = offer_name_ + self.offer_url = offer_url_ + self.offer_uuid = offer_uuid_ + self.source_model_tag = source_model_tag_ + self.spaces = spaces_ + self.users = users_ + self.unknown_fields = unknown_fields + + +class ConsumeApplicationArgV5(Type): + _toSchema = { + "application_alias": "application-alias", + "application_description": "application-description", + "applicationofferdetailsv5": "ApplicationOfferDetailsV5", + "endpoints": "endpoints", + "external_controller": "external-controller", + "macaroon": "macaroon", + "offer_name": "offer-name", + "offer_url": "offer-url", + "offer_uuid": "offer-uuid", + "source_model_tag": "source-model-tag", + "users": "users", + } + _toPy = { + "ApplicationOfferDetailsV5": "applicationofferdetailsv5", + "application-alias": "application_alias", + "application-description": "application_description", + "endpoints": "endpoints", + "external-controller": "external_controller", + "macaroon": "macaroon", + "offer-name": "offer_name", + "offer-url": "offer_url", + "offer-uuid": "offer_uuid", + "source-model-tag": "source_model_tag", + "users": "users", + } + + def __init__( + self, + applicationofferdetailsv5=None, + application_alias=None, + application_description=None, + endpoints=None, + external_controller=None, + macaroon=None, + offer_name=None, + offer_url=None, + offer_uuid=None, + source_model_tag=None, + users=None, + **unknown_fields, + ): + """applicationofferdetailsv5 : ApplicationOfferDetailsV5 + application_alias : str + application_description : str + endpoints : typing.Sequence[~RemoteEndpoint] + external_controller : ExternalControllerInfo + macaroon : Macaroon + offer_name : str + offer_url : str + offer_uuid : str + source_model_tag : str + users : typing.Sequence[~OfferUserDetails] + """ + applicationofferdetailsv5_ = ( + ApplicationOfferDetailsV5.from_json(applicationofferdetailsv5) + if applicationofferdetailsv5 + else None + ) + application_alias_ = application_alias + application_description_ = application_description + endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] + external_controller_ = ( + ExternalControllerInfo.from_json(external_controller) + if external_controller + else None + ) + macaroon_ = Macaroon.from_json(macaroon) if macaroon else None + offer_name_ = offer_name + offer_url_ = offer_url + offer_uuid_ = offer_uuid + source_model_tag_ = source_model_tag + users_ = [OfferUserDetails.from_json(o) for o in users or []] + + # Validate arguments against known Juju API types. + if applicationofferdetailsv5_ is not None and not isinstance( + applicationofferdetailsv5_, (dict, ApplicationOfferDetailsV5) + ): + raise Exception( + f"Expected applicationofferdetailsv5_ to be a ApplicationOfferDetailsV5, received: {type(applicationofferdetailsv5_)}" + ) + + if application_alias_ is not None and not isinstance( + application_alias_, (bytes, str) + ): + raise Exception( + f"Expected application_alias_ to be a str, received: {type(application_alias_)}" + ) + + if application_description_ is not None and not isinstance( + application_description_, (bytes, str) + ): + raise Exception( + f"Expected application_description_ to be a str, received: {type(application_description_)}" + ) + + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if external_controller_ is not None and not isinstance( + external_controller_, (dict, ExternalControllerInfo) + ): + raise Exception( + f"Expected external_controller_ to be a ExternalControllerInfo, received: {type(external_controller_)}" + ) + + if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): + raise Exception( + f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" + ) + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): + raise Exception( + f"Expected offer_url_ to be a str, received: {type(offer_url_)}" + ) + + if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): + raise Exception( + f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" + ) + + if source_model_tag_ is not None and not isinstance( + source_model_tag_, (bytes, str) + ): + raise Exception( + f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" + ) + + if users_ is not None and not isinstance(users_, (bytes, str, list)): + raise Exception( + f"Expected users_ to be a Sequence, received: {type(users_)}" + ) + + self.applicationofferdetailsv5 = applicationofferdetailsv5_ + self.application_alias = application_alias_ + self.application_description = application_description_ + self.endpoints = endpoints_ + self.external_controller = external_controller_ + self.macaroon = macaroon_ + self.offer_name = offer_name_ + self.offer_url = offer_url_ + self.offer_uuid = offer_uuid_ + self.source_model_tag = source_model_tag_ + self.users = users_ + self.unknown_fields = unknown_fields + + +class ConsumeApplicationArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~ConsumeApplicationArg]""" + args_ = [ConsumeApplicationArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class ConsumeApplicationArgsV5(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~ConsumeApplicationArgV5]""" + args_ = [ConsumeApplicationArgV5.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class ConsumeOfferDetails(Type): + _toSchema = { + "external_controller": "external-controller", + "macaroon": "macaroon", + "offer": "offer", + } + _toPy = { + "external-controller": "external_controller", + "macaroon": "macaroon", + "offer": "offer", + } + + def __init__( + self, external_controller=None, macaroon=None, offer=None, **unknown_fields + ): + """external_controller : ExternalControllerInfo + macaroon : Macaroon + offer : ApplicationOfferDetailsV5 + """ + external_controller_ = ( + ExternalControllerInfo.from_json(external_controller) + if external_controller + else None + ) + macaroon_ = Macaroon.from_json(macaroon) if macaroon else None + offer_ = ApplicationOfferDetailsV5.from_json(offer) if offer else None + + # Validate arguments against known Juju API types. + if external_controller_ is not None and not isinstance( + external_controller_, (dict, ExternalControllerInfo) + ): + raise Exception( + f"Expected external_controller_ to be a ExternalControllerInfo, received: {type(external_controller_)}" + ) + + if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): + raise Exception( + f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" + ) + + if offer_ is not None and not isinstance( + offer_, (dict, ApplicationOfferDetailsV5) + ): + raise Exception( + f"Expected offer_ to be a ApplicationOfferDetailsV5, received: {type(offer_)}" + ) + + self.external_controller = external_controller_ + self.macaroon = macaroon_ + self.offer = offer_ + self.unknown_fields = unknown_fields + + +class ConsumeOfferDetailsArg(Type): + _toSchema = {"offer_urls": "offer-urls", "user_tag": "user-tag"} + _toPy = {"offer-urls": "offer_urls", "user-tag": "user_tag"} + + def __init__(self, offer_urls=None, user_tag=None, **unknown_fields): + """offer_urls : OfferURLs + user_tag : str + """ + offer_urls_ = OfferURLs.from_json(offer_urls) if offer_urls else None + user_tag_ = user_tag + + # Validate arguments against known Juju API types. + if offer_urls_ is not None and not isinstance(offer_urls_, (dict, OfferURLs)): + raise Exception( + f"Expected offer_urls_ to be a OfferURLs, received: {type(offer_urls_)}" + ) + + if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): + raise Exception( + f"Expected user_tag_ to be a str, received: {type(user_tag_)}" + ) + + self.offer_urls = offer_urls_ + self.user_tag = user_tag_ + self.unknown_fields = unknown_fields + + +class ConsumeOfferDetailsResult(Type): + _toSchema = { + "consumeofferdetails": "ConsumeOfferDetails", + "error": "error", + "external_controller": "external-controller", + "macaroon": "macaroon", + "offer": "offer", + } + _toPy = { + "ConsumeOfferDetails": "consumeofferdetails", + "error": "error", + "external-controller": "external_controller", + "macaroon": "macaroon", + "offer": "offer", + } + + def __init__( + self, + consumeofferdetails=None, + error=None, + external_controller=None, + macaroon=None, + offer=None, + **unknown_fields, + ): + """Consumeofferdetails : ConsumeOfferDetails + error : Error + external_controller : ExternalControllerInfo + macaroon : Macaroon + offer : ApplicationOfferDetailsV5 + """ + consumeofferdetails_ = ( + ConsumeOfferDetails.from_json(consumeofferdetails) + if consumeofferdetails + else None + ) + error_ = Error.from_json(error) if error else None + external_controller_ = ( + ExternalControllerInfo.from_json(external_controller) + if external_controller + else None + ) + macaroon_ = Macaroon.from_json(macaroon) if macaroon else None + offer_ = ApplicationOfferDetailsV5.from_json(offer) if offer else None + + # Validate arguments against known Juju API types. + if consumeofferdetails_ is not None and not isinstance( + consumeofferdetails_, (dict, ConsumeOfferDetails) + ): + raise Exception( + f"Expected consumeofferdetails_ to be a ConsumeOfferDetails, received: {type(consumeofferdetails_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if external_controller_ is not None and not isinstance( + external_controller_, (dict, ExternalControllerInfo) + ): + raise Exception( + f"Expected external_controller_ to be a ExternalControllerInfo, received: {type(external_controller_)}" + ) + + if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): + raise Exception( + f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" + ) + + if offer_ is not None and not isinstance( + offer_, (dict, ApplicationOfferDetailsV5) + ): + raise Exception( + f"Expected offer_ to be a ApplicationOfferDetailsV5, received: {type(offer_)}" + ) + + self.consumeofferdetails = consumeofferdetails_ + self.error = error_ + self.external_controller = external_controller_ + self.macaroon = macaroon_ + self.offer = offer_ + self.unknown_fields = unknown_fields + + +class ConsumeOfferDetailsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ConsumeOfferDetailsResult]""" + results_ = [ConsumeOfferDetailsResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ControllerAPIInfoResult(Type): + _toSchema = {"addresses": "addresses", "cacert": "cacert", "error": "error"} + _toPy = {"addresses": "addresses", "cacert": "cacert", "error": "error"} + + def __init__(self, addresses=None, cacert=None, error=None, **unknown_fields): + """Addresses : typing.Sequence[str] + cacert : str + error : Error + """ + addresses_ = addresses + cacert_ = cacert + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if addresses_ is not None and not isinstance(addresses_, (bytes, str, list)): + raise Exception( + f"Expected addresses_ to be a Sequence, received: {type(addresses_)}" + ) + + if cacert_ is not None and not isinstance(cacert_, (bytes, str)): + raise Exception(f"Expected cacert_ to be a str, received: {type(cacert_)}") + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.addresses = addresses_ + self.cacert = cacert_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class ControllerAPIInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ControllerAPIInfoResult]""" + results_ = [ControllerAPIInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ControllerConfigResult(Type): + _toSchema = {"config": "config"} + _toPy = {"config": "config"} + + def __init__(self, config=None, **unknown_fields): + """Config : typing.Mapping[str, typing.Any]""" + config_ = config + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + self.config = config_ + self.unknown_fields = unknown_fields + + +class ControllerConfigSet(Type): + _toSchema = {"config": "config"} + _toPy = {"config": "config"} + + def __init__(self, config=None, **unknown_fields): + """Config : typing.Mapping[str, typing.Any]""" + config_ = config + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + self.config = config_ + self.unknown_fields = unknown_fields + + +class ControllerCredentialInfo(Type): + _toSchema = {"content": "content", "models": "models"} + _toPy = {"content": "content", "models": "models"} + + def __init__(self, content=None, models=None, **unknown_fields): + """Content : CredentialContent + models : typing.Sequence[~ModelAccess] + """ + content_ = CredentialContent.from_json(content) if content else None + models_ = [ModelAccess.from_json(o) for o in models or []] + + # Validate arguments against known Juju API types. + if content_ is not None and not isinstance(content_, (dict, CredentialContent)): + raise Exception( + f"Expected content_ to be a CredentialContent, received: {type(content_)}" + ) + + if models_ is not None and not isinstance(models_, (bytes, str, list)): + raise Exception( + f"Expected models_ to be a Sequence, received: {type(models_)}" + ) + + self.content = content_ + self.models = models_ + self.unknown_fields = unknown_fields + + +class ControllerVersionResults(Type): + _toSchema = {"git_commit": "git-commit", "version": "version"} + _toPy = {"git-commit": "git_commit", "version": "version"} + + def __init__(self, git_commit=None, version=None, **unknown_fields): + """git_commit : str + version : str + """ + git_commit_ = git_commit + version_ = version + + # Validate arguments against known Juju API types. + if git_commit_ is not None and not isinstance(git_commit_, (bytes, str)): + raise Exception( + f"Expected git_commit_ to be a str, received: {type(git_commit_)}" + ) + + if version_ is not None and not isinstance(version_, (bytes, str)): + raise Exception( + f"Expected version_ to be a str, received: {type(version_)}" + ) + + self.git_commit = git_commit_ + self.version = version_ + self.unknown_fields = unknown_fields + + +class ControllersChangeResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : ControllersChanges + """ + error_ = Error.from_json(error) if error else None + result_ = ControllersChanges.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, ControllersChanges)): + raise Exception( + f"Expected result_ to be a ControllersChanges, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class ControllersChangeResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ControllersChangeResult]""" + results_ = [ControllersChangeResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ControllersChanges(Type): + _toSchema = { + "added": "added", + "converted": "converted", + "maintained": "maintained", + "removed": "removed", + } + _toPy = { + "added": "added", + "converted": "converted", + "maintained": "maintained", + "removed": "removed", + } + + def __init__( + self, + added=None, + converted=None, + maintained=None, + removed=None, + **unknown_fields, + ): + """Added : typing.Sequence[str] + converted : typing.Sequence[str] + maintained : typing.Sequence[str] + removed : typing.Sequence[str] + """ + added_ = added + converted_ = converted + maintained_ = maintained + removed_ = removed + + # Validate arguments against known Juju API types. + if added_ is not None and not isinstance(added_, (bytes, str, list)): + raise Exception( + f"Expected added_ to be a Sequence, received: {type(added_)}" + ) + + if converted_ is not None and not isinstance(converted_, (bytes, str, list)): + raise Exception( + f"Expected converted_ to be a Sequence, received: {type(converted_)}" + ) + + if maintained_ is not None and not isinstance(maintained_, (bytes, str, list)): + raise Exception( + f"Expected maintained_ to be a Sequence, received: {type(maintained_)}" + ) + + if removed_ is not None and not isinstance(removed_, (bytes, str, list)): + raise Exception( + f"Expected removed_ to be a Sequence, received: {type(removed_)}" + ) + + self.added = added_ + self.converted = converted_ + self.maintained = maintained_ + self.removed = removed_ + self.unknown_fields = unknown_fields + + +class ControllersSpec(Type): + _toSchema = { + "constraints": "constraints", + "num_controllers": "num-controllers", + "placement": "placement", + } + _toPy = { + "constraints": "constraints", + "num-controllers": "num_controllers", + "placement": "placement", + } + + def __init__( + self, constraints=None, num_controllers=None, placement=None, **unknown_fields + ): + """Constraints : Value + num_controllers : int + placement : typing.Sequence[str] + """ + constraints_ = Value.from_json(constraints) if constraints else None + num_controllers_ = num_controllers + placement_ = placement + + # Validate arguments against known Juju API types. + if constraints_ is not None and not isinstance(constraints_, (dict, Value)): + raise Exception( + f"Expected constraints_ to be a Value, received: {type(constraints_)}" + ) + + if num_controllers_ is not None and not isinstance(num_controllers_, int): + raise Exception( + f"Expected num_controllers_ to be a int, received: {type(num_controllers_)}" + ) + + if placement_ is not None and not isinstance(placement_, (bytes, str, list)): + raise Exception( + f"Expected placement_ to be a Sequence, received: {type(placement_)}" + ) + + self.constraints = constraints_ + self.num_controllers = num_controllers_ + self.placement = placement_ + self.unknown_fields = unknown_fields + + +class ControllersSpecs(Type): + _toSchema = {"specs": "specs"} + _toPy = {"specs": "specs"} + + def __init__(self, specs=None, **unknown_fields): + """Specs : typing.Sequence[~ControllersSpec]""" + specs_ = [ControllersSpec.from_json(o) for o in specs or []] + + # Validate arguments against known Juju API types. + if specs_ is not None and not isinstance(specs_, (bytes, str, list)): + raise Exception( + f"Expected specs_ to be a Sequence, received: {type(specs_)}" + ) + + self.specs = specs_ + self.unknown_fields = unknown_fields + + +class CreateSecretArg(Type): + _toSchema = { + "content": "content", + "description": "description", + "expire_time": "expire-time", + "label": "label", + "owner_tag": "owner-tag", + "params": "params", + "rotate_policy": "rotate-policy", + "upsertsecretarg": "UpsertSecretArg", + "uri": "uri", + } + _toPy = { + "UpsertSecretArg": "upsertsecretarg", + "content": "content", + "description": "description", + "expire-time": "expire_time", + "label": "label", + "owner-tag": "owner_tag", + "params": "params", + "rotate-policy": "rotate_policy", + "uri": "uri", + } + + def __init__( + self, + upsertsecretarg=None, + content=None, + description=None, + expire_time=None, + label=None, + owner_tag=None, + params=None, + rotate_policy=None, + uri=None, + **unknown_fields, + ): + """Upsertsecretarg : UpsertSecretArg + content : SecretContentParams + description : str + expire_time : str + label : str + owner_tag : str + params : typing.Mapping[str, typing.Any] + rotate_policy : str + uri : str + """ + upsertsecretarg_ = ( + UpsertSecretArg.from_json(upsertsecretarg) if upsertsecretarg else None + ) + content_ = SecretContentParams.from_json(content) if content else None + description_ = description + expire_time_ = expire_time + label_ = label + owner_tag_ = owner_tag + params_ = params + rotate_policy_ = rotate_policy + uri_ = uri + + # Validate arguments against known Juju API types. + if upsertsecretarg_ is not None and not isinstance( + upsertsecretarg_, (dict, UpsertSecretArg) + ): + raise Exception( + f"Expected upsertsecretarg_ to be a UpsertSecretArg, received: {type(upsertsecretarg_)}" + ) + + if content_ is not None and not isinstance( + content_, (dict, SecretContentParams) + ): + raise Exception( + f"Expected content_ to be a SecretContentParams, received: {type(content_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if expire_time_ is not None and not isinstance(expire_time_, (bytes, str)): + raise Exception( + f"Expected expire_time_ to be a str, received: {type(expire_time_)}" + ) + + if label_ is not None and not isinstance(label_, (bytes, str)): + raise Exception(f"Expected label_ to be a str, received: {type(label_)}") + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + if params_ is not None and not isinstance(params_, dict): + raise Exception( + f"Expected params_ to be a Mapping, received: {type(params_)}" + ) + + if rotate_policy_ is not None and not isinstance(rotate_policy_, (bytes, str)): + raise Exception( + f"Expected rotate_policy_ to be a str, received: {type(rotate_policy_)}" + ) + + if uri_ is not None and not isinstance(uri_, (bytes, str)): + raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") + + self.upsertsecretarg = upsertsecretarg_ + self.content = content_ + self.description = description_ + self.expire_time = expire_time_ + self.label = label_ + self.owner_tag = owner_tag_ + self.params = params_ + self.rotate_policy = rotate_policy_ + self.uri = uri_ + self.unknown_fields = unknown_fields + + +class CreateSecretArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~CreateSecretArg]""" + args_ = [CreateSecretArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class CreateSpaceParams(Type): + _toSchema = { + "cidrs": "cidrs", + "provider_id": "provider-id", + "public": "public", + "space_tag": "space-tag", + } + _toPy = { + "cidrs": "cidrs", + "provider-id": "provider_id", + "public": "public", + "space-tag": "space_tag", + } + + def __init__( + self, + cidrs=None, + provider_id=None, + public=None, + space_tag=None, + **unknown_fields, + ): + """Cidrs : typing.Sequence[str] + provider_id : str + public : bool + space_tag : str + """ + cidrs_ = cidrs + provider_id_ = provider_id + public_ = public + space_tag_ = space_tag + + # Validate arguments against known Juju API types. + if cidrs_ is not None and not isinstance(cidrs_, (bytes, str, list)): + raise Exception( + f"Expected cidrs_ to be a Sequence, received: {type(cidrs_)}" + ) + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if public_ is not None and not isinstance(public_, bool): + raise Exception(f"Expected public_ to be a bool, received: {type(public_)}") + + if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): + raise Exception( + f"Expected space_tag_ to be a str, received: {type(space_tag_)}" + ) + + self.cidrs = cidrs_ + self.provider_id = provider_id_ + self.public = public_ + self.space_tag = space_tag_ + self.unknown_fields = unknown_fields + + +class CreateSpacesParams(Type): + _toSchema = {"spaces": "spaces"} + _toPy = {"spaces": "spaces"} + + def __init__(self, spaces=None, **unknown_fields): + """Spaces : typing.Sequence[~CreateSpaceParams]""" + spaces_ = [CreateSpaceParams.from_json(o) for o in spaces or []] + + # Validate arguments against known Juju API types. + if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): + raise Exception( + f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" + ) + + self.spaces = spaces_ + self.unknown_fields = unknown_fields + + +class CredentialContent(Type): + _toSchema = { + "attrs": "attrs", + "auth_type": "auth-type", + "cloud": "cloud", + "name": "name", + "valid": "valid", + } + _toPy = { + "attrs": "attrs", + "auth-type": "auth_type", + "cloud": "cloud", + "name": "name", + "valid": "valid", + } + + def __init__( + self, + attrs=None, + auth_type=None, + cloud=None, + name=None, + valid=None, + **unknown_fields, + ): + """Attrs : typing.Mapping[str, str] + auth_type : str + cloud : str + name : str + valid : bool + """ + attrs_ = attrs + auth_type_ = auth_type + cloud_ = cloud + name_ = name + valid_ = valid + + # Validate arguments against known Juju API types. + if attrs_ is not None and not isinstance(attrs_, dict): + raise Exception( + f"Expected attrs_ to be a Mapping, received: {type(attrs_)}" + ) + + if auth_type_ is not None and not isinstance(auth_type_, (bytes, str)): + raise Exception( + f"Expected auth_type_ to be a str, received: {type(auth_type_)}" + ) + + if cloud_ is not None and not isinstance(cloud_, (bytes, str)): + raise Exception(f"Expected cloud_ to be a str, received: {type(cloud_)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if valid_ is not None and not isinstance(valid_, bool): + raise Exception(f"Expected valid_ to be a bool, received: {type(valid_)}") + + self.attrs = attrs_ + self.auth_type = auth_type_ + self.cloud = cloud_ + self.name = name_ + self.valid = valid_ + self.unknown_fields = unknown_fields + + +class CredentialContentResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : ControllerCredentialInfo + """ + error_ = Error.from_json(error) if error else None + result_ = ControllerCredentialInfo.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance( + result_, (dict, ControllerCredentialInfo) + ): + raise Exception( + f"Expected result_ to be a ControllerCredentialInfo, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class CredentialContentResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~CredentialContentResult]""" + results_ = [CredentialContentResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class DashboardConnectionInfo(Type): + _toSchema = { + "error": "error", + "proxy_connection": "proxy-connection", + "ssh_connection": "ssh-connection", + } + _toPy = { + "error": "error", + "proxy-connection": "proxy_connection", + "ssh-connection": "ssh_connection", + } + + def __init__( + self, error=None, proxy_connection=None, ssh_connection=None, **unknown_fields + ): + """Error : Error + proxy_connection : Proxy + ssh_connection : DashboardConnectionSSHTunnel + """ + error_ = Error.from_json(error) if error else None + proxy_connection_ = ( + Proxy.from_json(proxy_connection) if proxy_connection else None + ) + ssh_connection_ = ( + DashboardConnectionSSHTunnel.from_json(ssh_connection) + if ssh_connection + else None + ) + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if proxy_connection_ is not None and not isinstance( + proxy_connection_, (dict, Proxy) + ): + raise Exception( + f"Expected proxy_connection_ to be a Proxy, received: {type(proxy_connection_)}" + ) + + if ssh_connection_ is not None and not isinstance( + ssh_connection_, (dict, DashboardConnectionSSHTunnel) + ): + raise Exception( + f"Expected ssh_connection_ to be a DashboardConnectionSSHTunnel, received: {type(ssh_connection_)}" + ) + + self.error = error_ + self.proxy_connection = proxy_connection_ + self.ssh_connection = ssh_connection_ + self.unknown_fields = unknown_fields + + +class DashboardConnectionSSHTunnel(Type): + _toSchema = {"entity": "entity", "host": "host", "model": "model", "port": "port"} + _toPy = {"entity": "entity", "host": "host", "model": "model", "port": "port"} + + def __init__(self, entity=None, host=None, model=None, port=None, **unknown_fields): + """Entity : str + host : str + model : str + port : str + """ + entity_ = entity + host_ = host + model_ = model + port_ = port + + # Validate arguments against known Juju API types. + if entity_ is not None and not isinstance(entity_, (bytes, str)): + raise Exception(f"Expected entity_ to be a str, received: {type(entity_)}") + + if host_ is not None and not isinstance(host_, (bytes, str)): + raise Exception(f"Expected host_ to be a str, received: {type(host_)}") + + if model_ is not None and not isinstance(model_, (bytes, str)): + raise Exception(f"Expected model_ to be a str, received: {type(model_)}") + + if port_ is not None and not isinstance(port_, (bytes, str)): + raise Exception(f"Expected port_ to be a str, received: {type(port_)}") + + self.entity = entity_ + self.host = host_ + self.model = model_ + self.port = port_ + self.unknown_fields = unknown_fields + + +class DeleteSecretArg(Type): + _toSchema = {"label": "label", "revisions": "revisions", "uri": "uri"} + _toPy = {"label": "label", "revisions": "revisions", "uri": "uri"} + + def __init__(self, label=None, revisions=None, uri=None, **unknown_fields): + """Label : str + revisions : typing.Sequence[int] + uri : str + """ + label_ = label + revisions_ = revisions + uri_ = uri + + # Validate arguments against known Juju API types. + if label_ is not None and not isinstance(label_, (bytes, str)): + raise Exception(f"Expected label_ to be a str, received: {type(label_)}") + + if revisions_ is not None and not isinstance(revisions_, (bytes, str, list)): + raise Exception( + f"Expected revisions_ to be a Sequence, received: {type(revisions_)}" + ) + + if uri_ is not None and not isinstance(uri_, (bytes, str)): + raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") + + self.label = label_ + self.revisions = revisions_ + self.uri = uri_ + self.unknown_fields = unknown_fields + + +class DeleteSecretArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~DeleteSecretArg]""" + args_ = [DeleteSecretArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class Delta(Type): + _toSchema = {"entity": "entity", "removed": "removed"} + _toPy = {"entity": "entity", "removed": "removed"} + + def __init__(self, entity=None, removed=None, **unknown_fields): + """Entity : Any + removed : bool + """ + entity_ = entity + removed_ = removed + + # Validate arguments against known Juju API types. + if removed_ is not None and not isinstance(removed_, bool): + raise Exception( + f"Expected removed_ to be a bool, received: {type(removed_)}" + ) + + self.entity = entity_ + self.removed = removed_ + self.unknown_fields = unknown_fields + + +class DeployFromRepositoryArg(Type): + _toSchema = { + "applicationname": "ApplicationName", + "attachstorage": "AttachStorage", + "base": "base", + "channel": "channel", + "charmname": "CharmName", + "configyaml": "ConfigYAML", + "cons": "Cons", + "devices": "Devices", + "dryrun": "DryRun", + "endpoint_bindings": "endpoint-bindings", + "force": "force", + "num_units": "num-units", + "placement": "Placement", + "resources": "resources", + "revision": "revision", + "storage": "Storage", + "trust": "Trust", + } + _toPy = { + "ApplicationName": "applicationname", + "AttachStorage": "attachstorage", + "CharmName": "charmname", + "ConfigYAML": "configyaml", + "Cons": "cons", + "Devices": "devices", + "DryRun": "dryrun", + "Placement": "placement", + "Storage": "storage", + "Trust": "trust", + "base": "base", + "channel": "channel", + "endpoint-bindings": "endpoint_bindings", + "force": "force", + "num-units": "num_units", + "resources": "resources", + "revision": "revision", + } + + def __init__( + self, + applicationname=None, + attachstorage=None, + charmname=None, + configyaml=None, + cons=None, + devices=None, + dryrun=None, + placement=None, + storage=None, + trust=None, + base=None, + channel=None, + endpoint_bindings=None, + force=None, + num_units=None, + resources=None, + revision=None, + **unknown_fields, + ): + """Applicationname : str + attachstorage : typing.Sequence[str] + charmname : str + configyaml : str + cons : Value + devices : typing.Mapping[str, ~Constraints] + dryrun : bool + placement : typing.Sequence[~Placement] + storage : typing.Mapping[str, ~Constraints] + trust : bool + base : Base + channel : str + endpoint_bindings : typing.Mapping[str, str] + force : bool + num_units : int + resources : typing.Mapping[str, str] + revision : int + """ + applicationname_ = applicationname + attachstorage_ = attachstorage + charmname_ = charmname + configyaml_ = configyaml + cons_ = Value.from_json(cons) if cons else None + devices_ = {k: Constraints.from_json(v) for k, v in (devices or dict()).items()} + dryrun_ = dryrun + placement_ = [Placement.from_json(o) for o in placement or []] + storage_ = {k: Constraints.from_json(v) for k, v in (storage or dict()).items()} + trust_ = trust + base_ = Base.from_json(base) if base else None + channel_ = channel + endpoint_bindings_ = endpoint_bindings + force_ = force + num_units_ = num_units + resources_ = resources + revision_ = revision + + # Validate arguments against known Juju API types. + if applicationname_ is not None and not isinstance( + applicationname_, (bytes, str) + ): + raise Exception( + f"Expected applicationname_ to be a str, received: {type(applicationname_)}" + ) + + if attachstorage_ is not None and not isinstance( + attachstorage_, (bytes, str, list) + ): + raise Exception( + f"Expected attachstorage_ to be a Sequence, received: {type(attachstorage_)}" + ) + + if charmname_ is not None and not isinstance(charmname_, (bytes, str)): + raise Exception( + f"Expected charmname_ to be a str, received: {type(charmname_)}" + ) + + if configyaml_ is not None and not isinstance(configyaml_, (bytes, str)): + raise Exception( + f"Expected configyaml_ to be a str, received: {type(configyaml_)}" + ) + + if cons_ is not None and not isinstance(cons_, (dict, Value)): + raise Exception(f"Expected cons_ to be a Value, received: {type(cons_)}") + + if devices_ is not None and not isinstance(devices_, dict): + raise Exception( + f"Expected devices_ to be a Mapping, received: {type(devices_)}" + ) + + if dryrun_ is not None and not isinstance(dryrun_, bool): + raise Exception(f"Expected dryrun_ to be a bool, received: {type(dryrun_)}") + + if placement_ is not None and not isinstance(placement_, (bytes, str, list)): + raise Exception( + f"Expected placement_ to be a Sequence, received: {type(placement_)}" + ) + + if storage_ is not None and not isinstance(storage_, dict): + raise Exception( + f"Expected storage_ to be a Mapping, received: {type(storage_)}" + ) + + if trust_ is not None and not isinstance(trust_, bool): + raise Exception(f"Expected trust_ to be a bool, received: {type(trust_)}") + + if base_ is not None and not isinstance(base_, (dict, Base)): + raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") + + if channel_ is not None and not isinstance(channel_, (bytes, str)): + raise Exception( + f"Expected channel_ to be a str, received: {type(channel_)}" + ) + + if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): + raise Exception( + f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if num_units_ is not None and not isinstance(num_units_, int): + raise Exception( + f"Expected num_units_ to be a int, received: {type(num_units_)}" + ) + + if resources_ is not None and not isinstance(resources_, dict): + raise Exception( + f"Expected resources_ to be a Mapping, received: {type(resources_)}" + ) + + if revision_ is not None and not isinstance(revision_, int): + raise Exception( + f"Expected revision_ to be a int, received: {type(revision_)}" + ) + + self.applicationname = applicationname_ + self.attachstorage = attachstorage_ + self.charmname = charmname_ + self.configyaml = configyaml_ + self.cons = cons_ + self.devices = devices_ + self.dryrun = dryrun_ + self.placement = placement_ + self.storage = storage_ + self.trust = trust_ + self.base = base_ + self.channel = channel_ + self.endpoint_bindings = endpoint_bindings_ + self.force = force_ + self.num_units = num_units_ + self.resources = resources_ + self.revision = revision_ + self.unknown_fields = unknown_fields + + +class DeployFromRepositoryArgs(Type): + _toSchema = {"args": "Args"} + _toPy = {"Args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~DeployFromRepositoryArg]""" + args_ = [DeployFromRepositoryArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class DeployFromRepositoryInfo(Type): + _toSchema = { + "architecture": "architecture", + "base": "base", + "channel": "channel", + "effective_channel": "effective-channel", + "name": "name", + "revision": "revision", + } + _toPy = { + "architecture": "architecture", + "base": "base", + "channel": "channel", + "effective-channel": "effective_channel", + "name": "name", + "revision": "revision", + } + + def __init__( + self, + architecture=None, + base=None, + channel=None, + effective_channel=None, + name=None, + revision=None, + **unknown_fields, + ): + """Architecture : str + base : Base + channel : str + effective_channel : str + name : str + revision : int + """ + architecture_ = architecture + base_ = Base.from_json(base) if base else None + channel_ = channel + effective_channel_ = effective_channel + name_ = name + revision_ = revision + + # Validate arguments against known Juju API types. + if architecture_ is not None and not isinstance(architecture_, (bytes, str)): + raise Exception( + f"Expected architecture_ to be a str, received: {type(architecture_)}" + ) + + if base_ is not None and not isinstance(base_, (dict, Base)): + raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") + + if channel_ is not None and not isinstance(channel_, (bytes, str)): + raise Exception( + f"Expected channel_ to be a str, received: {type(channel_)}" + ) + + if effective_channel_ is not None and not isinstance( + effective_channel_, (bytes, str) + ): + raise Exception( + f"Expected effective_channel_ to be a str, received: {type(effective_channel_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if revision_ is not None and not isinstance(revision_, int): + raise Exception( + f"Expected revision_ to be a int, received: {type(revision_)}" + ) + + self.architecture = architecture_ + self.base = base_ + self.channel = channel_ + self.effective_channel = effective_channel_ + self.name = name_ + self.revision = revision_ + self.unknown_fields = unknown_fields + + +class DeployFromRepositoryResult(Type): + _toSchema = { + "errors": "Errors", + "info": "Info", + "pendingresourceuploads": "PendingResourceUploads", + } + _toPy = { + "Errors": "errors", + "Info": "info", + "PendingResourceUploads": "pendingresourceuploads", + } + + def __init__( + self, errors=None, info=None, pendingresourceuploads=None, **unknown_fields + ): + """Errors : typing.Sequence[~Error] + info : DeployFromRepositoryInfo + pendingresourceuploads : typing.Sequence[~PendingResourceUpload] + """ + errors_ = [Error.from_json(o) for o in errors or []] + info_ = DeployFromRepositoryInfo.from_json(info) if info else None + pendingresourceuploads_ = [ + PendingResourceUpload.from_json(o) for o in pendingresourceuploads or [] + ] + + # Validate arguments against known Juju API types. + if errors_ is not None and not isinstance(errors_, (bytes, str, list)): + raise Exception( + f"Expected errors_ to be a Sequence, received: {type(errors_)}" + ) + + if info_ is not None and not isinstance( + info_, (dict, DeployFromRepositoryInfo) + ): + raise Exception( + f"Expected info_ to be a DeployFromRepositoryInfo, received: {type(info_)}" + ) + + if pendingresourceuploads_ is not None and not isinstance( + pendingresourceuploads_, (bytes, str, list) + ): + raise Exception( + f"Expected pendingresourceuploads_ to be a Sequence, received: {type(pendingresourceuploads_)}" + ) + + self.errors = errors_ + self.info = info_ + self.pendingresourceuploads = pendingresourceuploads_ + self.unknown_fields = unknown_fields + + +class DeployFromRepositoryResults(Type): + _toSchema = {"results": "Results"} + _toPy = {"Results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~DeployFromRepositoryResult]""" + results_ = [DeployFromRepositoryResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class DestroyApplicationInfo(Type): + _toSchema = { + "destroyed_storage": "destroyed-storage", + "destroyed_units": "destroyed-units", + "detached_storage": "detached-storage", + } + _toPy = { + "destroyed-storage": "destroyed_storage", + "destroyed-units": "destroyed_units", + "detached-storage": "detached_storage", + } + + def __init__( + self, + destroyed_storage=None, + destroyed_units=None, + detached_storage=None, + **unknown_fields, + ): + """destroyed_storage : typing.Sequence[~Entity] + destroyed_units : typing.Sequence[~Entity] + detached_storage : typing.Sequence[~Entity] + """ + destroyed_storage_ = [Entity.from_json(o) for o in destroyed_storage or []] + destroyed_units_ = [Entity.from_json(o) for o in destroyed_units or []] + detached_storage_ = [Entity.from_json(o) for o in detached_storage or []] + + # Validate arguments against known Juju API types. + if destroyed_storage_ is not None and not isinstance( + destroyed_storage_, (bytes, str, list) + ): + raise Exception( + f"Expected destroyed_storage_ to be a Sequence, received: {type(destroyed_storage_)}" + ) + + if destroyed_units_ is not None and not isinstance( + destroyed_units_, (bytes, str, list) + ): + raise Exception( + f"Expected destroyed_units_ to be a Sequence, received: {type(destroyed_units_)}" + ) + + if detached_storage_ is not None and not isinstance( + detached_storage_, (bytes, str, list) + ): + raise Exception( + f"Expected detached_storage_ to be a Sequence, received: {type(detached_storage_)}" + ) + + self.destroyed_storage = destroyed_storage_ + self.destroyed_units = destroyed_units_ + self.detached_storage = detached_storage_ + self.unknown_fields = unknown_fields + + +class DestroyApplicationOffers(Type): + _toSchema = {"force": "force", "offer_urls": "offer-urls"} + _toPy = {"force": "force", "offer-urls": "offer_urls"} + + def __init__(self, force=None, offer_urls=None, **unknown_fields): + """Force : bool + offer_urls : typing.Sequence[str] + """ + force_ = force + offer_urls_ = offer_urls + + # Validate arguments against known Juju API types. + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if offer_urls_ is not None and not isinstance(offer_urls_, (bytes, str, list)): + raise Exception( + f"Expected offer_urls_ to be a Sequence, received: {type(offer_urls_)}" + ) + + self.force = force_ + self.offer_urls = offer_urls_ + self.unknown_fields = unknown_fields + + +class DestroyApplicationParams(Type): + _toSchema = { + "application_tag": "application-tag", + "destroy_storage": "destroy-storage", + "dry_run": "dry-run", + "force": "force", + "max_wait": "max-wait", + } + _toPy = { + "application-tag": "application_tag", + "destroy-storage": "destroy_storage", + "dry-run": "dry_run", + "force": "force", + "max-wait": "max_wait", + } + + def __init__( + self, + application_tag=None, + destroy_storage=None, + dry_run=None, + force=None, + max_wait=None, + **unknown_fields, + ): + """application_tag : str + destroy_storage : bool + dry_run : bool + force : bool + max_wait : int + """ + application_tag_ = application_tag + destroy_storage_ = destroy_storage + dry_run_ = dry_run + force_ = force + max_wait_ = max_wait + + # Validate arguments against known Juju API types. + if application_tag_ is not None and not isinstance( + application_tag_, (bytes, str) + ): + raise Exception( + f"Expected application_tag_ to be a str, received: {type(application_tag_)}" + ) + + if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): + raise Exception( + f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" + ) + + if dry_run_ is not None and not isinstance(dry_run_, bool): + raise Exception( + f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if max_wait_ is not None and not isinstance(max_wait_, int): + raise Exception( + f"Expected max_wait_ to be a int, received: {type(max_wait_)}" + ) + + self.application_tag = application_tag_ + self.destroy_storage = destroy_storage_ + self.dry_run = dry_run_ + self.force = force_ + self.max_wait = max_wait_ + self.unknown_fields = unknown_fields + + +class DestroyApplicationResult(Type): + _toSchema = {"error": "error", "info": "info"} + _toPy = {"error": "error", "info": "info"} + + def __init__(self, error=None, info=None, **unknown_fields): + """Error : Error + info : DestroyApplicationInfo + """ + error_ = Error.from_json(error) if error else None + info_ = DestroyApplicationInfo.from_json(info) if info else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if info_ is not None and not isinstance(info_, (dict, DestroyApplicationInfo)): + raise Exception( + f"Expected info_ to be a DestroyApplicationInfo, received: {type(info_)}" + ) + + self.error = error_ + self.info = info_ + self.unknown_fields = unknown_fields + + +class DestroyApplicationResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~DestroyApplicationResult]""" + results_ = [DestroyApplicationResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class DestroyApplicationsParams(Type): + _toSchema = {"applications": "applications"} + _toPy = {"applications": "applications"} + + def __init__(self, applications=None, **unknown_fields): + """Applications : typing.Sequence[~DestroyApplicationParams]""" + applications_ = [ + DestroyApplicationParams.from_json(o) for o in applications or [] + ] + + # Validate arguments against known Juju API types. + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + self.applications = applications_ + self.unknown_fields = unknown_fields + + +class DestroyConsumedApplicationParams(Type): + _toSchema = { + "application_tag": "application-tag", + "force": "force", + "max_wait": "max-wait", + } + _toPy = { + "application-tag": "application_tag", + "force": "force", + "max-wait": "max_wait", + } + + def __init__( + self, application_tag=None, force=None, max_wait=None, **unknown_fields + ): + """application_tag : str + force : bool + max_wait : int + """ + application_tag_ = application_tag + force_ = force + max_wait_ = max_wait + + # Validate arguments against known Juju API types. + if application_tag_ is not None and not isinstance( + application_tag_, (bytes, str) + ): + raise Exception( + f"Expected application_tag_ to be a str, received: {type(application_tag_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if max_wait_ is not None and not isinstance(max_wait_, int): + raise Exception( + f"Expected max_wait_ to be a int, received: {type(max_wait_)}" + ) + + self.application_tag = application_tag_ + self.force = force_ + self.max_wait = max_wait_ + self.unknown_fields = unknown_fields + + +class DestroyConsumedApplicationsParams(Type): + _toSchema = {"applications": "applications"} + _toPy = {"applications": "applications"} + + def __init__(self, applications=None, **unknown_fields): + """Applications : typing.Sequence[~DestroyConsumedApplicationParams]""" + applications_ = [ + DestroyConsumedApplicationParams.from_json(o) for o in applications or [] + ] + + # Validate arguments against known Juju API types. + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + self.applications = applications_ + self.unknown_fields = unknown_fields + + +class DestroyControllerArgs(Type): + _toSchema = { + "destroy_models": "destroy-models", + "destroy_storage": "destroy-storage", + "force": "force", + "max_wait": "max-wait", + "model_timeout": "model-timeout", + } + _toPy = { + "destroy-models": "destroy_models", + "destroy-storage": "destroy_storage", + "force": "force", + "max-wait": "max_wait", + "model-timeout": "model_timeout", + } + + def __init__( + self, + destroy_models=None, + destroy_storage=None, + force=None, + max_wait=None, + model_timeout=None, + **unknown_fields, + ): + """destroy_models : bool + destroy_storage : bool + force : bool + max_wait : int + model_timeout : int + """ + destroy_models_ = destroy_models + destroy_storage_ = destroy_storage + force_ = force + max_wait_ = max_wait + model_timeout_ = model_timeout + + # Validate arguments against known Juju API types. + if destroy_models_ is not None and not isinstance(destroy_models_, bool): + raise Exception( + f"Expected destroy_models_ to be a bool, received: {type(destroy_models_)}" + ) + + if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): + raise Exception( + f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if max_wait_ is not None and not isinstance(max_wait_, int): + raise Exception( + f"Expected max_wait_ to be a int, received: {type(max_wait_)}" + ) + + if model_timeout_ is not None and not isinstance(model_timeout_, int): + raise Exception( + f"Expected model_timeout_ to be a int, received: {type(model_timeout_)}" + ) + + self.destroy_models = destroy_models_ + self.destroy_storage = destroy_storage_ + self.force = force_ + self.max_wait = max_wait_ + self.model_timeout = model_timeout_ + self.unknown_fields = unknown_fields + + +class DestroyMachineInfo(Type): + _toSchema = { + "destroyed_containers": "destroyed-containers", + "destroyed_storage": "destroyed-storage", + "destroyed_units": "destroyed-units", + "detached_storage": "detached-storage", + "machine_id": "machine-id", + } + _toPy = { + "destroyed-containers": "destroyed_containers", + "destroyed-storage": "destroyed_storage", + "destroyed-units": "destroyed_units", + "detached-storage": "detached_storage", + "machine-id": "machine_id", + } + + def __init__( + self, + destroyed_containers=None, + destroyed_storage=None, + destroyed_units=None, + detached_storage=None, + machine_id=None, + **unknown_fields, + ): + """destroyed_containers : typing.Sequence[~DestroyMachineResult] + destroyed_storage : typing.Sequence[~Entity] + destroyed_units : typing.Sequence[~Entity] + detached_storage : typing.Sequence[~Entity] + machine_id : str + """ + destroyed_containers_ = [ + DestroyMachineResult.from_json(o) for o in destroyed_containers or [] + ] + destroyed_storage_ = [Entity.from_json(o) for o in destroyed_storage or []] + destroyed_units_ = [Entity.from_json(o) for o in destroyed_units or []] + detached_storage_ = [Entity.from_json(o) for o in detached_storage or []] + machine_id_ = machine_id + + # Validate arguments against known Juju API types. + if destroyed_containers_ is not None and not isinstance( + destroyed_containers_, (bytes, str, list) + ): + raise Exception( + f"Expected destroyed_containers_ to be a Sequence, received: {type(destroyed_containers_)}" + ) + + if destroyed_storage_ is not None and not isinstance( + destroyed_storage_, (bytes, str, list) + ): + raise Exception( + f"Expected destroyed_storage_ to be a Sequence, received: {type(destroyed_storage_)}" + ) + + if destroyed_units_ is not None and not isinstance( + destroyed_units_, (bytes, str, list) + ): + raise Exception( + f"Expected destroyed_units_ to be a Sequence, received: {type(destroyed_units_)}" + ) + + if detached_storage_ is not None and not isinstance( + detached_storage_, (bytes, str, list) + ): + raise Exception( + f"Expected detached_storage_ to be a Sequence, received: {type(detached_storage_)}" + ) + + if machine_id_ is not None and not isinstance(machine_id_, (bytes, str)): + raise Exception( + f"Expected machine_id_ to be a str, received: {type(machine_id_)}" + ) + + self.destroyed_containers = destroyed_containers_ + self.destroyed_storage = destroyed_storage_ + self.destroyed_units = destroyed_units_ + self.detached_storage = detached_storage_ + self.machine_id = machine_id_ + self.unknown_fields = unknown_fields + + +class DestroyMachineResult(Type): + _toSchema = {"error": "error", "info": "info"} + _toPy = {"error": "error", "info": "info"} + + def __init__(self, error=None, info=None, **unknown_fields): + """Error : Error + info : DestroyMachineInfo + """ + error_ = Error.from_json(error) if error else None + info_ = DestroyMachineInfo.from_json(info) if info else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if info_ is not None and not isinstance(info_, (dict, DestroyMachineInfo)): + raise Exception( + f"Expected info_ to be a DestroyMachineInfo, received: {type(info_)}" + ) + + self.error = error_ + self.info = info_ + self.unknown_fields = unknown_fields + + +class DestroyMachineResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~DestroyMachineResult]""" + results_ = [DestroyMachineResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class DestroyMachinesParams(Type): + _toSchema = { + "dry_run": "dry-run", + "force": "force", + "keep": "keep", + "machine_tags": "machine-tags", + "max_wait": "max-wait", + } + _toPy = { + "dry-run": "dry_run", + "force": "force", + "keep": "keep", + "machine-tags": "machine_tags", + "max-wait": "max_wait", + } + + def __init__( + self, + dry_run=None, + force=None, + keep=None, + machine_tags=None, + max_wait=None, + **unknown_fields, + ): + """dry_run : bool + force : bool + keep : bool + machine_tags : typing.Sequence[str] + max_wait : int + """ + dry_run_ = dry_run + force_ = force + keep_ = keep + machine_tags_ = machine_tags + max_wait_ = max_wait + + # Validate arguments against known Juju API types. + if dry_run_ is not None and not isinstance(dry_run_, bool): + raise Exception( + f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if keep_ is not None and not isinstance(keep_, bool): + raise Exception(f"Expected keep_ to be a bool, received: {type(keep_)}") + + if machine_tags_ is not None and not isinstance( + machine_tags_, (bytes, str, list) + ): + raise Exception( + f"Expected machine_tags_ to be a Sequence, received: {type(machine_tags_)}" + ) + + if max_wait_ is not None and not isinstance(max_wait_, int): + raise Exception( + f"Expected max_wait_ to be a int, received: {type(max_wait_)}" + ) + + self.dry_run = dry_run_ + self.force = force_ + self.keep = keep_ + self.machine_tags = machine_tags_ + self.max_wait = max_wait_ + self.unknown_fields = unknown_fields + + +class DestroyModelParams(Type): + _toSchema = { + "destroy_storage": "destroy-storage", + "force": "force", + "max_wait": "max-wait", + "model_tag": "model-tag", + "timeout": "timeout", + } + _toPy = { + "destroy-storage": "destroy_storage", + "force": "force", + "max-wait": "max_wait", + "model-tag": "model_tag", + "timeout": "timeout", + } + + def __init__( + self, + destroy_storage=None, + force=None, + max_wait=None, + model_tag=None, + timeout=None, + **unknown_fields, + ): + """destroy_storage : bool + force : bool + max_wait : int + model_tag : str + timeout : int + """ + destroy_storage_ = destroy_storage + force_ = force + max_wait_ = max_wait + model_tag_ = model_tag + timeout_ = timeout + + # Validate arguments against known Juju API types. + if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): + raise Exception( + f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if max_wait_ is not None and not isinstance(max_wait_, int): + raise Exception( + f"Expected max_wait_ to be a int, received: {type(max_wait_)}" + ) + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + if timeout_ is not None and not isinstance(timeout_, int): + raise Exception( + f"Expected timeout_ to be a int, received: {type(timeout_)}" + ) + + self.destroy_storage = destroy_storage_ + self.force = force_ + self.max_wait = max_wait_ + self.model_tag = model_tag_ + self.timeout = timeout_ + self.unknown_fields = unknown_fields + + +class DestroyModelsParams(Type): + _toSchema = {"models": "models"} + _toPy = {"models": "models"} + + def __init__(self, models=None, **unknown_fields): + """Models : typing.Sequence[~DestroyModelParams]""" + models_ = [DestroyModelParams.from_json(o) for o in models or []] + + # Validate arguments against known Juju API types. + if models_ is not None and not isinstance(models_, (bytes, str, list)): + raise Exception( + f"Expected models_ to be a Sequence, received: {type(models_)}" + ) + + self.models = models_ + self.unknown_fields = unknown_fields + + +class DestroyRelation(Type): + _toSchema = { + "endpoints": "endpoints", + "force": "force", + "max_wait": "max-wait", + "relation_id": "relation-id", + } + _toPy = { + "endpoints": "endpoints", + "force": "force", + "max-wait": "max_wait", + "relation-id": "relation_id", + } + + def __init__( + self, + endpoints=None, + force=None, + max_wait=None, + relation_id=None, + **unknown_fields, + ): + """Endpoints : typing.Sequence[str] + force : bool + max_wait : int + relation_id : int + """ + endpoints_ = endpoints + force_ = force + max_wait_ = max_wait + relation_id_ = relation_id + + # Validate arguments against known Juju API types. + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if max_wait_ is not None and not isinstance(max_wait_, int): + raise Exception( + f"Expected max_wait_ to be a int, received: {type(max_wait_)}" + ) + + if relation_id_ is not None and not isinstance(relation_id_, int): + raise Exception( + f"Expected relation_id_ to be a int, received: {type(relation_id_)}" + ) + + self.endpoints = endpoints_ + self.force = force_ + self.max_wait = max_wait_ + self.relation_id = relation_id_ + self.unknown_fields = unknown_fields + + +class DestroyUnitInfo(Type): + _toSchema = { + "destroyed_storage": "destroyed-storage", + "detached_storage": "detached-storage", + } + _toPy = { + "destroyed-storage": "destroyed_storage", + "detached-storage": "detached_storage", + } + + def __init__(self, destroyed_storage=None, detached_storage=None, **unknown_fields): + """destroyed_storage : typing.Sequence[~Entity] + detached_storage : typing.Sequence[~Entity] + """ + destroyed_storage_ = [Entity.from_json(o) for o in destroyed_storage or []] + detached_storage_ = [Entity.from_json(o) for o in detached_storage or []] + + # Validate arguments against known Juju API types. + if destroyed_storage_ is not None and not isinstance( + destroyed_storage_, (bytes, str, list) + ): + raise Exception( + f"Expected destroyed_storage_ to be a Sequence, received: {type(destroyed_storage_)}" + ) + + if detached_storage_ is not None and not isinstance( + detached_storage_, (bytes, str, list) + ): + raise Exception( + f"Expected detached_storage_ to be a Sequence, received: {type(detached_storage_)}" + ) + + self.destroyed_storage = destroyed_storage_ + self.detached_storage = detached_storage_ + self.unknown_fields = unknown_fields + + +class DestroyUnitParams(Type): + _toSchema = { + "destroy_storage": "destroy-storage", + "dry_run": "dry-run", + "force": "force", + "max_wait": "max-wait", + "unit_tag": "unit-tag", + } + _toPy = { + "destroy-storage": "destroy_storage", + "dry-run": "dry_run", + "force": "force", + "max-wait": "max_wait", + "unit-tag": "unit_tag", + } + + def __init__( + self, + destroy_storage=None, + dry_run=None, + force=None, + max_wait=None, + unit_tag=None, + **unknown_fields, + ): + """destroy_storage : bool + dry_run : bool + force : bool + max_wait : int + unit_tag : str + """ + destroy_storage_ = destroy_storage + dry_run_ = dry_run + force_ = force + max_wait_ = max_wait + unit_tag_ = unit_tag + + # Validate arguments against known Juju API types. + if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): + raise Exception( + f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" + ) + + if dry_run_ is not None and not isinstance(dry_run_, bool): + raise Exception( + f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if max_wait_ is not None and not isinstance(max_wait_, int): + raise Exception( + f"Expected max_wait_ to be a int, received: {type(max_wait_)}" + ) + + if unit_tag_ is not None and not isinstance(unit_tag_, (bytes, str)): + raise Exception( + f"Expected unit_tag_ to be a str, received: {type(unit_tag_)}" + ) + + self.destroy_storage = destroy_storage_ + self.dry_run = dry_run_ + self.force = force_ + self.max_wait = max_wait_ + self.unit_tag = unit_tag_ + self.unknown_fields = unknown_fields + + +class DestroyUnitResult(Type): + _toSchema = {"error": "error", "info": "info"} + _toPy = {"error": "error", "info": "info"} + + def __init__(self, error=None, info=None, **unknown_fields): + """Error : Error + info : DestroyUnitInfo + """ + error_ = Error.from_json(error) if error else None + info_ = DestroyUnitInfo.from_json(info) if info else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if info_ is not None and not isinstance(info_, (dict, DestroyUnitInfo)): + raise Exception( + f"Expected info_ to be a DestroyUnitInfo, received: {type(info_)}" + ) + + self.error = error_ + self.info = info_ + self.unknown_fields = unknown_fields + + +class DestroyUnitResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~DestroyUnitResult]""" + results_ = [DestroyUnitResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class DestroyUnitsParams(Type): + _toSchema = {"units": "units"} + _toPy = {"units": "units"} + + def __init__(self, units=None, **unknown_fields): + """Units : typing.Sequence[~DestroyUnitParams]""" + units_ = [DestroyUnitParams.from_json(o) for o in units or []] + + # Validate arguments against known Juju API types. + if units_ is not None and not isinstance(units_, (bytes, str, list)): + raise Exception( + f"Expected units_ to be a Sequence, received: {type(units_)}" + ) + + self.units = units_ + self.unknown_fields = unknown_fields + + +class DetailedStatus(Type): + _toSchema = { + "data": "data", + "err": "err", + "info": "info", + "kind": "kind", + "life": "life", + "since": "since", + "status": "status", + "version": "version", + } + _toPy = { + "data": "data", + "err": "err", + "info": "info", + "kind": "kind", + "life": "life", + "since": "since", + "status": "status", + "version": "version", + } + + def __init__( + self, + data=None, + err=None, + info=None, + kind=None, + life=None, + since=None, + status=None, + version=None, + **unknown_fields, + ): + """Data : typing.Mapping[str, typing.Any] + err : Error + info : str + kind : str + life : str + since : str + status : str + version : str + """ + data_ = data + err_ = Error.from_json(err) if err else None + info_ = info + kind_ = kind + life_ = life + since_ = since + status_ = status + version_ = version + + # Validate arguments against known Juju API types. + if data_ is not None and not isinstance(data_, dict): + raise Exception(f"Expected data_ to be a Mapping, received: {type(data_)}") + + if err_ is not None and not isinstance(err_, (dict, Error)): + raise Exception(f"Expected err_ to be a Error, received: {type(err_)}") + + if info_ is not None and not isinstance(info_, (bytes, str)): + raise Exception(f"Expected info_ to be a str, received: {type(info_)}") + + if kind_ is not None and not isinstance(kind_, (bytes, str)): + raise Exception(f"Expected kind_ to be a str, received: {type(kind_)}") + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if since_ is not None and not isinstance(since_, (bytes, str)): + raise Exception(f"Expected since_ to be a str, received: {type(since_)}") + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + if version_ is not None and not isinstance(version_, (bytes, str)): + raise Exception( + f"Expected version_ to be a str, received: {type(version_)}" + ) + + self.data = data_ + self.err = err_ + self.info = info_ + self.kind = kind_ + self.life = life_ + self.since = since_ + self.status = status_ + self.version = version_ + self.unknown_fields = unknown_fields + + +class DownloadInfoResult(Type): + _toSchema = {"charm_origin": "charm-origin", "url": "url"} + _toPy = {"charm-origin": "charm_origin", "url": "url"} + + def __init__(self, charm_origin=None, url=None, **unknown_fields): + """charm_origin : CharmOrigin + url : str + """ + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + url_ = url + + # Validate arguments against known Juju API types. + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if url_ is not None and not isinstance(url_, (bytes, str)): + raise Exception(f"Expected url_ to be a str, received: {type(url_)}") + + self.charm_origin = charm_origin_ + self.url = url_ + self.unknown_fields = unknown_fields + + +class DownloadInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~DownloadInfoResult]""" + results_ = [DownloadInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class DumpModelRequest(Type): + _toSchema = {"entities": "entities", "simplified": "simplified"} + _toPy = {"entities": "entities", "simplified": "simplified"} + + def __init__(self, entities=None, simplified=None, **unknown_fields): + """Entities : typing.Sequence[~Entity] + simplified : bool + """ + entities_ = [Entity.from_json(o) for o in entities or []] + simplified_ = simplified + + # Validate arguments against known Juju API types. + if entities_ is not None and not isinstance(entities_, (bytes, str, list)): + raise Exception( + f"Expected entities_ to be a Sequence, received: {type(entities_)}" + ) + + if simplified_ is not None and not isinstance(simplified_, bool): + raise Exception( + f"Expected simplified_ to be a bool, received: {type(simplified_)}" + ) + + self.entities = entities_ + self.simplified = simplified_ + self.unknown_fields = unknown_fields + + +class EndpointFilterAttributes(Type): + _toSchema = {"interface": "interface", "name": "name", "role": "role"} + _toPy = {"interface": "interface", "name": "name", "role": "role"} + + def __init__(self, interface=None, name=None, role=None, **unknown_fields): + """Interface : str + name : str + role : str + """ + interface_ = interface + name_ = name + role_ = role + + # Validate arguments against known Juju API types. + if interface_ is not None and not isinstance(interface_, (bytes, str)): + raise Exception( + f"Expected interface_ to be a str, received: {type(interface_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if role_ is not None and not isinstance(role_, (bytes, str)): + raise Exception(f"Expected role_ to be a str, received: {type(role_)}") + + self.interface = interface_ + self.name = name_ + self.role = role_ + self.unknown_fields = unknown_fields + + +class EndpointRelationData(Type): + _toSchema = { + "applicationdata": "ApplicationData", + "cross_model": "cross-model", + "endpoint": "endpoint", + "related_endpoint": "related-endpoint", + "relation_id": "relation-id", + "unit_relation_data": "unit-relation-data", + } + _toPy = { + "ApplicationData": "applicationdata", + "cross-model": "cross_model", + "endpoint": "endpoint", + "related-endpoint": "related_endpoint", + "relation-id": "relation_id", + "unit-relation-data": "unit_relation_data", + } + + def __init__( + self, + applicationdata=None, + cross_model=None, + endpoint=None, + related_endpoint=None, + relation_id=None, + unit_relation_data=None, + **unknown_fields, + ): + """Applicationdata : typing.Mapping[str, typing.Any] + cross_model : bool + endpoint : str + related_endpoint : str + relation_id : int + unit_relation_data : typing.Mapping[str, ~RelationData] + """ + applicationdata_ = applicationdata + cross_model_ = cross_model + endpoint_ = endpoint + related_endpoint_ = related_endpoint + relation_id_ = relation_id + unit_relation_data_ = { + k: RelationData.from_json(v) + for k, v in (unit_relation_data or dict()).items() + } + + # Validate arguments against known Juju API types. + if applicationdata_ is not None and not isinstance(applicationdata_, dict): + raise Exception( + f"Expected applicationdata_ to be a Mapping, received: {type(applicationdata_)}" + ) + + if cross_model_ is not None and not isinstance(cross_model_, bool): + raise Exception( + f"Expected cross_model_ to be a bool, received: {type(cross_model_)}" + ) + + if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): + raise Exception( + f"Expected endpoint_ to be a str, received: {type(endpoint_)}" + ) + + if related_endpoint_ is not None and not isinstance( + related_endpoint_, (bytes, str) + ): + raise Exception( + f"Expected related_endpoint_ to be a str, received: {type(related_endpoint_)}" + ) + + if relation_id_ is not None and not isinstance(relation_id_, int): + raise Exception( + f"Expected relation_id_ to be a int, received: {type(relation_id_)}" + ) + + if unit_relation_data_ is not None and not isinstance( + unit_relation_data_, dict + ): + raise Exception( + f"Expected unit_relation_data_ to be a Mapping, received: {type(unit_relation_data_)}" + ) + + self.applicationdata = applicationdata_ + self.cross_model = cross_model_ + self.endpoint = endpoint_ + self.related_endpoint = related_endpoint_ + self.relation_id = relation_id_ + self.unit_relation_data = unit_relation_data_ + self.unknown_fields = unknown_fields + + +class EndpointStatus(Type): + _toSchema = { + "application": "application", + "name": "name", + "role": "role", + "subordinate": "subordinate", + } + _toPy = { + "application": "application", + "name": "name", + "role": "role", + "subordinate": "subordinate", + } + + def __init__( + self, application=None, name=None, role=None, subordinate=None, **unknown_fields + ): + """Application : str + name : str + role : str + subordinate : bool + """ + application_ = application + name_ = name + role_ = role + subordinate_ = subordinate + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if role_ is not None and not isinstance(role_, (bytes, str)): + raise Exception(f"Expected role_ to be a str, received: {type(role_)}") + + if subordinate_ is not None and not isinstance(subordinate_, bool): + raise Exception( + f"Expected subordinate_ to be a bool, received: {type(subordinate_)}" + ) + + self.application = application_ + self.name = name_ + self.role = role_ + self.subordinate = subordinate_ + self.unknown_fields = unknown_fields + + +class EnqueuedActions(Type): + _toSchema = {"actions": "actions", "operation": "operation"} + _toPy = {"actions": "actions", "operation": "operation"} + + def __init__(self, actions=None, operation=None, **unknown_fields): + """Actions : typing.Sequence[~ActionResult] + operation : str + """ + actions_ = [ActionResult.from_json(o) for o in actions or []] + operation_ = operation + + # Validate arguments against known Juju API types. + if actions_ is not None and not isinstance(actions_, (bytes, str, list)): + raise Exception( + f"Expected actions_ to be a Sequence, received: {type(actions_)}" + ) + + if operation_ is not None and not isinstance(operation_, (bytes, str)): + raise Exception( + f"Expected operation_ to be a str, received: {type(operation_)}" + ) + + self.actions = actions_ + self.operation = operation_ + self.unknown_fields = unknown_fields + + +class Entities(Type): + _toSchema = {"entities": "entities"} + _toPy = {"entities": "entities"} + + def __init__(self, entities=None, **unknown_fields): + """Entities : typing.Sequence[~Entity]""" + entities_ = [Entity.from_json(o) for o in entities or []] + + # Validate arguments against known Juju API types. + if entities_ is not None and not isinstance(entities_, (bytes, str, list)): + raise Exception( + f"Expected entities_ to be a Sequence, received: {type(entities_)}" + ) + + self.entities = entities_ + self.unknown_fields = unknown_fields + + +class Entity(Type): + _toSchema = {"tag": "tag"} + _toPy = {"tag": "tag"} + + def __init__(self, tag=None, **unknown_fields): + """Tag : str""" + tag_ = tag + + # Validate arguments against known Juju API types. + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class EntityAnnotations(Type): + _toSchema = {"annotations": "annotations", "entity": "entity"} + _toPy = {"annotations": "annotations", "entity": "entity"} + + def __init__(self, annotations=None, entity=None, **unknown_fields): + """Annotations : typing.Mapping[str, str] + entity : str + """ + annotations_ = annotations + entity_ = entity + + # Validate arguments against known Juju API types. + if annotations_ is not None and not isinstance(annotations_, dict): + raise Exception( + f"Expected annotations_ to be a Mapping, received: {type(annotations_)}" + ) + + if entity_ is not None and not isinstance(entity_, (bytes, str)): + raise Exception(f"Expected entity_ to be a str, received: {type(entity_)}") + + self.annotations = annotations_ + self.entity = entity_ + self.unknown_fields = unknown_fields + + +class EntityMetrics(Type): + _toSchema = {"error": "error", "metrics": "metrics"} + _toPy = {"error": "error", "metrics": "metrics"} + + def __init__(self, error=None, metrics=None, **unknown_fields): + """Error : Error + metrics : typing.Sequence[~MetricResult] + """ + error_ = Error.from_json(error) if error else None + metrics_ = [MetricResult.from_json(o) for o in metrics or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if metrics_ is not None and not isinstance(metrics_, (bytes, str, list)): + raise Exception( + f"Expected metrics_ to be a Sequence, received: {type(metrics_)}" + ) + + self.error = error_ + self.metrics = metrics_ + self.unknown_fields = unknown_fields + + +class EntityPassword(Type): + _toSchema = {"password": "password", "tag": "tag"} + _toPy = {"password": "password", "tag": "tag"} + + def __init__(self, password=None, tag=None, **unknown_fields): + """Password : str + tag : str + """ + password_ = password + tag_ = tag + + # Validate arguments against known Juju API types. + if password_ is not None and not isinstance(password_, (bytes, str)): + raise Exception( + f"Expected password_ to be a str, received: {type(password_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.password = password_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class EntityPasswords(Type): + _toSchema = {"changes": "changes"} + _toPy = {"changes": "changes"} + + def __init__(self, changes=None, **unknown_fields): + """Changes : typing.Sequence[~EntityPassword]""" + changes_ = [EntityPassword.from_json(o) for o in changes or []] + + # Validate arguments against known Juju API types. + if changes_ is not None and not isinstance(changes_, (bytes, str, list)): + raise Exception( + f"Expected changes_ to be a Sequence, received: {type(changes_)}" + ) + + self.changes = changes_ + self.unknown_fields = unknown_fields + + +class EntityStatus(Type): + _toSchema = {"data": "data", "info": "info", "since": "since", "status": "status"} + _toPy = {"data": "data", "info": "info", "since": "since", "status": "status"} + + def __init__(self, data=None, info=None, since=None, status=None, **unknown_fields): + """Data : typing.Mapping[str, typing.Any] + info : str + since : str + status : str + """ + data_ = data + info_ = info + since_ = since + status_ = status + + # Validate arguments against known Juju API types. + if data_ is not None and not isinstance(data_, dict): + raise Exception(f"Expected data_ to be a Mapping, received: {type(data_)}") + + if info_ is not None and not isinstance(info_, (bytes, str)): + raise Exception(f"Expected info_ to be a str, received: {type(info_)}") + + if since_ is not None and not isinstance(since_, (bytes, str)): + raise Exception(f"Expected since_ to be a str, received: {type(since_)}") + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + self.data = data_ + self.info = info_ + self.since = since_ + self.status = status_ + self.unknown_fields = unknown_fields + + +class Error(Type): + _toSchema = {"code": "code", "info": "info", "message": "message"} + _toPy = {"code": "code", "info": "info", "message": "message"} + + def __init__(self, code=None, info=None, message=None, **unknown_fields): + """Code : str + info : typing.Mapping[str, typing.Any] + message : str + """ + code_ = code + info_ = info + message_ = message + + # Validate arguments against known Juju API types. + if code_ is not None and not isinstance(code_, (bytes, str)): + raise Exception(f"Expected code_ to be a str, received: {type(code_)}") + + if info_ is not None and not isinstance(info_, dict): + raise Exception(f"Expected info_ to be a Mapping, received: {type(info_)}") + + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + self.code = code_ + self.info = info_ + self.message = message_ + self.unknown_fields = unknown_fields + + +class ErrorResult(Type): + _toSchema = {"error": "error"} + _toPy = {"error": "error"} + + def __init__(self, error=None, **unknown_fields): + """Error : Error""" + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.error = error_ + self.unknown_fields = unknown_fields + + +class ErrorResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ErrorResult]""" + results_ = [ErrorResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ExportBundleParams(Type): + _toSchema = { + "include_charm_defaults": "include-charm-defaults", + "include_series": "include-series", + } + _toPy = { + "include-charm-defaults": "include_charm_defaults", + "include-series": "include_series", + } + + def __init__( + self, include_charm_defaults=None, include_series=None, **unknown_fields + ): + """include_charm_defaults : bool + include_series : bool + """ + include_charm_defaults_ = include_charm_defaults + include_series_ = include_series + + # Validate arguments against known Juju API types. + if include_charm_defaults_ is not None and not isinstance( + include_charm_defaults_, bool + ): + raise Exception( + f"Expected include_charm_defaults_ to be a bool, received: {type(include_charm_defaults_)}" + ) + + if include_series_ is not None and not isinstance(include_series_, bool): + raise Exception( + f"Expected include_series_ to be a bool, received: {type(include_series_)}" + ) + + self.include_charm_defaults = include_charm_defaults_ + self.include_series = include_series_ + self.unknown_fields = unknown_fields + + +class ExposedEndpoint(Type): + _toSchema = { + "expose_to_cidrs": "expose-to-cidrs", + "expose_to_spaces": "expose-to-spaces", + } + _toPy = { + "expose-to-cidrs": "expose_to_cidrs", + "expose-to-spaces": "expose_to_spaces", + } + + def __init__(self, expose_to_cidrs=None, expose_to_spaces=None, **unknown_fields): + """expose_to_cidrs : typing.Sequence[str] + expose_to_spaces : typing.Sequence[str] + """ + expose_to_cidrs_ = expose_to_cidrs + expose_to_spaces_ = expose_to_spaces + + # Validate arguments against known Juju API types. + if expose_to_cidrs_ is not None and not isinstance( + expose_to_cidrs_, (bytes, str, list) + ): + raise Exception( + f"Expected expose_to_cidrs_ to be a Sequence, received: {type(expose_to_cidrs_)}" + ) + + if expose_to_spaces_ is not None and not isinstance( + expose_to_spaces_, (bytes, str, list) + ): + raise Exception( + f"Expected expose_to_spaces_ to be a Sequence, received: {type(expose_to_spaces_)}" + ) + + self.expose_to_cidrs = expose_to_cidrs_ + self.expose_to_spaces = expose_to_spaces_ + self.unknown_fields = unknown_fields + + +class ExpressionTree(Type): + _toSchema = {"expression": "Expression"} + _toPy = {"Expression": "expression"} + + def __init__(self, expression=None, **unknown_fields): + """Expression : Any""" + expression_ = expression + + # Validate arguments against known Juju API types. + self.expression = expression_ + self.unknown_fields = unknown_fields + + +class ExternalControllerInfo(Type): + _toSchema = { + "addrs": "addrs", + "ca_cert": "ca-cert", + "controller_alias": "controller-alias", + "controller_tag": "controller-tag", + } + _toPy = { + "addrs": "addrs", + "ca-cert": "ca_cert", + "controller-alias": "controller_alias", + "controller-tag": "controller_tag", + } + + def __init__( + self, + addrs=None, + ca_cert=None, + controller_alias=None, + controller_tag=None, + **unknown_fields, + ): + """Addrs : typing.Sequence[str] + ca_cert : str + controller_alias : str + controller_tag : str + """ + addrs_ = addrs + ca_cert_ = ca_cert + controller_alias_ = controller_alias + controller_tag_ = controller_tag + + # Validate arguments against known Juju API types. + if addrs_ is not None and not isinstance(addrs_, (bytes, str, list)): + raise Exception( + f"Expected addrs_ to be a Sequence, received: {type(addrs_)}" + ) + + if ca_cert_ is not None and not isinstance(ca_cert_, (bytes, str)): + raise Exception( + f"Expected ca_cert_ to be a str, received: {type(ca_cert_)}" + ) + + if controller_alias_ is not None and not isinstance( + controller_alias_, (bytes, str) + ): + raise Exception( + f"Expected controller_alias_ to be a str, received: {type(controller_alias_)}" + ) + + if controller_tag_ is not None and not isinstance( + controller_tag_, (bytes, str) + ): + raise Exception( + f"Expected controller_tag_ to be a str, received: {type(controller_tag_)}" + ) + + self.addrs = addrs_ + self.ca_cert = ca_cert_ + self.controller_alias = controller_alias_ + self.controller_tag = controller_tag_ + self.unknown_fields = unknown_fields + + +class FilesystemAttachmentDetails(Type): + _toSchema = { + "filesystemattachmentinfo": "FilesystemAttachmentInfo", + "life": "life", + "mount_point": "mount-point", + "read_only": "read-only", + } + _toPy = { + "FilesystemAttachmentInfo": "filesystemattachmentinfo", + "life": "life", + "mount-point": "mount_point", + "read-only": "read_only", + } + + def __init__( + self, + filesystemattachmentinfo=None, + life=None, + mount_point=None, + read_only=None, + **unknown_fields, + ): + """Filesystemattachmentinfo : FilesystemAttachmentInfo + life : str + mount_point : str + read_only : bool + """ + filesystemattachmentinfo_ = ( + FilesystemAttachmentInfo.from_json(filesystemattachmentinfo) + if filesystemattachmentinfo + else None + ) + life_ = life + mount_point_ = mount_point + read_only_ = read_only + + # Validate arguments against known Juju API types. + if filesystemattachmentinfo_ is not None and not isinstance( + filesystemattachmentinfo_, (dict, FilesystemAttachmentInfo) + ): + raise Exception( + f"Expected filesystemattachmentinfo_ to be a FilesystemAttachmentInfo, received: {type(filesystemattachmentinfo_)}" + ) + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if mount_point_ is not None and not isinstance(mount_point_, (bytes, str)): + raise Exception( + f"Expected mount_point_ to be a str, received: {type(mount_point_)}" + ) + + if read_only_ is not None and not isinstance(read_only_, bool): + raise Exception( + f"Expected read_only_ to be a bool, received: {type(read_only_)}" + ) + + self.filesystemattachmentinfo = filesystemattachmentinfo_ + self.life = life_ + self.mount_point = mount_point_ + self.read_only = read_only_ + self.unknown_fields = unknown_fields + + +class FilesystemAttachmentInfo(Type): + _toSchema = {"mount_point": "mount-point", "read_only": "read-only"} + _toPy = {"mount-point": "mount_point", "read-only": "read_only"} + + def __init__(self, mount_point=None, read_only=None, **unknown_fields): + """mount_point : str + read_only : bool + """ + mount_point_ = mount_point + read_only_ = read_only + + # Validate arguments against known Juju API types. + if mount_point_ is not None and not isinstance(mount_point_, (bytes, str)): + raise Exception( + f"Expected mount_point_ to be a str, received: {type(mount_point_)}" + ) + + if read_only_ is not None and not isinstance(read_only_, bool): + raise Exception( + f"Expected read_only_ to be a bool, received: {type(read_only_)}" + ) + + self.mount_point = mount_point_ + self.read_only = read_only_ + self.unknown_fields = unknown_fields + + +class FilesystemDetails(Type): + _toSchema = { + "filesystem_tag": "filesystem-tag", + "info": "info", + "life": "life", + "machine_attachments": "machine-attachments", + "status": "status", + "storage": "storage", + "unit_attachments": "unit-attachments", + "volume_tag": "volume-tag", + } + _toPy = { + "filesystem-tag": "filesystem_tag", + "info": "info", + "life": "life", + "machine-attachments": "machine_attachments", + "status": "status", + "storage": "storage", + "unit-attachments": "unit_attachments", + "volume-tag": "volume_tag", + } + + def __init__( + self, + filesystem_tag=None, + info=None, + life=None, + machine_attachments=None, + status=None, + storage=None, + unit_attachments=None, + volume_tag=None, + **unknown_fields, + ): + """filesystem_tag : str + info : FilesystemInfo + life : str + machine_attachments : typing.Mapping[str, ~FilesystemAttachmentDetails] + status : EntityStatus + storage : StorageDetails + unit_attachments : typing.Mapping[str, ~FilesystemAttachmentDetails] + volume_tag : str + """ + filesystem_tag_ = filesystem_tag + info_ = FilesystemInfo.from_json(info) if info else None + life_ = life + machine_attachments_ = { + k: FilesystemAttachmentDetails.from_json(v) + for k, v in (machine_attachments or dict()).items() + } + status_ = EntityStatus.from_json(status) if status else None + storage_ = StorageDetails.from_json(storage) if storage else None + unit_attachments_ = { + k: FilesystemAttachmentDetails.from_json(v) + for k, v in (unit_attachments or dict()).items() + } + volume_tag_ = volume_tag + + # Validate arguments against known Juju API types. + if filesystem_tag_ is not None and not isinstance( + filesystem_tag_, (bytes, str) + ): + raise Exception( + f"Expected filesystem_tag_ to be a str, received: {type(filesystem_tag_)}" + ) + + if info_ is not None and not isinstance(info_, (dict, FilesystemInfo)): + raise Exception( + f"Expected info_ to be a FilesystemInfo, received: {type(info_)}" + ) + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if machine_attachments_ is not None and not isinstance( + machine_attachments_, dict + ): + raise Exception( + f"Expected machine_attachments_ to be a Mapping, received: {type(machine_attachments_)}" + ) + + if status_ is not None and not isinstance(status_, (dict, EntityStatus)): + raise Exception( + f"Expected status_ to be a EntityStatus, received: {type(status_)}" + ) + + if storage_ is not None and not isinstance(storage_, (dict, StorageDetails)): + raise Exception( + f"Expected storage_ to be a StorageDetails, received: {type(storage_)}" + ) + + if unit_attachments_ is not None and not isinstance(unit_attachments_, dict): + raise Exception( + f"Expected unit_attachments_ to be a Mapping, received: {type(unit_attachments_)}" + ) + + if volume_tag_ is not None and not isinstance(volume_tag_, (bytes, str)): + raise Exception( + f"Expected volume_tag_ to be a str, received: {type(volume_tag_)}" + ) + + self.filesystem_tag = filesystem_tag_ + self.info = info_ + self.life = life_ + self.machine_attachments = machine_attachments_ + self.status = status_ + self.storage = storage_ + self.unit_attachments = unit_attachments_ + self.volume_tag = volume_tag_ + self.unknown_fields = unknown_fields + + +class FilesystemDetailsListResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : typing.Sequence[~FilesystemDetails] + """ + error_ = Error.from_json(error) if error else None + result_ = [FilesystemDetails.from_json(o) for o in result or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (bytes, str, list)): + raise Exception( + f"Expected result_ to be a Sequence, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class FilesystemDetailsListResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~FilesystemDetailsListResult]""" + results_ = [FilesystemDetailsListResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class FilesystemFilter(Type): + _toSchema = {"machines": "machines"} + _toPy = {"machines": "machines"} + + def __init__(self, machines=None, **unknown_fields): + """Machines : typing.Sequence[str]""" + machines_ = machines + + # Validate arguments against known Juju API types. + if machines_ is not None and not isinstance(machines_, (bytes, str, list)): + raise Exception( + f"Expected machines_ to be a Sequence, received: {type(machines_)}" + ) + + self.machines = machines_ + self.unknown_fields = unknown_fields + + +class FilesystemFilters(Type): + _toSchema = {"filters": "filters"} + _toPy = {"filters": "filters"} + + def __init__(self, filters=None, **unknown_fields): + """Filters : typing.Sequence[~FilesystemFilter]""" + filters_ = [FilesystemFilter.from_json(o) for o in filters or []] + + # Validate arguments against known Juju API types. + if filters_ is not None and not isinstance(filters_, (bytes, str, list)): + raise Exception( + f"Expected filters_ to be a Sequence, received: {type(filters_)}" + ) + + self.filters = filters_ + self.unknown_fields = unknown_fields + + +class FilesystemInfo(Type): + _toSchema = {"filesystem_id": "filesystem-id", "pool": "pool", "size": "size"} + _toPy = {"filesystem-id": "filesystem_id", "pool": "pool", "size": "size"} + + def __init__(self, filesystem_id=None, pool=None, size=None, **unknown_fields): + """filesystem_id : str + pool : str + size : int + """ + filesystem_id_ = filesystem_id + pool_ = pool + size_ = size + + # Validate arguments against known Juju API types. + if filesystem_id_ is not None and not isinstance(filesystem_id_, (bytes, str)): + raise Exception( + f"Expected filesystem_id_ to be a str, received: {type(filesystem_id_)}" + ) + + if pool_ is not None and not isinstance(pool_, (bytes, str)): + raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + self.filesystem_id = filesystem_id_ + self.pool = pool_ + self.size = size_ + self.unknown_fields = unknown_fields + + +class FindToolsParams(Type): + _toSchema = { + "agentstream": "agentstream", + "arch": "arch", + "major": "major", + "number": "number", + "os_type": "os-type", + } + _toPy = { + "agentstream": "agentstream", + "arch": "arch", + "major": "major", + "number": "number", + "os-type": "os_type", + } + + def __init__( + self, + agentstream=None, + arch=None, + major=None, + number=None, + os_type=None, + **unknown_fields, + ): + """Agentstream : str + arch : str + major : int + number : Number + os_type : str + """ + agentstream_ = agentstream + arch_ = arch + major_ = major + number_ = Number.from_json(number) if number else None + os_type_ = os_type + + # Validate arguments against known Juju API types. + if agentstream_ is not None and not isinstance(agentstream_, (bytes, str)): + raise Exception( + f"Expected agentstream_ to be a str, received: {type(agentstream_)}" + ) + + if arch_ is not None and not isinstance(arch_, (bytes, str)): + raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") + + if major_ is not None and not isinstance(major_, int): + raise Exception(f"Expected major_ to be a int, received: {type(major_)}") + + if number_ is not None and not isinstance(number_, (dict, Number)): + raise Exception( + f"Expected number_ to be a Number, received: {type(number_)}" + ) + + if os_type_ is not None and not isinstance(os_type_, (bytes, str)): + raise Exception( + f"Expected os_type_ to be a str, received: {type(os_type_)}" + ) + + self.agentstream = agentstream_ + self.arch = arch_ + self.major = major_ + self.number = number_ + self.os_type = os_type_ + self.unknown_fields = unknown_fields + + +class FindToolsResult(Type): + _toSchema = {"error": "error", "list_": "list"} + _toPy = {"error": "error", "list": "list_"} + + def __init__(self, error=None, list_=None, **unknown_fields): + """Error : Error + list_ : typing.Sequence[~Tools] + """ + error_ = Error.from_json(error) if error else None + list__ = [Tools.from_json(o) for o in list_ or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if list__ is not None and not isinstance(list__, (bytes, str, list)): + raise Exception( + f"Expected list__ to be a Sequence, received: {type(list__)}" + ) + + self.error = error_ + self.list_ = list__ + self.unknown_fields = unknown_fields + + +class FirewallRule(Type): + _toSchema = {"known_service": "known-service", "whitelist_cidrs": "whitelist-cidrs"} + _toPy = {"known-service": "known_service", "whitelist-cidrs": "whitelist_cidrs"} + + def __init__(self, known_service=None, whitelist_cidrs=None, **unknown_fields): + """known_service : str + whitelist_cidrs : typing.Sequence[str] + """ + known_service_ = known_service + whitelist_cidrs_ = whitelist_cidrs + + # Validate arguments against known Juju API types. + if known_service_ is not None and not isinstance(known_service_, (bytes, str)): + raise Exception( + f"Expected known_service_ to be a str, received: {type(known_service_)}" + ) + + if whitelist_cidrs_ is not None and not isinstance( + whitelist_cidrs_, (bytes, str, list) + ): + raise Exception( + f"Expected whitelist_cidrs_ to be a Sequence, received: {type(whitelist_cidrs_)}" + ) + + self.known_service = known_service_ + self.whitelist_cidrs = whitelist_cidrs_ + self.unknown_fields = unknown_fields + + +class FirewallRuleArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~FirewallRule]""" + args_ = [FirewallRule.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class FullStatus(Type): + _toSchema = { + "applications": "applications", + "branches": "branches", + "controller_timestamp": "controller-timestamp", + "filesystems": "filesystems", + "machines": "machines", + "model": "model", + "offers": "offers", + "relations": "relations", + "remote_applications": "remote-applications", + "storage": "storage", + "volumes": "volumes", + } + _toPy = { + "applications": "applications", + "branches": "branches", + "controller-timestamp": "controller_timestamp", + "filesystems": "filesystems", + "machines": "machines", + "model": "model", + "offers": "offers", + "relations": "relations", + "remote-applications": "remote_applications", + "storage": "storage", + "volumes": "volumes", + } + + def __init__( + self, + applications=None, + branches=None, + controller_timestamp=None, + filesystems=None, + machines=None, + model=None, + offers=None, + relations=None, + remote_applications=None, + storage=None, + volumes=None, + **unknown_fields, + ): + """Applications : typing.Mapping[str, ~ApplicationStatus] + branches : typing.Mapping[str, ~BranchStatus] + controller_timestamp : str + filesystems : typing.Sequence[~FilesystemDetails] + machines : typing.Mapping[str, ~MachineStatus] + model : ModelStatusInfo + offers : typing.Mapping[str, ~ApplicationOfferStatus] + relations : typing.Sequence[~RelationStatus] + remote_applications : typing.Mapping[str, ~RemoteApplicationStatus] + storage : typing.Sequence[~StorageDetails] + volumes : typing.Sequence[~VolumeDetails] + """ + applications_ = { + k: ApplicationStatus.from_json(v) + for k, v in (applications or dict()).items() + } + branches_ = { + k: BranchStatus.from_json(v) for k, v in (branches or dict()).items() + } + controller_timestamp_ = controller_timestamp + filesystems_ = [FilesystemDetails.from_json(o) for o in filesystems or []] + machines_ = { + k: MachineStatus.from_json(v) for k, v in (machines or dict()).items() + } + model_ = ModelStatusInfo.from_json(model) if model else None + offers_ = { + k: ApplicationOfferStatus.from_json(v) + for k, v in (offers or dict()).items() + } + relations_ = [RelationStatus.from_json(o) for o in relations or []] + remote_applications_ = { + k: RemoteApplicationStatus.from_json(v) + for k, v in (remote_applications or dict()).items() + } + storage_ = [StorageDetails.from_json(o) for o in storage or []] + volumes_ = [VolumeDetails.from_json(o) for o in volumes or []] + + # Validate arguments against known Juju API types. + if applications_ is not None and not isinstance(applications_, dict): + raise Exception( + f"Expected applications_ to be a Mapping, received: {type(applications_)}" + ) + + if branches_ is not None and not isinstance(branches_, dict): + raise Exception( + f"Expected branches_ to be a Mapping, received: {type(branches_)}" + ) + + if controller_timestamp_ is not None and not isinstance( + controller_timestamp_, (bytes, str) + ): + raise Exception( + f"Expected controller_timestamp_ to be a str, received: {type(controller_timestamp_)}" + ) + + if filesystems_ is not None and not isinstance( + filesystems_, (bytes, str, list) + ): + raise Exception( + f"Expected filesystems_ to be a Sequence, received: {type(filesystems_)}" + ) + + if machines_ is not None and not isinstance(machines_, dict): + raise Exception( + f"Expected machines_ to be a Mapping, received: {type(machines_)}" + ) + + if model_ is not None and not isinstance(model_, (dict, ModelStatusInfo)): + raise Exception( + f"Expected model_ to be a ModelStatusInfo, received: {type(model_)}" + ) + + if offers_ is not None and not isinstance(offers_, dict): + raise Exception( + f"Expected offers_ to be a Mapping, received: {type(offers_)}" + ) + + if relations_ is not None and not isinstance(relations_, (bytes, str, list)): + raise Exception( + f"Expected relations_ to be a Sequence, received: {type(relations_)}" + ) + + if remote_applications_ is not None and not isinstance( + remote_applications_, dict + ): + raise Exception( + f"Expected remote_applications_ to be a Mapping, received: {type(remote_applications_)}" + ) + + if storage_ is not None and not isinstance(storage_, (bytes, str, list)): + raise Exception( + f"Expected storage_ to be a Sequence, received: {type(storage_)}" + ) + + if volumes_ is not None and not isinstance(volumes_, (bytes, str, list)): + raise Exception( + f"Expected volumes_ to be a Sequence, received: {type(volumes_)}" + ) + + self.applications = applications_ + self.branches = branches_ + self.controller_timestamp = controller_timestamp_ + self.filesystems = filesystems_ + self.machines = machines_ + self.model = model_ + self.offers = offers_ + self.relations = relations_ + self.remote_applications = remote_applications_ + self.storage = storage_ + self.volumes = volumes_ + self.unknown_fields = unknown_fields + + +class Generation(Type): + _toSchema = { + "applications": "applications", + "branch": "branch", + "completed": "completed", + "completed_by": "completed-by", + "created": "created", + "created_by": "created-by", + "generation_id": "generation-id", + } + _toPy = { + "applications": "applications", + "branch": "branch", + "completed": "completed", + "completed-by": "completed_by", + "created": "created", + "created-by": "created_by", + "generation-id": "generation_id", + } + + def __init__( + self, + applications=None, + branch=None, + completed=None, + completed_by=None, + created=None, + created_by=None, + generation_id=None, + **unknown_fields, + ): + """Applications : typing.Sequence[~GenerationApplication] + branch : str + completed : int + completed_by : str + created : int + created_by : str + generation_id : int + """ + applications_ = [GenerationApplication.from_json(o) for o in applications or []] + branch_ = branch + completed_ = completed + completed_by_ = completed_by + created_ = created + created_by_ = created_by + generation_id_ = generation_id + + # Validate arguments against known Juju API types. + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + if branch_ is not None and not isinstance(branch_, (bytes, str)): + raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") + + if completed_ is not None and not isinstance(completed_, int): + raise Exception( + f"Expected completed_ to be a int, received: {type(completed_)}" + ) + + if completed_by_ is not None and not isinstance(completed_by_, (bytes, str)): + raise Exception( + f"Expected completed_by_ to be a str, received: {type(completed_by_)}" + ) + + if created_ is not None and not isinstance(created_, int): + raise Exception( + f"Expected created_ to be a int, received: {type(created_)}" + ) + + if created_by_ is not None and not isinstance(created_by_, (bytes, str)): + raise Exception( + f"Expected created_by_ to be a str, received: {type(created_by_)}" + ) + + if generation_id_ is not None and not isinstance(generation_id_, int): + raise Exception( + f"Expected generation_id_ to be a int, received: {type(generation_id_)}" + ) + + self.applications = applications_ + self.branch = branch_ + self.completed = completed_ + self.completed_by = completed_by_ + self.created = created_ + self.created_by = created_by_ + self.generation_id = generation_id_ + self.unknown_fields = unknown_fields + + +class GenerationApplication(Type): + _toSchema = { + "application": "application", + "config": "config", + "pending": "pending", + "progress": "progress", + "tracking": "tracking", + } + _toPy = { + "application": "application", + "config": "config", + "pending": "pending", + "progress": "progress", + "tracking": "tracking", + } + + def __init__( + self, + application=None, + config=None, + pending=None, + progress=None, + tracking=None, + **unknown_fields, + ): + """Application : str + config : typing.Mapping[str, typing.Any] + pending : typing.Sequence[str] + progress : str + tracking : typing.Sequence[str] + """ + application_ = application + config_ = config + pending_ = pending + progress_ = progress + tracking_ = tracking + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if pending_ is not None and not isinstance(pending_, (bytes, str, list)): + raise Exception( + f"Expected pending_ to be a Sequence, received: {type(pending_)}" + ) + + if progress_ is not None and not isinstance(progress_, (bytes, str)): + raise Exception( + f"Expected progress_ to be a str, received: {type(progress_)}" + ) + + if tracking_ is not None and not isinstance(tracking_, (bytes, str, list)): + raise Exception( + f"Expected tracking_ to be a Sequence, received: {type(tracking_)}" + ) + + self.application = application_ + self.config = config_ + self.pending = pending_ + self.progress = progress_ + self.tracking = tracking_ + self.unknown_fields = unknown_fields + + +class GenerationId(Type): + _toSchema = {"generation_id": "generation-id"} + _toPy = {"generation-id": "generation_id"} + + def __init__(self, generation_id=None, **unknown_fields): + """generation_id : int""" + generation_id_ = generation_id + + # Validate arguments against known Juju API types. + if generation_id_ is not None and not isinstance(generation_id_, int): + raise Exception( + f"Expected generation_id_ to be a int, received: {type(generation_id_)}" + ) + + self.generation_id = generation_id_ + self.unknown_fields = unknown_fields + + +class GenerationResult(Type): + _toSchema = {"error": "error", "generation": "generation"} + _toPy = {"error": "error", "generation": "generation"} + + def __init__(self, error=None, generation=None, **unknown_fields): + """Error : Error + generation : Generation + """ + error_ = Error.from_json(error) if error else None + generation_ = Generation.from_json(generation) if generation else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if generation_ is not None and not isinstance(generation_, (dict, Generation)): + raise Exception( + f"Expected generation_ to be a Generation, received: {type(generation_)}" + ) + + self.error = error_ + self.generation = generation_ + self.unknown_fields = unknown_fields + + +class GetConstraintsResults(Type): + _toSchema = {"constraints": "constraints"} + _toPy = {"constraints": "constraints"} + + def __init__(self, constraints=None, **unknown_fields): + """Constraints : Value""" + constraints_ = Value.from_json(constraints) if constraints else None + + # Validate arguments against known Juju API types. + if constraints_ is not None and not isinstance(constraints_, (dict, Value)): + raise Exception( + f"Expected constraints_ to be a Value, received: {type(constraints_)}" + ) + + self.constraints = constraints_ + self.unknown_fields = unknown_fields + + +class GrantRevokeUserSecretArg(Type): + _toSchema = {"applications": "applications", "label": "label", "uri": "uri"} + _toPy = {"applications": "applications", "label": "label", "uri": "uri"} + + def __init__(self, applications=None, label=None, uri=None, **unknown_fields): + """Applications : typing.Sequence[str] + label : str + uri : str + """ + applications_ = applications + label_ = label + uri_ = uri + + # Validate arguments against known Juju API types. + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + if label_ is not None and not isinstance(label_, (bytes, str)): + raise Exception(f"Expected label_ to be a str, received: {type(label_)}") + + if uri_ is not None and not isinstance(uri_, (bytes, str)): + raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") + + self.applications = applications_ + self.label = label_ + self.uri = uri_ + self.unknown_fields = unknown_fields + + +class HardwareCharacteristics(Type): + _toSchema = { + "arch": "arch", + "availability_zone": "availability-zone", + "cpu_cores": "cpu-cores", + "cpu_power": "cpu-power", + "mem": "mem", + "root_disk": "root-disk", + "root_disk_source": "root-disk-source", + "tags": "tags", + "virt_type": "virt-type", + } + _toPy = { + "arch": "arch", + "availability-zone": "availability_zone", + "cpu-cores": "cpu_cores", + "cpu-power": "cpu_power", + "mem": "mem", + "root-disk": "root_disk", + "root-disk-source": "root_disk_source", + "tags": "tags", + "virt-type": "virt_type", + } + + def __init__( + self, + arch=None, + availability_zone=None, + cpu_cores=None, + cpu_power=None, + mem=None, + root_disk=None, + root_disk_source=None, + tags=None, + virt_type=None, + **unknown_fields, + ): + """Arch : str + availability_zone : str + cpu_cores : int + cpu_power : int + mem : int + root_disk : int + root_disk_source : str + tags : typing.Sequence[str] + virt_type : str + """ + arch_ = arch + availability_zone_ = availability_zone + cpu_cores_ = cpu_cores + cpu_power_ = cpu_power + mem_ = mem + root_disk_ = root_disk + root_disk_source_ = root_disk_source + tags_ = tags + virt_type_ = virt_type + + # Validate arguments against known Juju API types. + if arch_ is not None and not isinstance(arch_, (bytes, str)): + raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") + + if availability_zone_ is not None and not isinstance( + availability_zone_, (bytes, str) + ): + raise Exception( + f"Expected availability_zone_ to be a str, received: {type(availability_zone_)}" + ) + + if cpu_cores_ is not None and not isinstance(cpu_cores_, int): + raise Exception( + f"Expected cpu_cores_ to be a int, received: {type(cpu_cores_)}" + ) + + if cpu_power_ is not None and not isinstance(cpu_power_, int): + raise Exception( + f"Expected cpu_power_ to be a int, received: {type(cpu_power_)}" + ) + + if mem_ is not None and not isinstance(mem_, int): + raise Exception(f"Expected mem_ to be a int, received: {type(mem_)}") + + if root_disk_ is not None and not isinstance(root_disk_, int): + raise Exception( + f"Expected root_disk_ to be a int, received: {type(root_disk_)}" + ) + + if root_disk_source_ is not None and not isinstance( + root_disk_source_, (bytes, str) + ): + raise Exception( + f"Expected root_disk_source_ to be a str, received: {type(root_disk_source_)}" + ) + + if tags_ is not None and not isinstance(tags_, (bytes, str, list)): + raise Exception(f"Expected tags_ to be a Sequence, received: {type(tags_)}") + + if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): + raise Exception( + f"Expected virt_type_ to be a str, received: {type(virt_type_)}" + ) + + self.arch = arch_ + self.availability_zone = availability_zone_ + self.cpu_cores = cpu_cores_ + self.cpu_power = cpu_power_ + self.mem = mem_ + self.root_disk = root_disk_ + self.root_disk_source = root_disk_source_ + self.tags = tags_ + self.virt_type = virt_type_ + self.unknown_fields = unknown_fields + + +class History(Type): + _toSchema = {"error": "error", "statuses": "statuses"} + _toPy = {"error": "error", "statuses": "statuses"} + + def __init__(self, error=None, statuses=None, **unknown_fields): + """Error : Error + statuses : typing.Sequence[~DetailedStatus] + """ + error_ = Error.from_json(error) if error else None + statuses_ = [DetailedStatus.from_json(o) for o in statuses or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if statuses_ is not None and not isinstance(statuses_, (bytes, str, list)): + raise Exception( + f"Expected statuses_ to be a Sequence, received: {type(statuses_)}" + ) + + self.error = error_ + self.statuses = statuses_ + self.unknown_fields = unknown_fields + + +class HostPort(Type): + _toSchema = { + "address": "Address", + "cidr": "cidr", + "config_type": "config-type", + "is_secondary": "is-secondary", + "port": "port", + "scope": "scope", + "space_id": "space-id", + "space_name": "space-name", + "type_": "type", + "value": "value", + } + _toPy = { + "Address": "address", + "cidr": "cidr", + "config-type": "config_type", + "is-secondary": "is_secondary", + "port": "port", + "scope": "scope", + "space-id": "space_id", + "space-name": "space_name", + "type": "type_", + "value": "value", + } + + def __init__( + self, + address=None, + cidr=None, + config_type=None, + is_secondary=None, + port=None, + scope=None, + space_id=None, + space_name=None, + type_=None, + value=None, + **unknown_fields, + ): + """Address : Address + cidr : str + config_type : str + is_secondary : bool + port : int + scope : str + space_id : str + space_name : str + type_ : str + value : str + """ + address_ = Address.from_json(address) if address else None + cidr_ = cidr + config_type_ = config_type + is_secondary_ = is_secondary + port_ = port + scope_ = scope + space_id_ = space_id + space_name_ = space_name + type__ = type_ + value_ = value + + # Validate arguments against known Juju API types. + if address_ is not None and not isinstance(address_, (dict, Address)): + raise Exception( + f"Expected address_ to be a Address, received: {type(address_)}" + ) + + if cidr_ is not None and not isinstance(cidr_, (bytes, str)): + raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") + + if config_type_ is not None and not isinstance(config_type_, (bytes, str)): + raise Exception( + f"Expected config_type_ to be a str, received: {type(config_type_)}" + ) + + if is_secondary_ is not None and not isinstance(is_secondary_, bool): + raise Exception( + f"Expected is_secondary_ to be a bool, received: {type(is_secondary_)}" + ) + + if port_ is not None and not isinstance(port_, int): + raise Exception(f"Expected port_ to be a int, received: {type(port_)}") + + if scope_ is not None and not isinstance(scope_, (bytes, str)): + raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") + + if space_id_ is not None and not isinstance(space_id_, (bytes, str)): + raise Exception( + f"Expected space_id_ to be a str, received: {type(space_id_)}" + ) + + if space_name_ is not None and not isinstance(space_name_, (bytes, str)): + raise Exception( + f"Expected space_name_ to be a str, received: {type(space_name_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + if value_ is not None and not isinstance(value_, (bytes, str)): + raise Exception(f"Expected value_ to be a str, received: {type(value_)}") + + self.address = address_ + self.cidr = cidr_ + self.config_type = config_type_ + self.is_secondary = is_secondary_ + self.port = port_ + self.scope = scope_ + self.space_id = space_id_ + self.space_name = space_name_ + self.type_ = type__ + self.value = value_ + self.unknown_fields = unknown_fields + + +class HostedModelConfig(Type): + _toSchema = { + "cloud_spec": "cloud-spec", + "config": "config", + "error": "error", + "name": "name", + "owner": "owner", + } + _toPy = { + "cloud-spec": "cloud_spec", + "config": "config", + "error": "error", + "name": "name", + "owner": "owner", + } + + def __init__( + self, + cloud_spec=None, + config=None, + error=None, + name=None, + owner=None, + **unknown_fields, + ): + """cloud_spec : CloudSpec + config : typing.Mapping[str, typing.Any] + error : Error + name : str + owner : str + """ + cloud_spec_ = CloudSpec.from_json(cloud_spec) if cloud_spec else None + config_ = config + error_ = Error.from_json(error) if error else None + name_ = name + owner_ = owner + + # Validate arguments against known Juju API types. + if cloud_spec_ is not None and not isinstance(cloud_spec_, (dict, CloudSpec)): + raise Exception( + f"Expected cloud_spec_ to be a CloudSpec, received: {type(cloud_spec_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if owner_ is not None and not isinstance(owner_, (bytes, str)): + raise Exception(f"Expected owner_ to be a str, received: {type(owner_)}") + + self.cloud_spec = cloud_spec_ + self.config = config_ + self.error = error_ + self.name = name_ + self.owner = owner_ + self.unknown_fields = unknown_fields + + +class HostedModelConfigsResults(Type): + _toSchema = {"models": "models"} + _toPy = {"models": "models"} + + def __init__(self, models=None, **unknown_fields): + """Models : typing.Sequence[~HostedModelConfig]""" + models_ = [HostedModelConfig.from_json(o) for o in models or []] + + # Validate arguments against known Juju API types. + if models_ is not None and not isinstance(models_, (bytes, str, list)): + raise Exception( + f"Expected models_ to be a Sequence, received: {type(models_)}" + ) + + self.models = models_ + self.unknown_fields = unknown_fields + + +class ImageMetadataFilter(Type): + _toSchema = { + "arches": "arches", + "region": "region", + "root_storage_type": "root-storage-type", + "stream": "stream", + "versions": "versions", + "virt_type": "virt-type", + } + _toPy = { + "arches": "arches", + "region": "region", + "root-storage-type": "root_storage_type", + "stream": "stream", + "versions": "versions", + "virt-type": "virt_type", + } + + def __init__( + self, + arches=None, + region=None, + root_storage_type=None, + stream=None, + versions=None, + virt_type=None, + **unknown_fields, + ): + """Arches : typing.Sequence[str] + region : str + root_storage_type : str + stream : str + versions : typing.Sequence[str] + virt_type : str + """ + arches_ = arches + region_ = region + root_storage_type_ = root_storage_type + stream_ = stream + versions_ = versions + virt_type_ = virt_type + + # Validate arguments against known Juju API types. + if arches_ is not None and not isinstance(arches_, (bytes, str, list)): + raise Exception( + f"Expected arches_ to be a Sequence, received: {type(arches_)}" + ) + + if region_ is not None and not isinstance(region_, (bytes, str)): + raise Exception(f"Expected region_ to be a str, received: {type(region_)}") + + if root_storage_type_ is not None and not isinstance( + root_storage_type_, (bytes, str) + ): + raise Exception( + f"Expected root_storage_type_ to be a str, received: {type(root_storage_type_)}" + ) + + if stream_ is not None and not isinstance(stream_, (bytes, str)): + raise Exception(f"Expected stream_ to be a str, received: {type(stream_)}") + + if versions_ is not None and not isinstance(versions_, (bytes, str, list)): + raise Exception( + f"Expected versions_ to be a Sequence, received: {type(versions_)}" + ) + + if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): + raise Exception( + f"Expected virt_type_ to be a str, received: {type(virt_type_)}" + ) + + self.arches = arches_ + self.region = region_ + self.root_storage_type = root_storage_type_ + self.stream = stream_ + self.versions = versions_ + self.virt_type = virt_type_ + self.unknown_fields = unknown_fields + + +class ImportStorageDetails(Type): + _toSchema = {"storage_tag": "storage-tag"} + _toPy = {"storage-tag": "storage_tag"} + + def __init__(self, storage_tag=None, **unknown_fields): + """storage_tag : str""" + storage_tag_ = storage_tag + + # Validate arguments against known Juju API types. + if storage_tag_ is not None and not isinstance(storage_tag_, (bytes, str)): + raise Exception( + f"Expected storage_tag_ to be a str, received: {type(storage_tag_)}" + ) + + self.storage_tag = storage_tag_ + self.unknown_fields = unknown_fields + + +class ImportStorageParams(Type): + _toSchema = { + "kind": "kind", + "pool": "pool", + "provider_id": "provider-id", + "storage_name": "storage-name", + } + _toPy = { + "kind": "kind", + "pool": "pool", + "provider-id": "provider_id", + "storage-name": "storage_name", + } + + def __init__( + self, + kind=None, + pool=None, + provider_id=None, + storage_name=None, + **unknown_fields, + ): + """Kind : int + pool : str + provider_id : str + storage_name : str + """ + kind_ = kind + pool_ = pool + provider_id_ = provider_id + storage_name_ = storage_name + + # Validate arguments against known Juju API types. + if kind_ is not None and not isinstance(kind_, int): + raise Exception(f"Expected kind_ to be a int, received: {type(kind_)}") + + if pool_ is not None and not isinstance(pool_, (bytes, str)): + raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if storage_name_ is not None and not isinstance(storage_name_, (bytes, str)): + raise Exception( + f"Expected storage_name_ to be a str, received: {type(storage_name_)}" + ) + + self.kind = kind_ + self.pool = pool_ + self.provider_id = provider_id_ + self.storage_name = storage_name_ + self.unknown_fields = unknown_fields + + +class ImportStorageResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : ImportStorageDetails + """ + error_ = Error.from_json(error) if error else None + result_ = ImportStorageDetails.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance( + result_, (dict, ImportStorageDetails) + ): + raise Exception( + f"Expected result_ to be a ImportStorageDetails, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class ImportStorageResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ImportStorageResult]""" + results_ = [ImportStorageResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class InitiateMigrationArgs(Type): + _toSchema = {"specs": "specs"} + _toPy = {"specs": "specs"} + + def __init__(self, specs=None, **unknown_fields): + """Specs : typing.Sequence[~MigrationSpec]""" + specs_ = [MigrationSpec.from_json(o) for o in specs or []] + + # Validate arguments against known Juju API types. + if specs_ is not None and not isinstance(specs_, (bytes, str, list)): + raise Exception( + f"Expected specs_ to be a Sequence, received: {type(specs_)}" + ) + + self.specs = specs_ + self.unknown_fields = unknown_fields + + +class InitiateMigrationResult(Type): + _toSchema = { + "error": "error", + "migration_id": "migration-id", + "model_tag": "model-tag", + } + _toPy = {"error": "error", "migration-id": "migration_id", "model-tag": "model_tag"} + + def __init__(self, error=None, migration_id=None, model_tag=None, **unknown_fields): + """Error : Error + migration_id : str + model_tag : str + """ + error_ = Error.from_json(error) if error else None + migration_id_ = migration_id + model_tag_ = model_tag + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if migration_id_ is not None and not isinstance(migration_id_, (bytes, str)): + raise Exception( + f"Expected migration_id_ to be a str, received: {type(migration_id_)}" + ) + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + self.error = error_ + self.migration_id = migration_id_ + self.model_tag = model_tag_ + self.unknown_fields = unknown_fields + + +class InitiateMigrationResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~InitiateMigrationResult]""" + results_ = [InitiateMigrationResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class InstanceType(Type): + _toSchema = { + "arches": "arches", + "cost": "cost", + "cpu_cores": "cpu-cores", + "memory": "memory", + "name": "name", + "root_disk": "root-disk", + "virt_type": "virt-type", + } + _toPy = { + "arches": "arches", + "cost": "cost", + "cpu-cores": "cpu_cores", + "memory": "memory", + "name": "name", + "root-disk": "root_disk", + "virt-type": "virt_type", + } + + def __init__( + self, + arches=None, + cost=None, + cpu_cores=None, + memory=None, + name=None, + root_disk=None, + virt_type=None, + **unknown_fields, + ): + """Arches : typing.Sequence[str] + cost : int + cpu_cores : int + memory : int + name : str + root_disk : int + virt_type : str + """ + arches_ = arches + cost_ = cost + cpu_cores_ = cpu_cores + memory_ = memory + name_ = name + root_disk_ = root_disk + virt_type_ = virt_type + + # Validate arguments against known Juju API types. + if arches_ is not None and not isinstance(arches_, (bytes, str, list)): + raise Exception( + f"Expected arches_ to be a Sequence, received: {type(arches_)}" + ) + + if cost_ is not None and not isinstance(cost_, int): + raise Exception(f"Expected cost_ to be a int, received: {type(cost_)}") + + if cpu_cores_ is not None and not isinstance(cpu_cores_, int): + raise Exception( + f"Expected cpu_cores_ to be a int, received: {type(cpu_cores_)}" + ) + + if memory_ is not None and not isinstance(memory_, int): + raise Exception(f"Expected memory_ to be a int, received: {type(memory_)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if root_disk_ is not None and not isinstance(root_disk_, int): + raise Exception( + f"Expected root_disk_ to be a int, received: {type(root_disk_)}" + ) + + if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): + raise Exception( + f"Expected virt_type_ to be a str, received: {type(virt_type_)}" + ) + + self.arches = arches_ + self.cost = cost_ + self.cpu_cores = cpu_cores_ + self.memory = memory_ + self.name = name_ + self.root_disk = root_disk_ + self.virt_type = virt_type_ + self.unknown_fields = unknown_fields + + +class InstanceTypesResult(Type): + _toSchema = { + "cost_currency": "cost-currency", + "cost_divisor": "cost-divisor", + "cost_unit": "cost-unit", + "error": "error", + "instance_types": "instance-types", + } + _toPy = { + "cost-currency": "cost_currency", + "cost-divisor": "cost_divisor", + "cost-unit": "cost_unit", + "error": "error", + "instance-types": "instance_types", + } + + def __init__( + self, + cost_currency=None, + cost_divisor=None, + cost_unit=None, + error=None, + instance_types=None, + **unknown_fields, + ): + """cost_currency : str + cost_divisor : int + cost_unit : str + error : Error + instance_types : typing.Sequence[~InstanceType] + """ + cost_currency_ = cost_currency + cost_divisor_ = cost_divisor + cost_unit_ = cost_unit + error_ = Error.from_json(error) if error else None + instance_types_ = [InstanceType.from_json(o) for o in instance_types or []] + + # Validate arguments against known Juju API types. + if cost_currency_ is not None and not isinstance(cost_currency_, (bytes, str)): + raise Exception( + f"Expected cost_currency_ to be a str, received: {type(cost_currency_)}" + ) + + if cost_divisor_ is not None and not isinstance(cost_divisor_, int): + raise Exception( + f"Expected cost_divisor_ to be a int, received: {type(cost_divisor_)}" + ) + + if cost_unit_ is not None and not isinstance(cost_unit_, (bytes, str)): + raise Exception( + f"Expected cost_unit_ to be a str, received: {type(cost_unit_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if instance_types_ is not None and not isinstance( + instance_types_, (bytes, str, list) + ): + raise Exception( + f"Expected instance_types_ to be a Sequence, received: {type(instance_types_)}" + ) + + self.cost_currency = cost_currency_ + self.cost_divisor = cost_divisor_ + self.cost_unit = cost_unit_ + self.error = error_ + self.instance_types = instance_types_ + self.unknown_fields = unknown_fields + + +class InstanceTypesResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~InstanceTypesResult]""" + results_ = [InstanceTypesResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class IntResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : int + """ + error_ = Error.from_json(error) if error else None + result_ = result + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, int): + raise Exception(f"Expected result_ to be a int, received: {type(result_)}") + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class InvalidateCredentialArg(Type): + _toSchema = {"reason": "reason"} + _toPy = {"reason": "reason"} + + def __init__(self, reason=None, **unknown_fields): + """Reason : str""" + reason_ = reason + + # Validate arguments against known Juju API types. + if reason_ is not None and not isinstance(reason_, (bytes, str)): + raise Exception(f"Expected reason_ to be a str, received: {type(reason_)}") + + self.reason = reason_ + self.unknown_fields = unknown_fields + + +class IsMeteredResult(Type): + _toSchema = {"metered": "metered"} + _toPy = {"metered": "metered"} + + def __init__(self, metered=None, **unknown_fields): + """Metered : bool""" + metered_ = metered + + # Validate arguments against known Juju API types. + if metered_ is not None and not isinstance(metered_, bool): + raise Exception( + f"Expected metered_ to be a bool, received: {type(metered_)}" + ) + + self.metered = metered_ + self.unknown_fields = unknown_fields + + +class LXDProfile(Type): + _toSchema = {"config": "config", "description": "description", "devices": "devices"} + _toPy = {"config": "config", "description": "description", "devices": "devices"} + + def __init__(self, config=None, description=None, devices=None, **unknown_fields): + """Config : typing.Mapping[str, str] + description : str + devices : typing.Mapping[str, typing.Any] + """ + config_ = config + description_ = description + devices_ = devices + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if devices_ is not None and not isinstance(devices_, dict): + raise Exception( + f"Expected devices_ to be a Mapping, received: {type(devices_)}" + ) + + self.config = config_ + self.description = description_ + self.devices = devices_ + self.unknown_fields = unknown_fields + + +class ListCloudImageMetadataResult(Type): + _toSchema = {"result": "result"} + _toPy = {"result": "result"} + + def __init__(self, result=None, **unknown_fields): + """Result : typing.Sequence[~CloudImageMetadata]""" + result_ = [CloudImageMetadata.from_json(o) for o in result or []] + + # Validate arguments against known Juju API types. + if result_ is not None and not isinstance(result_, (bytes, str, list)): + raise Exception( + f"Expected result_ to be a Sequence, received: {type(result_)}" + ) + + self.result = result_ + self.unknown_fields = unknown_fields + + +class ListCloudInfo(Type): + _toSchema = {"clouddetails": "CloudDetails", "user_access": "user-access"} + _toPy = {"CloudDetails": "clouddetails", "user-access": "user_access"} + + def __init__(self, clouddetails=None, user_access=None, **unknown_fields): + """Clouddetails : CloudDetails + user_access : str + """ + clouddetails_ = CloudDetails.from_json(clouddetails) if clouddetails else None + user_access_ = user_access + + # Validate arguments against known Juju API types. + if clouddetails_ is not None and not isinstance( + clouddetails_, (dict, CloudDetails) + ): + raise Exception( + f"Expected clouddetails_ to be a CloudDetails, received: {type(clouddetails_)}" + ) + + if user_access_ is not None and not isinstance(user_access_, (bytes, str)): + raise Exception( + f"Expected user_access_ to be a str, received: {type(user_access_)}" + ) + + self.clouddetails = clouddetails_ + self.user_access = user_access_ + self.unknown_fields = unknown_fields + + +class ListCloudInfoResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : ListCloudInfo + """ + error_ = Error.from_json(error) if error else None + result_ = ListCloudInfo.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, ListCloudInfo)): + raise Exception( + f"Expected result_ to be a ListCloudInfo, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class ListCloudInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ListCloudInfoResult]""" + results_ = [ListCloudInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ListCloudsRequest(Type): + _toSchema = {"all_": "all", "user_tag": "user-tag"} + _toPy = {"all": "all_", "user-tag": "user_tag"} + + def __init__(self, all_=None, user_tag=None, **unknown_fields): + """all_ : bool + user_tag : str + """ + all__ = all_ + user_tag_ = user_tag + + # Validate arguments against known Juju API types. + if all__ is not None and not isinstance(all__, bool): + raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") + + if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): + raise Exception( + f"Expected user_tag_ to be a str, received: {type(user_tag_)}" + ) + + self.all_ = all__ + self.user_tag = user_tag_ + self.unknown_fields = unknown_fields + + +class ListFirewallRulesResults(Type): + _toSchema = {"rules": "Rules"} + _toPy = {"Rules": "rules"} + + def __init__(self, rules=None, **unknown_fields): + """Rules : typing.Sequence[~FirewallRule]""" + rules_ = [FirewallRule.from_json(o) for o in rules or []] + + # Validate arguments against known Juju API types. + if rules_ is not None and not isinstance(rules_, (bytes, str, list)): + raise Exception( + f"Expected rules_ to be a Sequence, received: {type(rules_)}" + ) + + self.rules = rules_ + self.unknown_fields = unknown_fields + + +class ListResourcesArgs(Type): + _toSchema = {"entities": "entities"} + _toPy = {"entities": "entities"} + + def __init__(self, entities=None, **unknown_fields): + """Entities : typing.Sequence[~Entity]""" + entities_ = [Entity.from_json(o) for o in entities or []] + + # Validate arguments against known Juju API types. + if entities_ is not None and not isinstance(entities_, (bytes, str, list)): + raise Exception( + f"Expected entities_ to be a Sequence, received: {type(entities_)}" + ) + + self.entities = entities_ + self.unknown_fields = unknown_fields + + +class ListSSHKeys(Type): + _toSchema = {"entities": "entities", "mode": "mode"} + _toPy = {"entities": "entities", "mode": "mode"} + + def __init__(self, entities=None, mode=None, **unknown_fields): + """Entities : Entities + mode : bool + """ + entities_ = Entities.from_json(entities) if entities else None + mode_ = mode + + # Validate arguments against known Juju API types. + if entities_ is not None and not isinstance(entities_, (dict, Entities)): + raise Exception( + f"Expected entities_ to be a Entities, received: {type(entities_)}" + ) + + if mode_ is not None and not isinstance(mode_, bool): + raise Exception(f"Expected mode_ to be a bool, received: {type(mode_)}") + + self.entities = entities_ + self.mode = mode_ + self.unknown_fields = unknown_fields + + +class ListSecretBackendsArgs(Type): + _toSchema = {"names": "names", "reveal": "reveal"} + _toPy = {"names": "names", "reveal": "reveal"} + + def __init__(self, names=None, reveal=None, **unknown_fields): + """Names : typing.Sequence[str] + reveal : bool + """ + names_ = names + reveal_ = reveal + + # Validate arguments against known Juju API types. + if names_ is not None and not isinstance(names_, (bytes, str, list)): + raise Exception( + f"Expected names_ to be a Sequence, received: {type(names_)}" + ) + + if reveal_ is not None and not isinstance(reveal_, bool): + raise Exception(f"Expected reveal_ to be a bool, received: {type(reveal_)}") + + self.names = names_ + self.reveal = reveal_ + self.unknown_fields = unknown_fields + + +class ListSecretBackendsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~SecretBackendResult]""" + results_ = [SecretBackendResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ListSecretResult(Type): + _toSchema = { + "access": "access", + "create_time": "create-time", + "description": "description", + "label": "label", + "latest_expire_time": "latest-expire-time", + "latest_revision": "latest-revision", + "latest_revision_checksum": "latest-revision-checksum", + "next_rotate_time": "next-rotate-time", + "owner_tag": "owner-tag", + "revisions": "revisions", + "rotate_policy": "rotate-policy", + "update_time": "update-time", + "uri": "uri", + "value": "value", + "version": "version", + } + _toPy = { + "access": "access", + "create-time": "create_time", + "description": "description", + "label": "label", + "latest-expire-time": "latest_expire_time", + "latest-revision": "latest_revision", + "latest-revision-checksum": "latest_revision_checksum", + "next-rotate-time": "next_rotate_time", + "owner-tag": "owner_tag", + "revisions": "revisions", + "rotate-policy": "rotate_policy", + "update-time": "update_time", + "uri": "uri", + "value": "value", + "version": "version", + } + + def __init__( + self, + access=None, + create_time=None, + description=None, + label=None, + latest_expire_time=None, + latest_revision=None, + latest_revision_checksum=None, + next_rotate_time=None, + owner_tag=None, + revisions=None, + rotate_policy=None, + update_time=None, + uri=None, + value=None, + version=None, + **unknown_fields, + ): + """Access : typing.Sequence[~AccessInfo] + create_time : str + description : str + label : str + latest_expire_time : str + latest_revision : int + latest_revision_checksum : str + next_rotate_time : str + owner_tag : str + revisions : typing.Sequence[~SecretRevision] + rotate_policy : str + update_time : str + uri : str + value : SecretValueResult + version : int + """ + access_ = [AccessInfo.from_json(o) for o in access or []] + create_time_ = create_time + description_ = description + label_ = label + latest_expire_time_ = latest_expire_time + latest_revision_ = latest_revision + latest_revision_checksum_ = latest_revision_checksum + next_rotate_time_ = next_rotate_time + owner_tag_ = owner_tag + revisions_ = [SecretRevision.from_json(o) for o in revisions or []] + rotate_policy_ = rotate_policy + update_time_ = update_time + uri_ = uri + value_ = SecretValueResult.from_json(value) if value else None + version_ = version + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str, list)): + raise Exception( + f"Expected access_ to be a Sequence, received: {type(access_)}" + ) + + if create_time_ is not None and not isinstance(create_time_, (bytes, str)): + raise Exception( + f"Expected create_time_ to be a str, received: {type(create_time_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if label_ is not None and not isinstance(label_, (bytes, str)): + raise Exception(f"Expected label_ to be a str, received: {type(label_)}") + + if latest_expire_time_ is not None and not isinstance( + latest_expire_time_, (bytes, str) + ): + raise Exception( + f"Expected latest_expire_time_ to be a str, received: {type(latest_expire_time_)}" + ) + + if latest_revision_ is not None and not isinstance(latest_revision_, int): + raise Exception( + f"Expected latest_revision_ to be a int, received: {type(latest_revision_)}" + ) + + if latest_revision_checksum_ is not None and not isinstance( + latest_revision_checksum_, (bytes, str) + ): + raise Exception( + f"Expected latest_revision_checksum_ to be a str, received: {type(latest_revision_checksum_)}" + ) + + if next_rotate_time_ is not None and not isinstance( + next_rotate_time_, (bytes, str) + ): + raise Exception( + f"Expected next_rotate_time_ to be a str, received: {type(next_rotate_time_)}" + ) + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + if revisions_ is not None and not isinstance(revisions_, (bytes, str, list)): + raise Exception( + f"Expected revisions_ to be a Sequence, received: {type(revisions_)}" + ) + + if rotate_policy_ is not None and not isinstance(rotate_policy_, (bytes, str)): + raise Exception( + f"Expected rotate_policy_ to be a str, received: {type(rotate_policy_)}" + ) + + if update_time_ is not None and not isinstance(update_time_, (bytes, str)): + raise Exception( + f"Expected update_time_ to be a str, received: {type(update_time_)}" + ) + + if uri_ is not None and not isinstance(uri_, (bytes, str)): + raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") + + if value_ is not None and not isinstance(value_, (dict, SecretValueResult)): + raise Exception( + f"Expected value_ to be a SecretValueResult, received: {type(value_)}" + ) + + if version_ is not None and not isinstance(version_, int): + raise Exception( + f"Expected version_ to be a int, received: {type(version_)}" + ) + + self.access = access_ + self.create_time = create_time_ + self.description = description_ + self.label = label_ + self.latest_expire_time = latest_expire_time_ + self.latest_revision = latest_revision_ + self.latest_revision_checksum = latest_revision_checksum_ + self.next_rotate_time = next_rotate_time_ + self.owner_tag = owner_tag_ + self.revisions = revisions_ + self.rotate_policy = rotate_policy_ + self.update_time = update_time_ + self.uri = uri_ + self.value = value_ + self.version = version_ + self.unknown_fields = unknown_fields + + +class ListSecretResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ListSecretResult]""" + results_ = [ListSecretResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ListSecretsArgs(Type): + _toSchema = {"filter_": "filter", "show_secrets": "show-secrets"} + _toPy = {"filter": "filter_", "show-secrets": "show_secrets"} + + def __init__(self, filter_=None, show_secrets=None, **unknown_fields): + """filter_ : SecretsFilter + show_secrets : bool + """ + filter__ = SecretsFilter.from_json(filter_) if filter_ else None + show_secrets_ = show_secrets + + # Validate arguments against known Juju API types. + if filter__ is not None and not isinstance(filter__, (dict, SecretsFilter)): + raise Exception( + f"Expected filter__ to be a SecretsFilter, received: {type(filter__)}" + ) + + if show_secrets_ is not None and not isinstance(show_secrets_, bool): + raise Exception( + f"Expected show_secrets_ to be a bool, received: {type(show_secrets_)}" + ) + + self.filter_ = filter__ + self.show_secrets = show_secrets_ + self.unknown_fields = unknown_fields + + +class ListSpacesResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~Space]""" + results_ = [Space.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ListSubnetsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~Subnet]""" + results_ = [Subnet.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class LoginRequest(Type): + _toSchema = { + "auth_tag": "auth-tag", + "bakery_version": "bakery-version", + "cli_args": "cli-args", + "client_version": "client-version", + "credentials": "credentials", + "macaroons": "macaroons", + "nonce": "nonce", + "token": "token", + "user_data": "user-data", + } + _toPy = { + "auth-tag": "auth_tag", + "bakery-version": "bakery_version", + "cli-args": "cli_args", + "client-version": "client_version", + "credentials": "credentials", + "macaroons": "macaroons", + "nonce": "nonce", + "token": "token", + "user-data": "user_data", + } + + def __init__( + self, + auth_tag=None, + bakery_version=None, + cli_args=None, + client_version=None, + credentials=None, + macaroons=None, + nonce=None, + token=None, + user_data=None, + **unknown_fields, + ): + """auth_tag : str + bakery_version : int + cli_args : str + client_version : str + credentials : str + macaroons : typing.Sequence[~Macaroon] + nonce : str + token : str + user_data : str + """ + auth_tag_ = auth_tag + bakery_version_ = bakery_version + cli_args_ = cli_args + client_version_ = client_version + credentials_ = credentials + macaroons_ = [Macaroon.from_json(o) for o in macaroons or []] + nonce_ = nonce + token_ = token + user_data_ = user_data + + # Validate arguments against known Juju API types. + if auth_tag_ is not None and not isinstance(auth_tag_, (bytes, str)): + raise Exception( + f"Expected auth_tag_ to be a str, received: {type(auth_tag_)}" + ) + + if bakery_version_ is not None and not isinstance(bakery_version_, int): + raise Exception( + f"Expected bakery_version_ to be a int, received: {type(bakery_version_)}" + ) + + if cli_args_ is not None and not isinstance(cli_args_, (bytes, str)): + raise Exception( + f"Expected cli_args_ to be a str, received: {type(cli_args_)}" + ) + + if client_version_ is not None and not isinstance( + client_version_, (bytes, str) + ): + raise Exception( + f"Expected client_version_ to be a str, received: {type(client_version_)}" + ) + + if credentials_ is not None and not isinstance(credentials_, (bytes, str)): + raise Exception( + f"Expected credentials_ to be a str, received: {type(credentials_)}" + ) + + if macaroons_ is not None and not isinstance(macaroons_, (bytes, str, list)): + raise Exception( + f"Expected macaroons_ to be a Sequence, received: {type(macaroons_)}" + ) + + if nonce_ is not None and not isinstance(nonce_, (bytes, str)): + raise Exception(f"Expected nonce_ to be a str, received: {type(nonce_)}") + + if token_ is not None and not isinstance(token_, (bytes, str)): + raise Exception(f"Expected token_ to be a str, received: {type(token_)}") + + if user_data_ is not None and not isinstance(user_data_, (bytes, str)): + raise Exception( + f"Expected user_data_ to be a str, received: {type(user_data_)}" + ) + + self.auth_tag = auth_tag_ + self.bakery_version = bakery_version_ + self.cli_args = cli_args_ + self.client_version = client_version_ + self.credentials = credentials_ + self.macaroons = macaroons_ + self.nonce = nonce_ + self.token = token_ + self.user_data = user_data_ + self.unknown_fields = unknown_fields + + +class LoginResult(Type): + _toSchema = { + "bakery_discharge_required": "bakery-discharge-required", + "controller_tag": "controller-tag", + "discharge_required": "discharge-required", + "discharge_required_error": "discharge-required-error", + "facades": "facades", + "model_tag": "model-tag", + "public_dns_name": "public-dns-name", + "server_version": "server-version", + "servers": "servers", + "user_info": "user-info", + } + _toPy = { + "bakery-discharge-required": "bakery_discharge_required", + "controller-tag": "controller_tag", + "discharge-required": "discharge_required", + "discharge-required-error": "discharge_required_error", + "facades": "facades", + "model-tag": "model_tag", + "public-dns-name": "public_dns_name", + "server-version": "server_version", + "servers": "servers", + "user-info": "user_info", + } + + def __init__( + self, + bakery_discharge_required=None, + controller_tag=None, + discharge_required=None, + discharge_required_error=None, + facades=None, + model_tag=None, + public_dns_name=None, + server_version=None, + servers=None, + user_info=None, + **unknown_fields, + ): + """bakery_discharge_required : Macaroon + controller_tag : str + discharge_required : Macaroon + discharge_required_error : str + facades : typing.Sequence[~FacadeVersions] + model_tag : str + public_dns_name : str + server_version : str + servers : typing.Sequence[~HostPort] + user_info : AuthUserInfo + """ + bakery_discharge_required_ = ( + Macaroon.from_json(bakery_discharge_required) + if bakery_discharge_required + else None + ) + controller_tag_ = controller_tag + discharge_required_ = ( + Macaroon.from_json(discharge_required) if discharge_required else None + ) + discharge_required_error_ = discharge_required_error + facades_ = [FacadeVersions.from_json(o) for o in facades or []] + model_tag_ = model_tag + public_dns_name_ = public_dns_name + server_version_ = server_version + servers_ = [HostPort.from_json(o) for o in servers or []] + user_info_ = AuthUserInfo.from_json(user_info) if user_info else None + + # Validate arguments against known Juju API types. + if bakery_discharge_required_ is not None and not isinstance( + bakery_discharge_required_, (dict, Macaroon) + ): + raise Exception( + f"Expected bakery_discharge_required_ to be a Macaroon, received: {type(bakery_discharge_required_)}" + ) + + if controller_tag_ is not None and not isinstance( + controller_tag_, (bytes, str) + ): + raise Exception( + f"Expected controller_tag_ to be a str, received: {type(controller_tag_)}" + ) + + if discharge_required_ is not None and not isinstance( + discharge_required_, (dict, Macaroon) + ): + raise Exception( + f"Expected discharge_required_ to be a Macaroon, received: {type(discharge_required_)}" + ) + + if discharge_required_error_ is not None and not isinstance( + discharge_required_error_, (bytes, str) + ): + raise Exception( + f"Expected discharge_required_error_ to be a str, received: {type(discharge_required_error_)}" + ) + + if facades_ is not None and not isinstance(facades_, (bytes, str, list)): + raise Exception( + f"Expected facades_ to be a Sequence, received: {type(facades_)}" + ) + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + if public_dns_name_ is not None and not isinstance( + public_dns_name_, (bytes, str) + ): + raise Exception( + f"Expected public_dns_name_ to be a str, received: {type(public_dns_name_)}" + ) + + if server_version_ is not None and not isinstance( + server_version_, (bytes, str) + ): + raise Exception( + f"Expected server_version_ to be a str, received: {type(server_version_)}" + ) + + if servers_ is not None and not isinstance(servers_, (bytes, str, list)): + raise Exception( + f"Expected servers_ to be a Sequence, received: {type(servers_)}" + ) + + if user_info_ is not None and not isinstance(user_info_, (dict, AuthUserInfo)): + raise Exception( + f"Expected user_info_ to be a AuthUserInfo, received: {type(user_info_)}" + ) + + self.bakery_discharge_required = bakery_discharge_required_ + self.controller_tag = controller_tag_ + self.discharge_required = discharge_required_ + self.discharge_required_error = discharge_required_error_ + self.facades = facades_ + self.model_tag = model_tag_ + self.public_dns_name = public_dns_name_ + self.server_version = server_version_ + self.servers = servers_ + self.user_info = user_info_ + self.unknown_fields = unknown_fields + + +class Macaroon(Type): + _toSchema = {} + _toPy = {} + + def __init__(self, **unknown_fields): + """ """ + self.unknown_fields = unknown_fields + + +class MachineHardware(Type): + _toSchema = { + "arch": "arch", + "availability_zone": "availability-zone", + "cores": "cores", + "cpu_power": "cpu-power", + "mem": "mem", + "root_disk": "root-disk", + "tags": "tags", + "virt_type": "virt-type", + } + _toPy = { + "arch": "arch", + "availability-zone": "availability_zone", + "cores": "cores", + "cpu-power": "cpu_power", + "mem": "mem", + "root-disk": "root_disk", + "tags": "tags", + "virt-type": "virt_type", + } + + def __init__( + self, + arch=None, + availability_zone=None, + cores=None, + cpu_power=None, + mem=None, + root_disk=None, + tags=None, + virt_type=None, + **unknown_fields, + ): + """Arch : str + availability_zone : str + cores : int + cpu_power : int + mem : int + root_disk : int + tags : typing.Sequence[str] + virt_type : str + """ + arch_ = arch + availability_zone_ = availability_zone + cores_ = cores + cpu_power_ = cpu_power + mem_ = mem + root_disk_ = root_disk + tags_ = tags + virt_type_ = virt_type + + # Validate arguments against known Juju API types. + if arch_ is not None and not isinstance(arch_, (bytes, str)): + raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") + + if availability_zone_ is not None and not isinstance( + availability_zone_, (bytes, str) + ): + raise Exception( + f"Expected availability_zone_ to be a str, received: {type(availability_zone_)}" + ) + + if cores_ is not None and not isinstance(cores_, int): + raise Exception(f"Expected cores_ to be a int, received: {type(cores_)}") + + if cpu_power_ is not None and not isinstance(cpu_power_, int): + raise Exception( + f"Expected cpu_power_ to be a int, received: {type(cpu_power_)}" + ) + + if mem_ is not None and not isinstance(mem_, int): + raise Exception(f"Expected mem_ to be a int, received: {type(mem_)}") + + if root_disk_ is not None and not isinstance(root_disk_, int): + raise Exception( + f"Expected root_disk_ to be a int, received: {type(root_disk_)}" + ) + + if tags_ is not None and not isinstance(tags_, (bytes, str, list)): + raise Exception(f"Expected tags_ to be a Sequence, received: {type(tags_)}") + + if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): + raise Exception( + f"Expected virt_type_ to be a str, received: {type(virt_type_)}" + ) + + self.arch = arch_ + self.availability_zone = availability_zone_ + self.cores = cores_ + self.cpu_power = cpu_power_ + self.mem = mem_ + self.root_disk = root_disk_ + self.tags = tags_ + self.virt_type = virt_type_ + self.unknown_fields = unknown_fields + + +class MachineStatus(Type): + _toSchema = { + "agent_status": "agent-status", + "base": "base", + "constraints": "constraints", + "containers": "containers", + "display_name": "display-name", + "dns_name": "dns-name", + "hardware": "hardware", + "has_vote": "has-vote", + "hostname": "hostname", + "id_": "id", + "instance_id": "instance-id", + "instance_status": "instance-status", + "ip_addresses": "ip-addresses", + "jobs": "jobs", + "lxd_profiles": "lxd-profiles", + "modification_status": "modification-status", + "network_interfaces": "network-interfaces", + "primary_controller_machine": "primary-controller-machine", + "wants_vote": "wants-vote", + } + _toPy = { + "agent-status": "agent_status", + "base": "base", + "constraints": "constraints", + "containers": "containers", + "display-name": "display_name", + "dns-name": "dns_name", + "hardware": "hardware", + "has-vote": "has_vote", + "hostname": "hostname", + "id": "id_", + "instance-id": "instance_id", + "instance-status": "instance_status", + "ip-addresses": "ip_addresses", + "jobs": "jobs", + "lxd-profiles": "lxd_profiles", + "modification-status": "modification_status", + "network-interfaces": "network_interfaces", + "primary-controller-machine": "primary_controller_machine", + "wants-vote": "wants_vote", + } + + def __init__( + self, + agent_status=None, + base=None, + constraints=None, + containers=None, + display_name=None, + dns_name=None, + hardware=None, + has_vote=None, + hostname=None, + id_=None, + instance_id=None, + instance_status=None, + ip_addresses=None, + jobs=None, + lxd_profiles=None, + modification_status=None, + network_interfaces=None, + primary_controller_machine=None, + wants_vote=None, + **unknown_fields, + ): + """agent_status : DetailedStatus + base : Base + constraints : str + containers : typing.Mapping[str, ~MachineStatus] + display_name : str + dns_name : str + hardware : str + has_vote : bool + hostname : str + id_ : str + instance_id : str + instance_status : DetailedStatus + ip_addresses : typing.Sequence[str] + jobs : typing.Sequence[str] + lxd_profiles : typing.Mapping[str, ~LXDProfile] + modification_status : DetailedStatus + network_interfaces : typing.Mapping[str, ~NetworkInterface] + primary_controller_machine : bool + wants_vote : bool + """ + agent_status_ = DetailedStatus.from_json(agent_status) if agent_status else None + base_ = Base.from_json(base) if base else None + constraints_ = constraints + containers_ = { + k: MachineStatus.from_json(v) for k, v in (containers or dict()).items() + } + display_name_ = display_name + dns_name_ = dns_name + hardware_ = hardware + has_vote_ = has_vote + hostname_ = hostname + id__ = id_ + instance_id_ = instance_id + instance_status_ = ( + DetailedStatus.from_json(instance_status) if instance_status else None + ) + ip_addresses_ = ip_addresses + jobs_ = jobs + lxd_profiles_ = { + k: LXDProfile.from_json(v) for k, v in (lxd_profiles or dict()).items() + } + modification_status_ = ( + DetailedStatus.from_json(modification_status) + if modification_status + else None + ) + network_interfaces_ = { + k: NetworkInterface.from_json(v) + for k, v in (network_interfaces or dict()).items() + } + primary_controller_machine_ = primary_controller_machine + wants_vote_ = wants_vote + + # Validate arguments against known Juju API types. + if agent_status_ is not None and not isinstance( + agent_status_, (dict, DetailedStatus) + ): + raise Exception( + f"Expected agent_status_ to be a DetailedStatus, received: {type(agent_status_)}" + ) + + if base_ is not None and not isinstance(base_, (dict, Base)): + raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") + + if constraints_ is not None and not isinstance(constraints_, (bytes, str)): + raise Exception( + f"Expected constraints_ to be a str, received: {type(constraints_)}" + ) + + if containers_ is not None and not isinstance(containers_, dict): + raise Exception( + f"Expected containers_ to be a Mapping, received: {type(containers_)}" + ) + + if display_name_ is not None and not isinstance(display_name_, (bytes, str)): + raise Exception( + f"Expected display_name_ to be a str, received: {type(display_name_)}" + ) + + if dns_name_ is not None and not isinstance(dns_name_, (bytes, str)): + raise Exception( + f"Expected dns_name_ to be a str, received: {type(dns_name_)}" + ) + + if hardware_ is not None and not isinstance(hardware_, (bytes, str)): + raise Exception( + f"Expected hardware_ to be a str, received: {type(hardware_)}" + ) + + if has_vote_ is not None and not isinstance(has_vote_, bool): + raise Exception( + f"Expected has_vote_ to be a bool, received: {type(has_vote_)}" + ) + + if hostname_ is not None and not isinstance(hostname_, (bytes, str)): + raise Exception( + f"Expected hostname_ to be a str, received: {type(hostname_)}" + ) + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if instance_id_ is not None and not isinstance(instance_id_, (bytes, str)): + raise Exception( + f"Expected instance_id_ to be a str, received: {type(instance_id_)}" + ) + + if instance_status_ is not None and not isinstance( + instance_status_, (dict, DetailedStatus) + ): + raise Exception( + f"Expected instance_status_ to be a DetailedStatus, received: {type(instance_status_)}" + ) + + if ip_addresses_ is not None and not isinstance( + ip_addresses_, (bytes, str, list) + ): + raise Exception( + f"Expected ip_addresses_ to be a Sequence, received: {type(ip_addresses_)}" + ) + + if jobs_ is not None and not isinstance(jobs_, (bytes, str, list)): + raise Exception(f"Expected jobs_ to be a Sequence, received: {type(jobs_)}") + + if lxd_profiles_ is not None and not isinstance(lxd_profiles_, dict): + raise Exception( + f"Expected lxd_profiles_ to be a Mapping, received: {type(lxd_profiles_)}" + ) + + if modification_status_ is not None and not isinstance( + modification_status_, (dict, DetailedStatus) + ): + raise Exception( + f"Expected modification_status_ to be a DetailedStatus, received: {type(modification_status_)}" + ) + + if network_interfaces_ is not None and not isinstance( + network_interfaces_, dict + ): + raise Exception( + f"Expected network_interfaces_ to be a Mapping, received: {type(network_interfaces_)}" + ) + + if primary_controller_machine_ is not None and not isinstance( + primary_controller_machine_, bool + ): + raise Exception( + f"Expected primary_controller_machine_ to be a bool, received: {type(primary_controller_machine_)}" + ) + + if wants_vote_ is not None and not isinstance(wants_vote_, bool): + raise Exception( + f"Expected wants_vote_ to be a bool, received: {type(wants_vote_)}" + ) + + self.agent_status = agent_status_ + self.base = base_ + self.constraints = constraints_ + self.containers = containers_ + self.display_name = display_name_ + self.dns_name = dns_name_ + self.hardware = hardware_ + self.has_vote = has_vote_ + self.hostname = hostname_ + self.id_ = id__ + self.instance_id = instance_id_ + self.instance_status = instance_status_ + self.ip_addresses = ip_addresses_ + self.jobs = jobs_ + self.lxd_profiles = lxd_profiles_ + self.modification_status = modification_status_ + self.network_interfaces = network_interfaces_ + self.primary_controller_machine = primary_controller_machine_ + self.wants_vote = wants_vote_ + self.unknown_fields = unknown_fields + + +class MapResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : typing.Mapping[str, typing.Any] + """ + error_ = Error.from_json(error) if error else None + result_ = result + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, dict): + raise Exception( + f"Expected result_ to be a Mapping, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class MapResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~MapResult]""" + results_ = [MapResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class MetadataImageIds(Type): + _toSchema = {"image_ids": "image-ids"} + _toPy = {"image-ids": "image_ids"} + + def __init__(self, image_ids=None, **unknown_fields): + """image_ids : typing.Sequence[str]""" + image_ids_ = image_ids + + # Validate arguments against known Juju API types. + if image_ids_ is not None and not isinstance(image_ids_, (bytes, str, list)): + raise Exception( + f"Expected image_ids_ to be a Sequence, received: {type(image_ids_)}" + ) + + self.image_ids = image_ids_ + self.unknown_fields = unknown_fields + + +class MetadataSaveParams(Type): + _toSchema = {"metadata": "metadata"} + _toPy = {"metadata": "metadata"} + + def __init__(self, metadata=None, **unknown_fields): + """Metadata : typing.Sequence[~CloudImageMetadataList]""" + metadata_ = [CloudImageMetadataList.from_json(o) for o in metadata or []] + + # Validate arguments against known Juju API types. + if metadata_ is not None and not isinstance(metadata_, (bytes, str, list)): + raise Exception( + f"Expected metadata_ to be a Sequence, received: {type(metadata_)}" + ) + + self.metadata = metadata_ + self.unknown_fields = unknown_fields + + +class MeterStatus(Type): + _toSchema = {"color": "color", "message": "message"} + _toPy = {"color": "color", "message": "message"} + + def __init__(self, color=None, message=None, **unknown_fields): + """Color : str + message : str + """ + color_ = color + message_ = message + + # Validate arguments against known Juju API types. + if color_ is not None and not isinstance(color_, (bytes, str)): + raise Exception(f"Expected color_ to be a str, received: {type(color_)}") + + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + self.color = color_ + self.message = message_ + self.unknown_fields = unknown_fields + + +class MeterStatusParam(Type): + _toSchema = {"code": "code", "info": "info", "tag": "tag"} + _toPy = {"code": "code", "info": "info", "tag": "tag"} + + def __init__(self, code=None, info=None, tag=None, **unknown_fields): + """Code : str + info : str + tag : str + """ + code_ = code + info_ = info + tag_ = tag + + # Validate arguments against known Juju API types. + if code_ is not None and not isinstance(code_, (bytes, str)): + raise Exception(f"Expected code_ to be a str, received: {type(code_)}") + + if info_ is not None and not isinstance(info_, (bytes, str)): + raise Exception(f"Expected info_ to be a str, received: {type(info_)}") + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.code = code_ + self.info = info_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class MeterStatusParams(Type): + _toSchema = {"statues": "statues"} + _toPy = {"statues": "statues"} + + def __init__(self, statues=None, **unknown_fields): + """Statues : typing.Sequence[~MeterStatusParam]""" + statues_ = [MeterStatusParam.from_json(o) for o in statues or []] + + # Validate arguments against known Juju API types. + if statues_ is not None and not isinstance(statues_, (bytes, str, list)): + raise Exception( + f"Expected statues_ to be a Sequence, received: {type(statues_)}" + ) + + self.statues = statues_ + self.unknown_fields = unknown_fields + + +class MetricResult(Type): + _toSchema = { + "key": "key", + "labels": "labels", + "time": "time", + "unit": "unit", + "value": "value", + } + _toPy = { + "key": "key", + "labels": "labels", + "time": "time", + "unit": "unit", + "value": "value", + } + + def __init__( + self, key=None, labels=None, time=None, unit=None, value=None, **unknown_fields + ): + """Key : str + labels : typing.Mapping[str, str] + time : str + unit : str + value : str + """ + key_ = key + labels_ = labels + time_ = time + unit_ = unit + value_ = value + + # Validate arguments against known Juju API types. + if key_ is not None and not isinstance(key_, (bytes, str)): + raise Exception(f"Expected key_ to be a str, received: {type(key_)}") + + if labels_ is not None and not isinstance(labels_, dict): + raise Exception( + f"Expected labels_ to be a Mapping, received: {type(labels_)}" + ) + + if time_ is not None and not isinstance(time_, (bytes, str)): + raise Exception(f"Expected time_ to be a str, received: {type(time_)}") + + if unit_ is not None and not isinstance(unit_, (bytes, str)): + raise Exception(f"Expected unit_ to be a str, received: {type(unit_)}") + + if value_ is not None and not isinstance(value_, (bytes, str)): + raise Exception(f"Expected value_ to be a str, received: {type(value_)}") + + self.key = key_ + self.labels = labels_ + self.time = time_ + self.unit = unit_ + self.value = value_ + self.unknown_fields = unknown_fields + + +class MetricResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~EntityMetrics]""" + results_ = [EntityMetrics.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class MigrationSpec(Type): + _toSchema = {"model_tag": "model-tag", "target_info": "target-info"} + _toPy = {"model-tag": "model_tag", "target-info": "target_info"} + + def __init__(self, model_tag=None, target_info=None, **unknown_fields): + """model_tag : str + target_info : MigrationTargetInfo + """ + model_tag_ = model_tag + target_info_ = ( + MigrationTargetInfo.from_json(target_info) if target_info else None + ) + + # Validate arguments against known Juju API types. + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + if target_info_ is not None and not isinstance( + target_info_, (dict, MigrationTargetInfo) + ): + raise Exception( + f"Expected target_info_ to be a MigrationTargetInfo, received: {type(target_info_)}" + ) + + self.model_tag = model_tag_ + self.target_info = target_info_ + self.unknown_fields = unknown_fields + + +class MigrationTargetInfo(Type): + _toSchema = { + "addrs": "addrs", + "auth_tag": "auth-tag", + "ca_cert": "ca-cert", + "controller_alias": "controller-alias", + "controller_tag": "controller-tag", + "macaroons": "macaroons", + "password": "password", + } + _toPy = { + "addrs": "addrs", + "auth-tag": "auth_tag", + "ca-cert": "ca_cert", + "controller-alias": "controller_alias", + "controller-tag": "controller_tag", + "macaroons": "macaroons", + "password": "password", + } + + def __init__( + self, + addrs=None, + auth_tag=None, + ca_cert=None, + controller_alias=None, + controller_tag=None, + macaroons=None, + password=None, + **unknown_fields, + ): + """Addrs : typing.Sequence[str] + auth_tag : str + ca_cert : str + controller_alias : str + controller_tag : str + macaroons : str + password : str + """ + addrs_ = addrs + auth_tag_ = auth_tag + ca_cert_ = ca_cert + controller_alias_ = controller_alias + controller_tag_ = controller_tag + macaroons_ = macaroons + password_ = password + + # Validate arguments against known Juju API types. + if addrs_ is not None and not isinstance(addrs_, (bytes, str, list)): + raise Exception( + f"Expected addrs_ to be a Sequence, received: {type(addrs_)}" + ) + + if auth_tag_ is not None and not isinstance(auth_tag_, (bytes, str)): + raise Exception( + f"Expected auth_tag_ to be a str, received: {type(auth_tag_)}" + ) + + if ca_cert_ is not None and not isinstance(ca_cert_, (bytes, str)): + raise Exception( + f"Expected ca_cert_ to be a str, received: {type(ca_cert_)}" + ) + + if controller_alias_ is not None and not isinstance( + controller_alias_, (bytes, str) + ): + raise Exception( + f"Expected controller_alias_ to be a str, received: {type(controller_alias_)}" + ) + + if controller_tag_ is not None and not isinstance( + controller_tag_, (bytes, str) + ): + raise Exception( + f"Expected controller_tag_ to be a str, received: {type(controller_tag_)}" + ) + + if macaroons_ is not None and not isinstance(macaroons_, (bytes, str)): + raise Exception( + f"Expected macaroons_ to be a str, received: {type(macaroons_)}" + ) + + if password_ is not None and not isinstance(password_, (bytes, str)): + raise Exception( + f"Expected password_ to be a str, received: {type(password_)}" + ) + + self.addrs = addrs_ + self.auth_tag = auth_tag_ + self.ca_cert = ca_cert_ + self.controller_alias = controller_alias_ + self.controller_tag = controller_tag_ + self.macaroons = macaroons_ + self.password = password_ + self.unknown_fields = unknown_fields + + +class Model(Type): + _toSchema = { + "name": "name", + "owner_tag": "owner-tag", + "type_": "type", + "uuid": "uuid", + } + _toPy = {"name": "name", "owner-tag": "owner_tag", "type": "type_", "uuid": "uuid"} + + def __init__( + self, name=None, owner_tag=None, type_=None, uuid=None, **unknown_fields + ): + """Name : str + owner_tag : str + type_ : str + uuid : str + """ + name_ = name + owner_tag_ = owner_tag + type__ = type_ + uuid_ = uuid + + # Validate arguments against known Juju API types. + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + if uuid_ is not None and not isinstance(uuid_, (bytes, str)): + raise Exception(f"Expected uuid_ to be a str, received: {type(uuid_)}") + + self.name = name_ + self.owner_tag = owner_tag_ + self.type_ = type__ + self.uuid = uuid_ + self.unknown_fields = unknown_fields + + +class ModelAccess(Type): + _toSchema = {"access": "access", "model": "model"} + _toPy = {"access": "access", "model": "model"} + + def __init__(self, access=None, model=None, **unknown_fields): + """Access : str + model : str + """ + access_ = access + model_ = model + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if model_ is not None and not isinstance(model_, (bytes, str)): + raise Exception(f"Expected model_ to be a str, received: {type(model_)}") + + self.access = access_ + self.model = model_ + self.unknown_fields = unknown_fields + + +class ModelApplicationInfo(Type): + _toSchema = {"name": "name"} + _toPy = {"name": "name"} + + def __init__(self, name=None, **unknown_fields): + """Name : str""" + name_ = name + + # Validate arguments against known Juju API types. + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + self.name = name_ + self.unknown_fields = unknown_fields + + +class ModelBlockInfo(Type): + _toSchema = { + "blocks": "blocks", + "model_uuid": "model-uuid", + "name": "name", + "owner_tag": "owner-tag", + } + _toPy = { + "blocks": "blocks", + "model-uuid": "model_uuid", + "name": "name", + "owner-tag": "owner_tag", + } + + def __init__( + self, blocks=None, model_uuid=None, name=None, owner_tag=None, **unknown_fields + ): + """Blocks : typing.Sequence[str] + model_uuid : str + name : str + owner_tag : str + """ + blocks_ = blocks + model_uuid_ = model_uuid + name_ = name + owner_tag_ = owner_tag + + # Validate arguments against known Juju API types. + if blocks_ is not None and not isinstance(blocks_, (bytes, str, list)): + raise Exception( + f"Expected blocks_ to be a Sequence, received: {type(blocks_)}" + ) + + if model_uuid_ is not None and not isinstance(model_uuid_, (bytes, str)): + raise Exception( + f"Expected model_uuid_ to be a str, received: {type(model_uuid_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + self.blocks = blocks_ + self.model_uuid = model_uuid_ + self.name = name_ + self.owner_tag = owner_tag_ + self.unknown_fields = unknown_fields + + +class ModelBlockInfoList(Type): + _toSchema = {"models": "models"} + _toPy = {"models": "models"} + + def __init__(self, models=None, **unknown_fields): + """Models : typing.Sequence[~ModelBlockInfo]""" + models_ = [ModelBlockInfo.from_json(o) for o in models or []] + + # Validate arguments against known Juju API types. + if models_ is not None and not isinstance(models_, (bytes, str, list)): + raise Exception( + f"Expected models_ to be a Sequence, received: {type(models_)}" + ) + + self.models = models_ + self.unknown_fields = unknown_fields + + +class ModelConfigResults(Type): + _toSchema = {"config": "config"} + _toPy = {"config": "config"} + + def __init__(self, config=None, **unknown_fields): + """Config : typing.Mapping[str, ~ConfigValue]""" + config_ = {k: ConfigValue.from_json(v) for k, v in (config or dict()).items()} + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + self.config = config_ + self.unknown_fields = unknown_fields + + +class ModelCreateArgs(Type): + _toSchema = { + "cloud_tag": "cloud-tag", + "config": "config", + "credential": "credential", + "name": "name", + "owner_tag": "owner-tag", + "region": "region", + } + _toPy = { + "cloud-tag": "cloud_tag", + "config": "config", + "credential": "credential", + "name": "name", + "owner-tag": "owner_tag", + "region": "region", + } + + def __init__( + self, + cloud_tag=None, + config=None, + credential=None, + name=None, + owner_tag=None, + region=None, + **unknown_fields, + ): + """cloud_tag : str + config : typing.Mapping[str, typing.Any] + credential : str + name : str + owner_tag : str + region : str + """ + cloud_tag_ = cloud_tag + config_ = config + credential_ = credential + name_ = name + owner_tag_ = owner_tag + region_ = region + + # Validate arguments against known Juju API types. + if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): + raise Exception( + f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if credential_ is not None and not isinstance(credential_, (bytes, str)): + raise Exception( + f"Expected credential_ to be a str, received: {type(credential_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + if region_ is not None and not isinstance(region_, (bytes, str)): + raise Exception(f"Expected region_ to be a str, received: {type(region_)}") + + self.cloud_tag = cloud_tag_ + self.config = config_ + self.credential = credential_ + self.name = name_ + self.owner_tag = owner_tag_ + self.region = region_ + self.unknown_fields = unknown_fields + + +class ModelDefaultValues(Type): + _toSchema = { + "cloud_region": "cloud-region", + "cloud_tag": "cloud-tag", + "config": "config", + } + _toPy = { + "cloud-region": "cloud_region", + "cloud-tag": "cloud_tag", + "config": "config", + } + + def __init__( + self, cloud_region=None, cloud_tag=None, config=None, **unknown_fields + ): + """cloud_region : str + cloud_tag : str + config : typing.Mapping[str, typing.Any] + """ + cloud_region_ = cloud_region + cloud_tag_ = cloud_tag + config_ = config + + # Validate arguments against known Juju API types. + if cloud_region_ is not None and not isinstance(cloud_region_, (bytes, str)): + raise Exception( + f"Expected cloud_region_ to be a str, received: {type(cloud_region_)}" + ) + + if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): + raise Exception( + f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + self.cloud_region = cloud_region_ + self.cloud_tag = cloud_tag_ + self.config = config_ + self.unknown_fields = unknown_fields + + +class ModelDefaults(Type): + _toSchema = {"controller": "controller", "default": "default", "regions": "regions"} + _toPy = {"controller": "controller", "default": "default", "regions": "regions"} + + def __init__(self, controller=None, default=None, regions=None, **unknown_fields): + """Controller : Any + default : Any + regions : typing.Sequence[~RegionDefaults] + """ + controller_ = controller + default_ = default + regions_ = [RegionDefaults.from_json(o) for o in regions or []] + + # Validate arguments against known Juju API types. + if regions_ is not None and not isinstance(regions_, (bytes, str, list)): + raise Exception( + f"Expected regions_ to be a Sequence, received: {type(regions_)}" + ) + + self.controller = controller_ + self.default = default_ + self.regions = regions_ + self.unknown_fields = unknown_fields + + +class ModelDefaultsResult(Type): + _toSchema = {"config": "config", "error": "error"} + _toPy = {"config": "config", "error": "error"} + + def __init__(self, config=None, error=None, **unknown_fields): + """Config : typing.Mapping[str, ~ModelDefaults] + error : Error + """ + config_ = {k: ModelDefaults.from_json(v) for k, v in (config or dict()).items()} + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.config = config_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class ModelDefaultsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ModelDefaultsResult]""" + results_ = [ModelDefaultsResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ModelEntityCount(Type): + _toSchema = {"count": "count", "entity": "entity"} + _toPy = {"count": "count", "entity": "entity"} + + def __init__(self, count=None, entity=None, **unknown_fields): + """Count : int + entity : str + """ + count_ = count + entity_ = entity + + # Validate arguments against known Juju API types. + if count_ is not None and not isinstance(count_, int): + raise Exception(f"Expected count_ to be a int, received: {type(count_)}") + + if entity_ is not None and not isinstance(entity_, (bytes, str)): + raise Exception(f"Expected entity_ to be a str, received: {type(entity_)}") + + self.count = count_ + self.entity = entity_ + self.unknown_fields = unknown_fields + + +class ModelFilesystemInfo(Type): + _toSchema = { + "detachable": "detachable", + "id_": "id", + "message": "message", + "provider_id": "provider-id", + "status": "status", + } + _toPy = { + "detachable": "detachable", + "id": "id_", + "message": "message", + "provider-id": "provider_id", + "status": "status", + } + + def __init__( + self, + detachable=None, + id_=None, + message=None, + provider_id=None, + status=None, + **unknown_fields, + ): + """Detachable : bool + id_ : str + message : str + provider_id : str + status : str + """ + detachable_ = detachable + id__ = id_ + message_ = message + provider_id_ = provider_id + status_ = status + + # Validate arguments against known Juju API types. + if detachable_ is not None and not isinstance(detachable_, bool): + raise Exception( + f"Expected detachable_ to be a bool, received: {type(detachable_)}" + ) + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + self.detachable = detachable_ + self.id_ = id__ + self.message = message_ + self.provider_id = provider_id_ + self.status = status_ + self.unknown_fields = unknown_fields + + +class ModelInfo(Type): + _toSchema = { + "agent_version": "agent-version", + "cloud_credential_tag": "cloud-credential-tag", + "cloud_credential_validity": "cloud-credential-validity", + "cloud_region": "cloud-region", + "cloud_tag": "cloud-tag", + "controller_uuid": "controller-uuid", + "default_base": "default-base", + "default_series": "default-series", + "is_controller": "is-controller", + "life": "life", + "machines": "machines", + "migration": "migration", + "name": "name", + "owner_tag": "owner-tag", + "provider_type": "provider-type", + "secret_backends": "secret-backends", + "sla": "sla", + "status": "status", + "supported_features": "supported-features", + "type_": "type", + "users": "users", + "uuid": "uuid", + } + _toPy = { + "agent-version": "agent_version", + "cloud-credential-tag": "cloud_credential_tag", + "cloud-credential-validity": "cloud_credential_validity", + "cloud-region": "cloud_region", + "cloud-tag": "cloud_tag", + "controller-uuid": "controller_uuid", + "default-base": "default_base", + "default-series": "default_series", + "is-controller": "is_controller", + "life": "life", + "machines": "machines", + "migration": "migration", + "name": "name", + "owner-tag": "owner_tag", + "provider-type": "provider_type", + "secret-backends": "secret_backends", + "sla": "sla", + "status": "status", + "supported-features": "supported_features", + "type": "type_", + "users": "users", + "uuid": "uuid", + } + + def __init__( + self, + agent_version=None, + cloud_credential_tag=None, + cloud_credential_validity=None, + cloud_region=None, + cloud_tag=None, + controller_uuid=None, + default_base=None, + default_series=None, + is_controller=None, + life=None, + machines=None, + migration=None, + name=None, + owner_tag=None, + provider_type=None, + secret_backends=None, + sla=None, + status=None, + supported_features=None, + type_=None, + users=None, + uuid=None, + **unknown_fields, + ): + """agent_version : Number + cloud_credential_tag : str + cloud_credential_validity : bool + cloud_region : str + cloud_tag : str + controller_uuid : str + default_base : str + default_series : str + is_controller : bool + life : str + machines : typing.Sequence[~ModelMachineInfo] + migration : ModelMigrationStatus + name : str + owner_tag : str + provider_type : str + secret_backends : typing.Sequence[~SecretBackendResult] + sla : ModelSLAInfo + status : EntityStatus + supported_features : typing.Sequence[~SupportedFeature] + type_ : str + users : typing.Sequence[~ModelUserInfo] + uuid : str + """ + agent_version_ = Number.from_json(agent_version) if agent_version else None + cloud_credential_tag_ = cloud_credential_tag + cloud_credential_validity_ = cloud_credential_validity + cloud_region_ = cloud_region + cloud_tag_ = cloud_tag + controller_uuid_ = controller_uuid + default_base_ = default_base + default_series_ = default_series + is_controller_ = is_controller + life_ = life + machines_ = [ModelMachineInfo.from_json(o) for o in machines or []] + migration_ = ModelMigrationStatus.from_json(migration) if migration else None + name_ = name + owner_tag_ = owner_tag + provider_type_ = provider_type + secret_backends_ = [ + SecretBackendResult.from_json(o) for o in secret_backends or [] + ] + sla_ = ModelSLAInfo.from_json(sla) if sla else None + status_ = EntityStatus.from_json(status) if status else None + supported_features_ = [ + SupportedFeature.from_json(o) for o in supported_features or [] + ] + type__ = type_ + users_ = [ModelUserInfo.from_json(o) for o in users or []] + uuid_ = uuid + + # Validate arguments against known Juju API types. + if agent_version_ is not None and not isinstance( + agent_version_, (dict, Number) + ): + raise Exception( + f"Expected agent_version_ to be a Number, received: {type(agent_version_)}" + ) + + if cloud_credential_tag_ is not None and not isinstance( + cloud_credential_tag_, (bytes, str) + ): + raise Exception( + f"Expected cloud_credential_tag_ to be a str, received: {type(cloud_credential_tag_)}" + ) + + if cloud_credential_validity_ is not None and not isinstance( + cloud_credential_validity_, bool + ): + raise Exception( + f"Expected cloud_credential_validity_ to be a bool, received: {type(cloud_credential_validity_)}" + ) + + if cloud_region_ is not None and not isinstance(cloud_region_, (bytes, str)): + raise Exception( + f"Expected cloud_region_ to be a str, received: {type(cloud_region_)}" + ) + + if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): + raise Exception( + f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" + ) + + if controller_uuid_ is not None and not isinstance( + controller_uuid_, (bytes, str) + ): + raise Exception( + f"Expected controller_uuid_ to be a str, received: {type(controller_uuid_)}" + ) + + if default_base_ is not None and not isinstance(default_base_, (bytes, str)): + raise Exception( + f"Expected default_base_ to be a str, received: {type(default_base_)}" + ) + + if default_series_ is not None and not isinstance( + default_series_, (bytes, str) + ): + raise Exception( + f"Expected default_series_ to be a str, received: {type(default_series_)}" + ) + + if is_controller_ is not None and not isinstance(is_controller_, bool): + raise Exception( + f"Expected is_controller_ to be a bool, received: {type(is_controller_)}" + ) + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if machines_ is not None and not isinstance(machines_, (bytes, str, list)): + raise Exception( + f"Expected machines_ to be a Sequence, received: {type(machines_)}" + ) + + if migration_ is not None and not isinstance( + migration_, (dict, ModelMigrationStatus) + ): + raise Exception( + f"Expected migration_ to be a ModelMigrationStatus, received: {type(migration_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + if provider_type_ is not None and not isinstance(provider_type_, (bytes, str)): + raise Exception( + f"Expected provider_type_ to be a str, received: {type(provider_type_)}" + ) + + if secret_backends_ is not None and not isinstance( + secret_backends_, (bytes, str, list) + ): + raise Exception( + f"Expected secret_backends_ to be a Sequence, received: {type(secret_backends_)}" + ) + + if sla_ is not None and not isinstance(sla_, (dict, ModelSLAInfo)): + raise Exception( + f"Expected sla_ to be a ModelSLAInfo, received: {type(sla_)}" + ) + + if status_ is not None and not isinstance(status_, (dict, EntityStatus)): + raise Exception( + f"Expected status_ to be a EntityStatus, received: {type(status_)}" + ) + + if supported_features_ is not None and not isinstance( + supported_features_, (bytes, str, list) + ): + raise Exception( + f"Expected supported_features_ to be a Sequence, received: {type(supported_features_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + if users_ is not None and not isinstance(users_, (bytes, str, list)): + raise Exception( + f"Expected users_ to be a Sequence, received: {type(users_)}" + ) + + if uuid_ is not None and not isinstance(uuid_, (bytes, str)): + raise Exception(f"Expected uuid_ to be a str, received: {type(uuid_)}") + + self.agent_version = agent_version_ + self.cloud_credential_tag = cloud_credential_tag_ + self.cloud_credential_validity = cloud_credential_validity_ + self.cloud_region = cloud_region_ + self.cloud_tag = cloud_tag_ + self.controller_uuid = controller_uuid_ + self.default_base = default_base_ + self.default_series = default_series_ + self.is_controller = is_controller_ + self.life = life_ + self.machines = machines_ + self.migration = migration_ + self.name = name_ + self.owner_tag = owner_tag_ + self.provider_type = provider_type_ + self.secret_backends = secret_backends_ + self.sla = sla_ + self.status = status_ + self.supported_features = supported_features_ + self.type_ = type__ + self.users = users_ + self.uuid = uuid_ + self.unknown_fields = unknown_fields + + +class ModelInfoResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : ModelInfo + """ + error_ = Error.from_json(error) if error else None + result_ = ModelInfo.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, ModelInfo)): + raise Exception( + f"Expected result_ to be a ModelInfo, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class ModelInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ModelInfoResult]""" + results_ = [ModelInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ModelInstanceTypesConstraint(Type): + _toSchema = {"value": "value"} + _toPy = {"value": "value"} + + def __init__(self, value=None, **unknown_fields): + """Value : Value""" + value_ = Value.from_json(value) if value else None + + # Validate arguments against known Juju API types. + if value_ is not None and not isinstance(value_, (dict, Value)): + raise Exception(f"Expected value_ to be a Value, received: {type(value_)}") + + self.value = value_ + self.unknown_fields = unknown_fields + + +class ModelInstanceTypesConstraints(Type): + _toSchema = {"constraints": "constraints"} + _toPy = {"constraints": "constraints"} + + def __init__(self, constraints=None, **unknown_fields): + """Constraints : typing.Sequence[~ModelInstanceTypesConstraint]""" + constraints_ = [ + ModelInstanceTypesConstraint.from_json(o) for o in constraints or [] + ] + + # Validate arguments against known Juju API types. + if constraints_ is not None and not isinstance( + constraints_, (bytes, str, list) + ): + raise Exception( + f"Expected constraints_ to be a Sequence, received: {type(constraints_)}" + ) + + self.constraints = constraints_ + self.unknown_fields = unknown_fields + + +class ModelMachineInfo(Type): + _toSchema = { + "display_name": "display-name", + "ha_primary": "ha-primary", + "hardware": "hardware", + "has_vote": "has-vote", + "id_": "id", + "instance_id": "instance-id", + "message": "message", + "status": "status", + "wants_vote": "wants-vote", + } + _toPy = { + "display-name": "display_name", + "ha-primary": "ha_primary", + "hardware": "hardware", + "has-vote": "has_vote", + "id": "id_", + "instance-id": "instance_id", + "message": "message", + "status": "status", + "wants-vote": "wants_vote", + } + + def __init__( + self, + display_name=None, + ha_primary=None, + hardware=None, + has_vote=None, + id_=None, + instance_id=None, + message=None, + status=None, + wants_vote=None, + **unknown_fields, + ): + """display_name : str + ha_primary : bool + hardware : MachineHardware + has_vote : bool + id_ : str + instance_id : str + message : str + status : str + wants_vote : bool + """ + display_name_ = display_name + ha_primary_ = ha_primary + hardware_ = MachineHardware.from_json(hardware) if hardware else None + has_vote_ = has_vote + id__ = id_ + instance_id_ = instance_id + message_ = message + status_ = status + wants_vote_ = wants_vote + + # Validate arguments against known Juju API types. + if display_name_ is not None and not isinstance(display_name_, (bytes, str)): + raise Exception( + f"Expected display_name_ to be a str, received: {type(display_name_)}" + ) + + if ha_primary_ is not None and not isinstance(ha_primary_, bool): + raise Exception( + f"Expected ha_primary_ to be a bool, received: {type(ha_primary_)}" + ) + + if hardware_ is not None and not isinstance(hardware_, (dict, MachineHardware)): + raise Exception( + f"Expected hardware_ to be a MachineHardware, received: {type(hardware_)}" + ) + + if has_vote_ is not None and not isinstance(has_vote_, bool): + raise Exception( + f"Expected has_vote_ to be a bool, received: {type(has_vote_)}" + ) + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if instance_id_ is not None and not isinstance(instance_id_, (bytes, str)): + raise Exception( + f"Expected instance_id_ to be a str, received: {type(instance_id_)}" + ) + + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + if wants_vote_ is not None and not isinstance(wants_vote_, bool): + raise Exception( + f"Expected wants_vote_ to be a bool, received: {type(wants_vote_)}" + ) + + self.display_name = display_name_ + self.ha_primary = ha_primary_ + self.hardware = hardware_ + self.has_vote = has_vote_ + self.id_ = id__ + self.instance_id = instance_id_ + self.message = message_ + self.status = status_ + self.wants_vote = wants_vote_ + self.unknown_fields = unknown_fields + + +class ModelMigrationStatus(Type): + _toSchema = {"end": "end", "start": "start", "status": "status"} + _toPy = {"end": "end", "start": "start", "status": "status"} + + def __init__(self, end=None, start=None, status=None, **unknown_fields): + """End : str + start : str + status : str + """ + end_ = end + start_ = start + status_ = status + + # Validate arguments against known Juju API types. + if end_ is not None and not isinstance(end_, (bytes, str)): + raise Exception(f"Expected end_ to be a str, received: {type(end_)}") + + if start_ is not None and not isinstance(start_, (bytes, str)): + raise Exception(f"Expected start_ to be a str, received: {type(start_)}") + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + self.end = end_ + self.start = start_ + self.status = status_ + self.unknown_fields = unknown_fields + + +class ModelParam(Type): + _toSchema = {"model_tag": "model-tag"} + _toPy = {"model-tag": "model_tag"} + + def __init__(self, model_tag=None, **unknown_fields): + """model_tag : str""" + model_tag_ = model_tag + + # Validate arguments against known Juju API types. + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + self.model_tag = model_tag_ + self.unknown_fields = unknown_fields + + +class ModelSLA(Type): + _toSchema = { + "creds": "creds", + "level": "level", + "modelslainfo": "ModelSLAInfo", + "owner": "owner", + } + _toPy = { + "ModelSLAInfo": "modelslainfo", + "creds": "creds", + "level": "level", + "owner": "owner", + } + + def __init__( + self, modelslainfo=None, creds=None, level=None, owner=None, **unknown_fields + ): + """Modelslainfo : ModelSLAInfo + creds : typing.Sequence[int] + level : str + owner : str + """ + modelslainfo_ = ModelSLAInfo.from_json(modelslainfo) if modelslainfo else None + creds_ = creds + level_ = level + owner_ = owner + + # Validate arguments against known Juju API types. + if modelslainfo_ is not None and not isinstance( + modelslainfo_, (dict, ModelSLAInfo) + ): + raise Exception( + f"Expected modelslainfo_ to be a ModelSLAInfo, received: {type(modelslainfo_)}" + ) + + if creds_ is not None and not isinstance(creds_, (bytes, str, list)): + raise Exception( + f"Expected creds_ to be a Sequence, received: {type(creds_)}" + ) + + if level_ is not None and not isinstance(level_, (bytes, str)): + raise Exception(f"Expected level_ to be a str, received: {type(level_)}") + + if owner_ is not None and not isinstance(owner_, (bytes, str)): + raise Exception(f"Expected owner_ to be a str, received: {type(owner_)}") + + self.modelslainfo = modelslainfo_ + self.creds = creds_ + self.level = level_ + self.owner = owner_ + self.unknown_fields = unknown_fields + + +class ModelSLAInfo(Type): + _toSchema = {"level": "level", "owner": "owner"} + _toPy = {"level": "level", "owner": "owner"} + + def __init__(self, level=None, owner=None, **unknown_fields): + """Level : str + owner : str + """ + level_ = level + owner_ = owner + + # Validate arguments against known Juju API types. + if level_ is not None and not isinstance(level_, (bytes, str)): + raise Exception(f"Expected level_ to be a str, received: {type(level_)}") + + if owner_ is not None and not isinstance(owner_, (bytes, str)): + raise Exception(f"Expected owner_ to be a str, received: {type(owner_)}") + + self.level = level_ + self.owner = owner_ + self.unknown_fields = unknown_fields + + +class ModelSequencesResult(Type): + _toSchema = {"sequences": "sequences"} + _toPy = {"sequences": "sequences"} + + def __init__(self, sequences=None, **unknown_fields): + """Sequences : typing.Mapping[str, int]""" + sequences_ = sequences + + # Validate arguments against known Juju API types. + if sequences_ is not None and not isinstance(sequences_, dict): + raise Exception( + f"Expected sequences_ to be a Mapping, received: {type(sequences_)}" + ) + + self.sequences = sequences_ + self.unknown_fields = unknown_fields + + +class ModelSet(Type): + _toSchema = {"config": "config"} + _toPy = {"config": "config"} + + def __init__(self, config=None, **unknown_fields): + """Config : typing.Mapping[str, typing.Any]""" + config_ = config + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + self.config = config_ + self.unknown_fields = unknown_fields + + +class ModelStatus(Type): + _toSchema = { + "application_count": "application-count", + "applications": "applications", + "error": "error", + "filesystems": "filesystems", + "hosted_machine_count": "hosted-machine-count", + "life": "life", + "machines": "machines", + "model_tag": "model-tag", + "owner_tag": "owner-tag", + "type_": "type", + "unit_count": "unit-count", + "volumes": "volumes", + } + _toPy = { + "application-count": "application_count", + "applications": "applications", + "error": "error", + "filesystems": "filesystems", + "hosted-machine-count": "hosted_machine_count", + "life": "life", + "machines": "machines", + "model-tag": "model_tag", + "owner-tag": "owner_tag", + "type": "type_", + "unit-count": "unit_count", + "volumes": "volumes", + } + + def __init__( + self, + application_count=None, + applications=None, + error=None, + filesystems=None, + hosted_machine_count=None, + life=None, + machines=None, + model_tag=None, + owner_tag=None, + type_=None, + unit_count=None, + volumes=None, + **unknown_fields, + ): + """application_count : int + applications : typing.Sequence[~ModelApplicationInfo] + error : Error + filesystems : typing.Sequence[~ModelFilesystemInfo] + hosted_machine_count : int + life : str + machines : typing.Sequence[~ModelMachineInfo] + model_tag : str + owner_tag : str + type_ : str + unit_count : int + volumes : typing.Sequence[~ModelVolumeInfo] + """ + application_count_ = application_count + applications_ = [ModelApplicationInfo.from_json(o) for o in applications or []] + error_ = Error.from_json(error) if error else None + filesystems_ = [ModelFilesystemInfo.from_json(o) for o in filesystems or []] + hosted_machine_count_ = hosted_machine_count + life_ = life + machines_ = [ModelMachineInfo.from_json(o) for o in machines or []] + model_tag_ = model_tag + owner_tag_ = owner_tag + type__ = type_ + unit_count_ = unit_count + volumes_ = [ModelVolumeInfo.from_json(o) for o in volumes or []] + + # Validate arguments against known Juju API types. + if application_count_ is not None and not isinstance(application_count_, int): + raise Exception( + f"Expected application_count_ to be a int, received: {type(application_count_)}" + ) + + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if filesystems_ is not None and not isinstance( + filesystems_, (bytes, str, list) + ): + raise Exception( + f"Expected filesystems_ to be a Sequence, received: {type(filesystems_)}" + ) + + if hosted_machine_count_ is not None and not isinstance( + hosted_machine_count_, int + ): + raise Exception( + f"Expected hosted_machine_count_ to be a int, received: {type(hosted_machine_count_)}" + ) + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if machines_ is not None and not isinstance(machines_, (bytes, str, list)): + raise Exception( + f"Expected machines_ to be a Sequence, received: {type(machines_)}" + ) + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + if unit_count_ is not None and not isinstance(unit_count_, int): + raise Exception( + f"Expected unit_count_ to be a int, received: {type(unit_count_)}" + ) + + if volumes_ is not None and not isinstance(volumes_, (bytes, str, list)): + raise Exception( + f"Expected volumes_ to be a Sequence, received: {type(volumes_)}" + ) + + self.application_count = application_count_ + self.applications = applications_ + self.error = error_ + self.filesystems = filesystems_ + self.hosted_machine_count = hosted_machine_count_ + self.life = life_ + self.machines = machines_ + self.model_tag = model_tag_ + self.owner_tag = owner_tag_ + self.type_ = type__ + self.unit_count = unit_count_ + self.volumes = volumes_ + self.unknown_fields = unknown_fields + + +class ModelStatusInfo(Type): + _toSchema = { + "available_version": "available-version", + "cloud_tag": "cloud-tag", + "meter_status": "meter-status", + "model_status": "model-status", + "name": "name", + "region": "region", + "sla": "sla", + "type_": "type", + "version": "version", + } + _toPy = { + "available-version": "available_version", + "cloud-tag": "cloud_tag", + "meter-status": "meter_status", + "model-status": "model_status", + "name": "name", + "region": "region", + "sla": "sla", + "type": "type_", + "version": "version", + } + + def __init__( + self, + available_version=None, + cloud_tag=None, + meter_status=None, + model_status=None, + name=None, + region=None, + sla=None, + type_=None, + version=None, + **unknown_fields, + ): + """available_version : str + cloud_tag : str + meter_status : MeterStatus + model_status : DetailedStatus + name : str + region : str + sla : str + type_ : str + version : str + """ + available_version_ = available_version + cloud_tag_ = cloud_tag + meter_status_ = MeterStatus.from_json(meter_status) if meter_status else None + model_status_ = DetailedStatus.from_json(model_status) if model_status else None + name_ = name + region_ = region + sla_ = sla + type__ = type_ + version_ = version + + # Validate arguments against known Juju API types. + if available_version_ is not None and not isinstance( + available_version_, (bytes, str) + ): + raise Exception( + f"Expected available_version_ to be a str, received: {type(available_version_)}" + ) + + if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): + raise Exception( + f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" + ) + + if meter_status_ is not None and not isinstance( + meter_status_, (dict, MeterStatus) + ): + raise Exception( + f"Expected meter_status_ to be a MeterStatus, received: {type(meter_status_)}" + ) + + if model_status_ is not None and not isinstance( + model_status_, (dict, DetailedStatus) + ): + raise Exception( + f"Expected model_status_ to be a DetailedStatus, received: {type(model_status_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if region_ is not None and not isinstance(region_, (bytes, str)): + raise Exception(f"Expected region_ to be a str, received: {type(region_)}") + + if sla_ is not None and not isinstance(sla_, (bytes, str)): + raise Exception(f"Expected sla_ to be a str, received: {type(sla_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + if version_ is not None and not isinstance(version_, (bytes, str)): + raise Exception( + f"Expected version_ to be a str, received: {type(version_)}" + ) + + self.available_version = available_version_ + self.cloud_tag = cloud_tag_ + self.meter_status = meter_status_ + self.model_status = model_status_ + self.name = name_ + self.region = region_ + self.sla = sla_ + self.type_ = type__ + self.version = version_ + self.unknown_fields = unknown_fields + + +class ModelStatusResults(Type): + _toSchema = {"models": "models"} + _toPy = {"models": "models"} + + def __init__(self, models=None, **unknown_fields): + """Models : typing.Sequence[~ModelStatus]""" + models_ = [ModelStatus.from_json(o) for o in models or []] + + # Validate arguments against known Juju API types. + if models_ is not None and not isinstance(models_, (bytes, str, list)): + raise Exception( + f"Expected models_ to be a Sequence, received: {type(models_)}" + ) + + self.models = models_ + self.unknown_fields = unknown_fields + + +class ModelSummariesRequest(Type): + _toSchema = {"all_": "all", "user_tag": "user-tag"} + _toPy = {"all": "all_", "user-tag": "user_tag"} + + def __init__(self, all_=None, user_tag=None, **unknown_fields): + """all_ : bool + user_tag : str + """ + all__ = all_ + user_tag_ = user_tag + + # Validate arguments against known Juju API types. + if all__ is not None and not isinstance(all__, bool): + raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") + + if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): + raise Exception( + f"Expected user_tag_ to be a str, received: {type(user_tag_)}" + ) + + self.all_ = all__ + self.user_tag = user_tag_ + self.unknown_fields = unknown_fields + + +class ModelSummary(Type): + _toSchema = { + "agent_version": "agent-version", + "cloud_credential_tag": "cloud-credential-tag", + "cloud_region": "cloud-region", + "cloud_tag": "cloud-tag", + "controller_uuid": "controller-uuid", + "counts": "counts", + "default_series": "default-series", + "is_controller": "is-controller", + "last_connection": "last-connection", + "life": "life", + "migration": "migration", + "name": "name", + "owner_tag": "owner-tag", + "provider_type": "provider-type", + "sla": "sla", + "status": "status", + "type_": "type", + "user_access": "user-access", + "uuid": "uuid", + } + _toPy = { + "agent-version": "agent_version", + "cloud-credential-tag": "cloud_credential_tag", + "cloud-region": "cloud_region", + "cloud-tag": "cloud_tag", + "controller-uuid": "controller_uuid", + "counts": "counts", + "default-series": "default_series", + "is-controller": "is_controller", + "last-connection": "last_connection", + "life": "life", + "migration": "migration", + "name": "name", + "owner-tag": "owner_tag", + "provider-type": "provider_type", + "sla": "sla", + "status": "status", + "type": "type_", + "user-access": "user_access", + "uuid": "uuid", + } + + def __init__( + self, + agent_version=None, + cloud_credential_tag=None, + cloud_region=None, + cloud_tag=None, + controller_uuid=None, + counts=None, + default_series=None, + is_controller=None, + last_connection=None, + life=None, + migration=None, + name=None, + owner_tag=None, + provider_type=None, + sla=None, + status=None, + type_=None, + user_access=None, + uuid=None, + **unknown_fields, + ): + """agent_version : Number + cloud_credential_tag : str + cloud_region : str + cloud_tag : str + controller_uuid : str + counts : typing.Sequence[~ModelEntityCount] + default_series : str + is_controller : bool + last_connection : str + life : str + migration : ModelMigrationStatus + name : str + owner_tag : str + provider_type : str + sla : ModelSLAInfo + status : EntityStatus + type_ : str + user_access : str + uuid : str + """ + agent_version_ = Number.from_json(agent_version) if agent_version else None + cloud_credential_tag_ = cloud_credential_tag + cloud_region_ = cloud_region + cloud_tag_ = cloud_tag + controller_uuid_ = controller_uuid + counts_ = [ModelEntityCount.from_json(o) for o in counts or []] + default_series_ = default_series + is_controller_ = is_controller + last_connection_ = last_connection + life_ = life + migration_ = ModelMigrationStatus.from_json(migration) if migration else None + name_ = name + owner_tag_ = owner_tag + provider_type_ = provider_type + sla_ = ModelSLAInfo.from_json(sla) if sla else None + status_ = EntityStatus.from_json(status) if status else None + type__ = type_ + user_access_ = user_access + uuid_ = uuid + + # Validate arguments against known Juju API types. + if agent_version_ is not None and not isinstance( + agent_version_, (dict, Number) + ): + raise Exception( + f"Expected agent_version_ to be a Number, received: {type(agent_version_)}" + ) + + if cloud_credential_tag_ is not None and not isinstance( + cloud_credential_tag_, (bytes, str) + ): + raise Exception( + f"Expected cloud_credential_tag_ to be a str, received: {type(cloud_credential_tag_)}" + ) + + if cloud_region_ is not None and not isinstance(cloud_region_, (bytes, str)): + raise Exception( + f"Expected cloud_region_ to be a str, received: {type(cloud_region_)}" + ) + + if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): + raise Exception( + f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" + ) + + if controller_uuid_ is not None and not isinstance( + controller_uuid_, (bytes, str) + ): + raise Exception( + f"Expected controller_uuid_ to be a str, received: {type(controller_uuid_)}" + ) + + if counts_ is not None and not isinstance(counts_, (bytes, str, list)): + raise Exception( + f"Expected counts_ to be a Sequence, received: {type(counts_)}" + ) + + if default_series_ is not None and not isinstance( + default_series_, (bytes, str) + ): + raise Exception( + f"Expected default_series_ to be a str, received: {type(default_series_)}" + ) + + if is_controller_ is not None and not isinstance(is_controller_, bool): + raise Exception( + f"Expected is_controller_ to be a bool, received: {type(is_controller_)}" + ) + + if last_connection_ is not None and not isinstance( + last_connection_, (bytes, str) + ): + raise Exception( + f"Expected last_connection_ to be a str, received: {type(last_connection_)}" + ) + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if migration_ is not None and not isinstance( + migration_, (dict, ModelMigrationStatus) + ): + raise Exception( + f"Expected migration_ to be a ModelMigrationStatus, received: {type(migration_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + if provider_type_ is not None and not isinstance(provider_type_, (bytes, str)): + raise Exception( + f"Expected provider_type_ to be a str, received: {type(provider_type_)}" + ) + + if sla_ is not None and not isinstance(sla_, (dict, ModelSLAInfo)): + raise Exception( + f"Expected sla_ to be a ModelSLAInfo, received: {type(sla_)}" + ) + + if status_ is not None and not isinstance(status_, (dict, EntityStatus)): + raise Exception( + f"Expected status_ to be a EntityStatus, received: {type(status_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + if user_access_ is not None and not isinstance(user_access_, (bytes, str)): + raise Exception( + f"Expected user_access_ to be a str, received: {type(user_access_)}" + ) + + if uuid_ is not None and not isinstance(uuid_, (bytes, str)): + raise Exception(f"Expected uuid_ to be a str, received: {type(uuid_)}") + + self.agent_version = agent_version_ + self.cloud_credential_tag = cloud_credential_tag_ + self.cloud_region = cloud_region_ + self.cloud_tag = cloud_tag_ + self.controller_uuid = controller_uuid_ + self.counts = counts_ + self.default_series = default_series_ + self.is_controller = is_controller_ + self.last_connection = last_connection_ + self.life = life_ + self.migration = migration_ + self.name = name_ + self.owner_tag = owner_tag_ + self.provider_type = provider_type_ + self.sla = sla_ + self.status = status_ + self.type_ = type__ + self.user_access = user_access_ + self.uuid = uuid_ + self.unknown_fields = unknown_fields + + +class ModelSummaryResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : ModelSummary + """ + error_ = Error.from_json(error) if error else None + result_ = ModelSummary.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, ModelSummary)): + raise Exception( + f"Expected result_ to be a ModelSummary, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class ModelSummaryResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ModelSummaryResult]""" + results_ = [ModelSummaryResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ModelTag(Type): + _toSchema = {} + _toPy = {} + + def __init__(self, **unknown_fields): + """ """ + self.unknown_fields = unknown_fields + + +class ModelUnset(Type): + _toSchema = {"keys": "keys"} + _toPy = {"keys": "keys"} + + def __init__(self, keys=None, **unknown_fields): + """Keys : typing.Sequence[str]""" + keys_ = keys + + # Validate arguments against known Juju API types. + if keys_ is not None and not isinstance(keys_, (bytes, str, list)): + raise Exception(f"Expected keys_ to be a Sequence, received: {type(keys_)}") + + self.keys = keys_ + self.unknown_fields = unknown_fields + + +class ModelUnsetKeys(Type): + _toSchema = { + "cloud_region": "cloud-region", + "cloud_tag": "cloud-tag", + "keys": "keys", + } + _toPy = {"cloud-region": "cloud_region", "cloud-tag": "cloud_tag", "keys": "keys"} + + def __init__(self, cloud_region=None, cloud_tag=None, keys=None, **unknown_fields): + """cloud_region : str + cloud_tag : str + keys : typing.Sequence[str] + """ + cloud_region_ = cloud_region + cloud_tag_ = cloud_tag + keys_ = keys + + # Validate arguments against known Juju API types. + if cloud_region_ is not None and not isinstance(cloud_region_, (bytes, str)): + raise Exception( + f"Expected cloud_region_ to be a str, received: {type(cloud_region_)}" + ) + + if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): + raise Exception( + f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" + ) + + if keys_ is not None and not isinstance(keys_, (bytes, str, list)): + raise Exception(f"Expected keys_ to be a Sequence, received: {type(keys_)}") + + self.cloud_region = cloud_region_ + self.cloud_tag = cloud_tag_ + self.keys = keys_ + self.unknown_fields = unknown_fields + + +class ModelUserInfo(Type): + _toSchema = { + "access": "access", + "display_name": "display-name", + "last_connection": "last-connection", + "model_tag": "model-tag", + "user": "user", + } + _toPy = { + "access": "access", + "display-name": "display_name", + "last-connection": "last_connection", + "model-tag": "model_tag", + "user": "user", + } + + def __init__( + self, + access=None, + display_name=None, + last_connection=None, + model_tag=None, + user=None, + **unknown_fields, + ): + """Access : str + display_name : str + last_connection : str + model_tag : str + user : str + """ + access_ = access + display_name_ = display_name + last_connection_ = last_connection + model_tag_ = model_tag + user_ = user + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if display_name_ is not None and not isinstance(display_name_, (bytes, str)): + raise Exception( + f"Expected display_name_ to be a str, received: {type(display_name_)}" + ) + + if last_connection_ is not None and not isinstance( + last_connection_, (bytes, str) + ): + raise Exception( + f"Expected last_connection_ to be a str, received: {type(last_connection_)}" + ) + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + if user_ is not None and not isinstance(user_, (bytes, str)): + raise Exception(f"Expected user_ to be a str, received: {type(user_)}") + + self.access = access_ + self.display_name = display_name_ + self.last_connection = last_connection_ + self.model_tag = model_tag_ + self.user = user_ + self.unknown_fields = unknown_fields + + +class ModelUserInfoResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : ModelUserInfo + """ + error_ = Error.from_json(error) if error else None + result_ = ModelUserInfo.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, ModelUserInfo)): + raise Exception( + f"Expected result_ to be a ModelUserInfo, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class ModelUserInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ModelUserInfoResult]""" + results_ = [ModelUserInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ModelVolumeInfo(Type): + _toSchema = { + "detachable": "detachable", + "id_": "id", + "message": "message", + "provider_id": "provider-id", + "status": "status", + } + _toPy = { + "detachable": "detachable", + "id": "id_", + "message": "message", + "provider-id": "provider_id", + "status": "status", + } + + def __init__( + self, + detachable=None, + id_=None, + message=None, + provider_id=None, + status=None, + **unknown_fields, + ): + """Detachable : bool + id_ : str + message : str + provider_id : str + status : str + """ + detachable_ = detachable + id__ = id_ + message_ = message + provider_id_ = provider_id + status_ = status + + # Validate arguments against known Juju API types. + if detachable_ is not None and not isinstance(detachable_, bool): + raise Exception( + f"Expected detachable_ to be a bool, received: {type(detachable_)}" + ) + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + self.detachable = detachable_ + self.id_ = id__ + self.message = message_ + self.provider_id = provider_id_ + self.status = status_ + self.unknown_fields = unknown_fields + + +class ModifyCloudAccess(Type): + _toSchema = { + "access": "access", + "action": "action", + "cloud_tag": "cloud-tag", + "user_tag": "user-tag", + } + _toPy = { + "access": "access", + "action": "action", + "cloud-tag": "cloud_tag", + "user-tag": "user_tag", + } + + def __init__( + self, access=None, action=None, cloud_tag=None, user_tag=None, **unknown_fields + ): + """Access : str + action : str + cloud_tag : str + user_tag : str + """ + access_ = access + action_ = action + cloud_tag_ = cloud_tag + user_tag_ = user_tag + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if action_ is not None and not isinstance(action_, (bytes, str)): + raise Exception(f"Expected action_ to be a str, received: {type(action_)}") + + if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): + raise Exception( + f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" + ) + + if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): + raise Exception( + f"Expected user_tag_ to be a str, received: {type(user_tag_)}" + ) + + self.access = access_ + self.action = action_ + self.cloud_tag = cloud_tag_ + self.user_tag = user_tag_ + self.unknown_fields = unknown_fields + + +class ModifyCloudAccessRequest(Type): + _toSchema = {"changes": "changes"} + _toPy = {"changes": "changes"} + + def __init__(self, changes=None, **unknown_fields): + """Changes : typing.Sequence[~ModifyCloudAccess]""" + changes_ = [ModifyCloudAccess.from_json(o) for o in changes or []] + + # Validate arguments against known Juju API types. + if changes_ is not None and not isinstance(changes_, (bytes, str, list)): + raise Exception( + f"Expected changes_ to be a Sequence, received: {type(changes_)}" + ) + + self.changes = changes_ + self.unknown_fields = unknown_fields + + +class ModifyControllerAccess(Type): + _toSchema = {"access": "access", "action": "action", "user_tag": "user-tag"} + _toPy = {"access": "access", "action": "action", "user-tag": "user_tag"} + + def __init__(self, access=None, action=None, user_tag=None, **unknown_fields): + """Access : str + action : str + user_tag : str + """ + access_ = access + action_ = action + user_tag_ = user_tag + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if action_ is not None and not isinstance(action_, (bytes, str)): + raise Exception(f"Expected action_ to be a str, received: {type(action_)}") + + if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): + raise Exception( + f"Expected user_tag_ to be a str, received: {type(user_tag_)}" + ) + + self.access = access_ + self.action = action_ + self.user_tag = user_tag_ + self.unknown_fields = unknown_fields + + +class ModifyControllerAccessRequest(Type): + _toSchema = {"changes": "changes"} + _toPy = {"changes": "changes"} + + def __init__(self, changes=None, **unknown_fields): + """Changes : typing.Sequence[~ModifyControllerAccess]""" + changes_ = [ModifyControllerAccess.from_json(o) for o in changes or []] + + # Validate arguments against known Juju API types. + if changes_ is not None and not isinstance(changes_, (bytes, str, list)): + raise Exception( + f"Expected changes_ to be a Sequence, received: {type(changes_)}" + ) + + self.changes = changes_ + self.unknown_fields = unknown_fields + + +class ModifyModelAccess(Type): + _toSchema = { + "access": "access", + "action": "action", + "model_tag": "model-tag", + "user_tag": "user-tag", + } + _toPy = { + "access": "access", + "action": "action", + "model-tag": "model_tag", + "user-tag": "user_tag", + } + + def __init__( + self, access=None, action=None, model_tag=None, user_tag=None, **unknown_fields + ): + """Access : str + action : str + model_tag : str + user_tag : str + """ + access_ = access + action_ = action + model_tag_ = model_tag + user_tag_ = user_tag + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if action_ is not None and not isinstance(action_, (bytes, str)): + raise Exception(f"Expected action_ to be a str, received: {type(action_)}") + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): + raise Exception( + f"Expected user_tag_ to be a str, received: {type(user_tag_)}" + ) + + self.access = access_ + self.action = action_ + self.model_tag = model_tag_ + self.user_tag = user_tag_ + self.unknown_fields = unknown_fields + + +class ModifyModelAccessRequest(Type): + _toSchema = {"changes": "changes"} + _toPy = {"changes": "changes"} + + def __init__(self, changes=None, **unknown_fields): + """Changes : typing.Sequence[~ModifyModelAccess]""" + changes_ = [ModifyModelAccess.from_json(o) for o in changes or []] + + # Validate arguments against known Juju API types. + if changes_ is not None and not isinstance(changes_, (bytes, str, list)): + raise Exception( + f"Expected changes_ to be a Sequence, received: {type(changes_)}" + ) + + self.changes = changes_ + self.unknown_fields = unknown_fields + + +class ModifyOfferAccess(Type): + _toSchema = { + "access": "access", + "action": "action", + "offer_url": "offer-url", + "user_tag": "user-tag", + } + _toPy = { + "access": "access", + "action": "action", + "offer-url": "offer_url", + "user-tag": "user_tag", + } + + def __init__( + self, access=None, action=None, offer_url=None, user_tag=None, **unknown_fields + ): + """Access : str + action : str + offer_url : str + user_tag : str + """ + access_ = access + action_ = action + offer_url_ = offer_url + user_tag_ = user_tag + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if action_ is not None and not isinstance(action_, (bytes, str)): + raise Exception(f"Expected action_ to be a str, received: {type(action_)}") + + if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): + raise Exception( + f"Expected offer_url_ to be a str, received: {type(offer_url_)}" + ) + + if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): + raise Exception( + f"Expected user_tag_ to be a str, received: {type(user_tag_)}" + ) + + self.access = access_ + self.action = action_ + self.offer_url = offer_url_ + self.user_tag = user_tag_ + self.unknown_fields = unknown_fields + + +class ModifyOfferAccessRequest(Type): + _toSchema = {"changes": "changes"} + _toPy = {"changes": "changes"} + + def __init__(self, changes=None, **unknown_fields): + """Changes : typing.Sequence[~ModifyOfferAccess]""" + changes_ = [ModifyOfferAccess.from_json(o) for o in changes or []] + + # Validate arguments against known Juju API types. + if changes_ is not None and not isinstance(changes_, (bytes, str, list)): + raise Exception( + f"Expected changes_ to be a Sequence, received: {type(changes_)}" + ) + + self.changes = changes_ + self.unknown_fields = unknown_fields + + +class ModifyUserSSHKeys(Type): + _toSchema = {"ssh_keys": "ssh-keys", "user": "user"} + _toPy = {"ssh-keys": "ssh_keys", "user": "user"} + + def __init__(self, ssh_keys=None, user=None, **unknown_fields): + """ssh_keys : typing.Sequence[str] + user : str + """ + ssh_keys_ = ssh_keys + user_ = user + + # Validate arguments against known Juju API types. + if ssh_keys_ is not None and not isinstance(ssh_keys_, (bytes, str, list)): + raise Exception( + f"Expected ssh_keys_ to be a Sequence, received: {type(ssh_keys_)}" + ) + + if user_ is not None and not isinstance(user_, (bytes, str)): + raise Exception(f"Expected user_ to be a str, received: {type(user_)}") + + self.ssh_keys = ssh_keys_ + self.user = user_ + self.unknown_fields = unknown_fields + + +class MoveSubnetsParam(Type): + _toSchema = {"force": "force", "space_tag": "space-tag", "subnets": "subnets"} + _toPy = {"force": "force", "space-tag": "space_tag", "subnets": "subnets"} + + def __init__(self, force=None, space_tag=None, subnets=None, **unknown_fields): + """Force : bool + space_tag : str + subnets : typing.Sequence[str] + """ + force_ = force + space_tag_ = space_tag + subnets_ = subnets + + # Validate arguments against known Juju API types. + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): + raise Exception( + f"Expected space_tag_ to be a str, received: {type(space_tag_)}" + ) + + if subnets_ is not None and not isinstance(subnets_, (bytes, str, list)): + raise Exception( + f"Expected subnets_ to be a Sequence, received: {type(subnets_)}" + ) + + self.force = force_ + self.space_tag = space_tag_ + self.subnets = subnets_ + self.unknown_fields = unknown_fields + + +class MoveSubnetsParams(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~MoveSubnetsParam]""" + args_ = [MoveSubnetsParam.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class MoveSubnetsResult(Type): + _toSchema = { + "error": "error", + "moved_subnets": "moved-subnets", + "new_space": "new-space", + } + _toPy = { + "error": "error", + "moved-subnets": "moved_subnets", + "new-space": "new_space", + } + + def __init__( + self, error=None, moved_subnets=None, new_space=None, **unknown_fields + ): + """Error : Error + moved_subnets : typing.Sequence[~MovedSubnet] + new_space : str + """ + error_ = Error.from_json(error) if error else None + moved_subnets_ = [MovedSubnet.from_json(o) for o in moved_subnets or []] + new_space_ = new_space + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if moved_subnets_ is not None and not isinstance( + moved_subnets_, (bytes, str, list) + ): + raise Exception( + f"Expected moved_subnets_ to be a Sequence, received: {type(moved_subnets_)}" + ) + + if new_space_ is not None and not isinstance(new_space_, (bytes, str)): + raise Exception( + f"Expected new_space_ to be a str, received: {type(new_space_)}" + ) + + self.error = error_ + self.moved_subnets = moved_subnets_ + self.new_space = new_space_ + self.unknown_fields = unknown_fields + + +class MoveSubnetsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~MoveSubnetsResult]""" + results_ = [MoveSubnetsResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class MovedSubnet(Type): + _toSchema = {"cidr": "cidr", "old_space": "old-space", "subnet": "subnet"} + _toPy = {"cidr": "cidr", "old-space": "old_space", "subnet": "subnet"} + + def __init__(self, cidr=None, old_space=None, subnet=None, **unknown_fields): + """Cidr : str + old_space : str + subnet : str + """ + cidr_ = cidr + old_space_ = old_space + subnet_ = subnet + + # Validate arguments against known Juju API types. + if cidr_ is not None and not isinstance(cidr_, (bytes, str)): + raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") + + if old_space_ is not None and not isinstance(old_space_, (bytes, str)): + raise Exception( + f"Expected old_space_ to be a str, received: {type(old_space_)}" + ) + + if subnet_ is not None and not isinstance(subnet_, (bytes, str)): + raise Exception(f"Expected subnet_ to be a str, received: {type(subnet_)}") + + self.cidr = cidr_ + self.old_space = old_space_ + self.subnet = subnet_ + self.unknown_fields = unknown_fields + + +class NetworkInterface(Type): + _toSchema = { + "dns_nameservers": "dns-nameservers", + "gateway": "gateway", + "ip_addresses": "ip-addresses", + "is_up": "is-up", + "mac_address": "mac-address", + "space": "space", + } + _toPy = { + "dns-nameservers": "dns_nameservers", + "gateway": "gateway", + "ip-addresses": "ip_addresses", + "is-up": "is_up", + "mac-address": "mac_address", + "space": "space", + } + + def __init__( + self, + dns_nameservers=None, + gateway=None, + ip_addresses=None, + is_up=None, + mac_address=None, + space=None, + **unknown_fields, + ): + """dns_nameservers : typing.Sequence[str] + gateway : str + ip_addresses : typing.Sequence[str] + is_up : bool + mac_address : str + space : str + """ + dns_nameservers_ = dns_nameservers + gateway_ = gateway + ip_addresses_ = ip_addresses + is_up_ = is_up + mac_address_ = mac_address + space_ = space + + # Validate arguments against known Juju API types. + if dns_nameservers_ is not None and not isinstance( + dns_nameservers_, (bytes, str, list) + ): + raise Exception( + f"Expected dns_nameservers_ to be a Sequence, received: {type(dns_nameservers_)}" + ) + + if gateway_ is not None and not isinstance(gateway_, (bytes, str)): + raise Exception( + f"Expected gateway_ to be a str, received: {type(gateway_)}" + ) + + if ip_addresses_ is not None and not isinstance( + ip_addresses_, (bytes, str, list) + ): + raise Exception( + f"Expected ip_addresses_ to be a Sequence, received: {type(ip_addresses_)}" + ) + + if is_up_ is not None and not isinstance(is_up_, bool): + raise Exception(f"Expected is_up_ to be a bool, received: {type(is_up_)}") + + if mac_address_ is not None and not isinstance(mac_address_, (bytes, str)): + raise Exception( + f"Expected mac_address_ to be a str, received: {type(mac_address_)}" + ) + + if space_ is not None and not isinstance(space_, (bytes, str)): + raise Exception(f"Expected space_ to be a str, received: {type(space_)}") + + self.dns_nameservers = dns_nameservers_ + self.gateway = gateway_ + self.ip_addresses = ip_addresses_ + self.is_up = is_up_ + self.mac_address = mac_address_ + self.space = space_ + self.unknown_fields = unknown_fields + + +class NotifyWatchResult(Type): + _toSchema = {"error": "error", "notifywatcherid": "NotifyWatcherId"} + _toPy = {"NotifyWatcherId": "notifywatcherid", "error": "error"} + + def __init__(self, notifywatcherid=None, error=None, **unknown_fields): + """Notifywatcherid : str + error : Error + """ + notifywatcherid_ = notifywatcherid + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if notifywatcherid_ is not None and not isinstance( + notifywatcherid_, (bytes, str) + ): + raise Exception( + f"Expected notifywatcherid_ to be a str, received: {type(notifywatcherid_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.notifywatcherid = notifywatcherid_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class NotifyWatchResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~NotifyWatchResult]""" + results_ = [NotifyWatchResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class Number(Type): + _toSchema = { + "build": "Build", + "major": "Major", + "minor": "Minor", + "patch": "Patch", + "tag": "Tag", + } + _toPy = { + "Build": "build", + "Major": "major", + "Minor": "minor", + "Patch": "patch", + "Tag": "tag", + } + + def __init__( + self, build=None, major=None, minor=None, patch=None, tag=None, **unknown_fields + ): + """Build : int + major : int + minor : int + patch : int + tag : str + """ + build_ = build + major_ = major + minor_ = minor + patch_ = patch + tag_ = tag + + # Validate arguments against known Juju API types. + if build_ is not None and not isinstance(build_, int): + raise Exception(f"Expected build_ to be a int, received: {type(build_)}") + + if major_ is not None and not isinstance(major_, int): + raise Exception(f"Expected major_ to be a int, received: {type(major_)}") + + if minor_ is not None and not isinstance(minor_, int): + raise Exception(f"Expected minor_ to be a int, received: {type(minor_)}") + + if patch_ is not None and not isinstance(patch_, int): + raise Exception(f"Expected patch_ to be a int, received: {type(patch_)}") + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.build = build_ + self.major = major_ + self.minor = minor_ + self.patch = patch_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class OfferConnection(Type): + _toSchema = { + "endpoint": "endpoint", + "ingress_subnets": "ingress-subnets", + "relation_id": "relation-id", + "source_model_tag": "source-model-tag", + "status": "status", + "username": "username", + } + _toPy = { + "endpoint": "endpoint", + "ingress-subnets": "ingress_subnets", + "relation-id": "relation_id", + "source-model-tag": "source_model_tag", + "status": "status", + "username": "username", + } + + def __init__( + self, + endpoint=None, + ingress_subnets=None, + relation_id=None, + source_model_tag=None, + status=None, + username=None, + **unknown_fields, + ): + """Endpoint : str + ingress_subnets : typing.Sequence[str] + relation_id : int + source_model_tag : str + status : EntityStatus + username : str + """ + endpoint_ = endpoint + ingress_subnets_ = ingress_subnets + relation_id_ = relation_id + source_model_tag_ = source_model_tag + status_ = EntityStatus.from_json(status) if status else None + username_ = username + + # Validate arguments against known Juju API types. + if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): + raise Exception( + f"Expected endpoint_ to be a str, received: {type(endpoint_)}" + ) + + if ingress_subnets_ is not None and not isinstance( + ingress_subnets_, (bytes, str, list) + ): + raise Exception( + f"Expected ingress_subnets_ to be a Sequence, received: {type(ingress_subnets_)}" + ) + + if relation_id_ is not None and not isinstance(relation_id_, int): + raise Exception( + f"Expected relation_id_ to be a int, received: {type(relation_id_)}" + ) + + if source_model_tag_ is not None and not isinstance( + source_model_tag_, (bytes, str) + ): + raise Exception( + f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" + ) + + if status_ is not None and not isinstance(status_, (dict, EntityStatus)): + raise Exception( + f"Expected status_ to be a EntityStatus, received: {type(status_)}" + ) + + if username_ is not None and not isinstance(username_, (bytes, str)): + raise Exception( + f"Expected username_ to be a str, received: {type(username_)}" + ) + + self.endpoint = endpoint_ + self.ingress_subnets = ingress_subnets_ + self.relation_id = relation_id_ + self.source_model_tag = source_model_tag_ + self.status = status_ + self.username = username_ + self.unknown_fields = unknown_fields + + +class OfferFilter(Type): + _toSchema = { + "allowed_users": "allowed-users", + "application_description": "application-description", + "application_name": "application-name", + "application_user": "application-user", + "connected_users": "connected-users", + "endpoints": "endpoints", + "model_name": "model-name", + "offer_name": "offer-name", + "owner_name": "owner-name", + } + _toPy = { + "allowed-users": "allowed_users", + "application-description": "application_description", + "application-name": "application_name", + "application-user": "application_user", + "connected-users": "connected_users", + "endpoints": "endpoints", + "model-name": "model_name", + "offer-name": "offer_name", + "owner-name": "owner_name", + } + + def __init__( + self, + allowed_users=None, + application_description=None, + application_name=None, + application_user=None, + connected_users=None, + endpoints=None, + model_name=None, + offer_name=None, + owner_name=None, + **unknown_fields, + ): + """allowed_users : typing.Sequence[str] + application_description : str + application_name : str + application_user : str + connected_users : typing.Sequence[str] + endpoints : typing.Sequence[~EndpointFilterAttributes] + model_name : str + offer_name : str + owner_name : str + """ + allowed_users_ = allowed_users + application_description_ = application_description + application_name_ = application_name + application_user_ = application_user + connected_users_ = connected_users + endpoints_ = [EndpointFilterAttributes.from_json(o) for o in endpoints or []] + model_name_ = model_name + offer_name_ = offer_name + owner_name_ = owner_name + + # Validate arguments against known Juju API types. + if allowed_users_ is not None and not isinstance( + allowed_users_, (bytes, str, list) + ): + raise Exception( + f"Expected allowed_users_ to be a Sequence, received: {type(allowed_users_)}" + ) + + if application_description_ is not None and not isinstance( + application_description_, (bytes, str) + ): + raise Exception( + f"Expected application_description_ to be a str, received: {type(application_description_)}" + ) + + if application_name_ is not None and not isinstance( + application_name_, (bytes, str) + ): + raise Exception( + f"Expected application_name_ to be a str, received: {type(application_name_)}" + ) + + if application_user_ is not None and not isinstance( + application_user_, (bytes, str) + ): + raise Exception( + f"Expected application_user_ to be a str, received: {type(application_user_)}" + ) + + if connected_users_ is not None and not isinstance( + connected_users_, (bytes, str, list) + ): + raise Exception( + f"Expected connected_users_ to be a Sequence, received: {type(connected_users_)}" + ) + + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if model_name_ is not None and not isinstance(model_name_, (bytes, str)): + raise Exception( + f"Expected model_name_ to be a str, received: {type(model_name_)}" + ) + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if owner_name_ is not None and not isinstance(owner_name_, (bytes, str)): + raise Exception( + f"Expected owner_name_ to be a str, received: {type(owner_name_)}" + ) + + self.allowed_users = allowed_users_ + self.application_description = application_description_ + self.application_name = application_name_ + self.application_user = application_user_ + self.connected_users = connected_users_ + self.endpoints = endpoints_ + self.model_name = model_name_ + self.offer_name = offer_name_ + self.owner_name = owner_name_ + self.unknown_fields = unknown_fields + + +class OfferFilters(Type): + _toSchema = {"filters": "Filters"} + _toPy = {"Filters": "filters"} + + def __init__(self, filters=None, **unknown_fields): + """Filters : typing.Sequence[~OfferFilter]""" + filters_ = [OfferFilter.from_json(o) for o in filters or []] + + # Validate arguments against known Juju API types. + if filters_ is not None and not isinstance(filters_, (bytes, str, list)): + raise Exception( + f"Expected filters_ to be a Sequence, received: {type(filters_)}" + ) + + self.filters = filters_ + self.unknown_fields = unknown_fields + + +class OfferURLs(Type): + _toSchema = {"bakery_version": "bakery-version", "offer_urls": "offer-urls"} + _toPy = {"bakery-version": "bakery_version", "offer-urls": "offer_urls"} + + def __init__(self, bakery_version=None, offer_urls=None, **unknown_fields): + """bakery_version : int + offer_urls : typing.Sequence[str] + """ + bakery_version_ = bakery_version + offer_urls_ = offer_urls + + # Validate arguments against known Juju API types. + if bakery_version_ is not None and not isinstance(bakery_version_, int): + raise Exception( + f"Expected bakery_version_ to be a int, received: {type(bakery_version_)}" + ) + + if offer_urls_ is not None and not isinstance(offer_urls_, (bytes, str, list)): + raise Exception( + f"Expected offer_urls_ to be a Sequence, received: {type(offer_urls_)}" + ) + + self.bakery_version = bakery_version_ + self.offer_urls = offer_urls_ + self.unknown_fields = unknown_fields + + +class OfferUserDetails(Type): + _toSchema = {"access": "access", "display_name": "display-name", "user": "user"} + _toPy = {"access": "access", "display-name": "display_name", "user": "user"} + + def __init__(self, access=None, display_name=None, user=None, **unknown_fields): + """Access : str + display_name : str + user : str + """ + access_ = access + display_name_ = display_name + user_ = user + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if display_name_ is not None and not isinstance(display_name_, (bytes, str)): + raise Exception( + f"Expected display_name_ to be a str, received: {type(display_name_)}" + ) + + if user_ is not None and not isinstance(user_, (bytes, str)): + raise Exception(f"Expected user_ to be a str, received: {type(user_)}") + + self.access = access_ + self.display_name = display_name_ + self.user = user_ + self.unknown_fields = unknown_fields + + +class OperationQueryArgs(Type): + _toSchema = { + "actions": "actions", + "applications": "applications", + "limit": "limit", + "machines": "machines", + "offset": "offset", + "status": "status", + "units": "units", + } + _toPy = { + "actions": "actions", + "applications": "applications", + "limit": "limit", + "machines": "machines", + "offset": "offset", + "status": "status", + "units": "units", + } + + def __init__( + self, + actions=None, + applications=None, + limit=None, + machines=None, + offset=None, + status=None, + units=None, + **unknown_fields, + ): + """Actions : typing.Sequence[str] + applications : typing.Sequence[str] + limit : int + machines : typing.Sequence[str] + offset : int + status : typing.Sequence[str] + units : typing.Sequence[str] + """ + actions_ = actions + applications_ = applications + limit_ = limit + machines_ = machines + offset_ = offset + status_ = status + units_ = units + + # Validate arguments against known Juju API types. + if actions_ is not None and not isinstance(actions_, (bytes, str, list)): + raise Exception( + f"Expected actions_ to be a Sequence, received: {type(actions_)}" + ) + + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + if limit_ is not None and not isinstance(limit_, int): + raise Exception(f"Expected limit_ to be a int, received: {type(limit_)}") + + if machines_ is not None and not isinstance(machines_, (bytes, str, list)): + raise Exception( + f"Expected machines_ to be a Sequence, received: {type(machines_)}" + ) + + if offset_ is not None and not isinstance(offset_, int): + raise Exception(f"Expected offset_ to be a int, received: {type(offset_)}") + + if status_ is not None and not isinstance(status_, (bytes, str, list)): + raise Exception( + f"Expected status_ to be a Sequence, received: {type(status_)}" + ) + + if units_ is not None and not isinstance(units_, (bytes, str, list)): + raise Exception( + f"Expected units_ to be a Sequence, received: {type(units_)}" + ) + + self.actions = actions_ + self.applications = applications_ + self.limit = limit_ + self.machines = machines_ + self.offset = offset_ + self.status = status_ + self.units = units_ + self.unknown_fields = unknown_fields + + +class OperationResult(Type): + _toSchema = { + "actions": "actions", + "completed": "completed", + "enqueued": "enqueued", + "error": "error", + "fail": "fail", + "operation": "operation", + "started": "started", + "status": "status", + "summary": "summary", + } + _toPy = { + "actions": "actions", + "completed": "completed", + "enqueued": "enqueued", + "error": "error", + "fail": "fail", + "operation": "operation", + "started": "started", + "status": "status", + "summary": "summary", + } + + def __init__( + self, + actions=None, + completed=None, + enqueued=None, + error=None, + fail=None, + operation=None, + started=None, + status=None, + summary=None, + **unknown_fields, + ): + """Actions : typing.Sequence[~ActionResult] + completed : str + enqueued : str + error : Error + fail : str + operation : str + started : str + status : str + summary : str + """ + actions_ = [ActionResult.from_json(o) for o in actions or []] + completed_ = completed + enqueued_ = enqueued + error_ = Error.from_json(error) if error else None + fail_ = fail + operation_ = operation + started_ = started + status_ = status + summary_ = summary + + # Validate arguments against known Juju API types. + if actions_ is not None and not isinstance(actions_, (bytes, str, list)): + raise Exception( + f"Expected actions_ to be a Sequence, received: {type(actions_)}" + ) + + if completed_ is not None and not isinstance(completed_, (bytes, str)): + raise Exception( + f"Expected completed_ to be a str, received: {type(completed_)}" + ) + + if enqueued_ is not None and not isinstance(enqueued_, (bytes, str)): + raise Exception( + f"Expected enqueued_ to be a str, received: {type(enqueued_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if fail_ is not None and not isinstance(fail_, (bytes, str)): + raise Exception(f"Expected fail_ to be a str, received: {type(fail_)}") + + if operation_ is not None and not isinstance(operation_, (bytes, str)): + raise Exception( + f"Expected operation_ to be a str, received: {type(operation_)}" + ) + + if started_ is not None and not isinstance(started_, (bytes, str)): + raise Exception( + f"Expected started_ to be a str, received: {type(started_)}" + ) + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + if summary_ is not None and not isinstance(summary_, (bytes, str)): + raise Exception( + f"Expected summary_ to be a str, received: {type(summary_)}" + ) + + self.actions = actions_ + self.completed = completed_ + self.enqueued = enqueued_ + self.error = error_ + self.fail = fail_ + self.operation = operation_ + self.started = started_ + self.status = status_ + self.summary = summary_ + self.unknown_fields = unknown_fields + + +class OperationResults(Type): + _toSchema = {"results": "results", "truncated": "truncated"} + _toPy = {"results": "results", "truncated": "truncated"} + + def __init__(self, results=None, truncated=None, **unknown_fields): + """Results : typing.Sequence[~OperationResult] + truncated : bool + """ + results_ = [OperationResult.from_json(o) for o in results or []] + truncated_ = truncated + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + if truncated_ is not None and not isinstance(truncated_, bool): + raise Exception( + f"Expected truncated_ to be a bool, received: {type(truncated_)}" + ) + + self.results = results_ + self.truncated = truncated_ + self.unknown_fields = unknown_fields + + +class Payload(Type): + _toSchema = { + "class_": "class", + "id_": "id", + "labels": "labels", + "machine": "machine", + "status": "status", + "type_": "type", + "unit": "unit", + } + _toPy = { + "class": "class_", + "id": "id_", + "labels": "labels", + "machine": "machine", + "status": "status", + "type": "type_", + "unit": "unit", + } + + def __init__( + self, + class_=None, + id_=None, + labels=None, + machine=None, + status=None, + type_=None, + unit=None, + **unknown_fields, + ): + """class_ : str + id_ : str + labels : typing.Sequence[str] + machine : str + status : str + type_ : str + unit : str + """ + class__ = class_ + id__ = id_ + labels_ = labels + machine_ = machine + status_ = status + type__ = type_ + unit_ = unit + + # Validate arguments against known Juju API types. + if class__ is not None and not isinstance(class__, (bytes, str)): + raise Exception(f"Expected class__ to be a str, received: {type(class__)}") + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if labels_ is not None and not isinstance(labels_, (bytes, str, list)): + raise Exception( + f"Expected labels_ to be a Sequence, received: {type(labels_)}" + ) + + if machine_ is not None and not isinstance(machine_, (bytes, str)): + raise Exception( + f"Expected machine_ to be a str, received: {type(machine_)}" + ) + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + if unit_ is not None and not isinstance(unit_, (bytes, str)): + raise Exception(f"Expected unit_ to be a str, received: {type(unit_)}") + + self.class_ = class__ + self.id_ = id__ + self.labels = labels_ + self.machine = machine_ + self.status = status_ + self.type_ = type__ + self.unit = unit_ + self.unknown_fields = unknown_fields + + +class PayloadListArgs(Type): + _toSchema = {"patterns": "patterns"} + _toPy = {"patterns": "patterns"} + + def __init__(self, patterns=None, **unknown_fields): + """Patterns : typing.Sequence[str]""" + patterns_ = patterns + + # Validate arguments against known Juju API types. + if patterns_ is not None and not isinstance(patterns_, (bytes, str, list)): + raise Exception( + f"Expected patterns_ to be a Sequence, received: {type(patterns_)}" + ) + + self.patterns = patterns_ + self.unknown_fields = unknown_fields + + +class PayloadListResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~Payload]""" + results_ = [Payload.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class PendingResourceUpload(Type): + _toSchema = {"filename": "Filename", "name": "Name", "type_": "Type"} + _toPy = {"Filename": "filename", "Name": "name", "Type": "type_"} + + def __init__(self, filename=None, name=None, type_=None, **unknown_fields): + """Filename : str + name : str + type_ : str + """ + filename_ = filename + name_ = name + type__ = type_ + + # Validate arguments against known Juju API types. + if filename_ is not None and not isinstance(filename_, (bytes, str)): + raise Exception( + f"Expected filename_ to be a str, received: {type(filename_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.filename = filename_ + self.name = name_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class Placement(Type): + _toSchema = {"directive": "directive", "scope": "scope"} + _toPy = {"directive": "directive", "scope": "scope"} + + def __init__(self, directive=None, scope=None, **unknown_fields): + """Directive : str + scope : str + """ + directive_ = directive + scope_ = scope + + # Validate arguments against known Juju API types. + if directive_ is not None and not isinstance(directive_, (bytes, str)): + raise Exception( + f"Expected directive_ to be a str, received: {type(directive_)}" + ) + + if scope_ is not None and not isinstance(scope_, (bytes, str)): + raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") + + self.directive = directive_ + self.scope = scope_ + self.unknown_fields = unknown_fields + + +class ProvisioningScriptParams(Type): + _toSchema = { + "data_dir": "data-dir", + "disable_package_commands": "disable-package-commands", + "machine_id": "machine-id", + "nonce": "nonce", + } + _toPy = { + "data-dir": "data_dir", + "disable-package-commands": "disable_package_commands", + "machine-id": "machine_id", + "nonce": "nonce", + } + + def __init__( + self, + data_dir=None, + disable_package_commands=None, + machine_id=None, + nonce=None, + **unknown_fields, + ): + """data_dir : str + disable_package_commands : bool + machine_id : str + nonce : str + """ + data_dir_ = data_dir + disable_package_commands_ = disable_package_commands + machine_id_ = machine_id + nonce_ = nonce + + # Validate arguments against known Juju API types. + if data_dir_ is not None and not isinstance(data_dir_, (bytes, str)): + raise Exception( + f"Expected data_dir_ to be a str, received: {type(data_dir_)}" + ) + + if disable_package_commands_ is not None and not isinstance( + disable_package_commands_, bool + ): + raise Exception( + f"Expected disable_package_commands_ to be a bool, received: {type(disable_package_commands_)}" + ) + + if machine_id_ is not None and not isinstance(machine_id_, (bytes, str)): + raise Exception( + f"Expected machine_id_ to be a str, received: {type(machine_id_)}" + ) + + if nonce_ is not None and not isinstance(nonce_, (bytes, str)): + raise Exception(f"Expected nonce_ to be a str, received: {type(nonce_)}") + + self.data_dir = data_dir_ + self.disable_package_commands = disable_package_commands_ + self.machine_id = machine_id_ + self.nonce = nonce_ + self.unknown_fields = unknown_fields + + +class ProvisioningScriptResult(Type): + _toSchema = {"script": "script"} + _toPy = {"script": "script"} + + def __init__(self, script=None, **unknown_fields): + """Script : str""" + script_ = script + + # Validate arguments against known Juju API types. + if script_ is not None and not isinstance(script_, (bytes, str)): + raise Exception(f"Expected script_ to be a str, received: {type(script_)}") + + self.script = script_ + self.unknown_fields = unknown_fields + + +class Proxy(Type): + _toSchema = {"config": "config", "type_": "type"} + _toPy = {"config": "config", "type": "type_"} + + def __init__(self, config=None, type_=None, **unknown_fields): + """Config : typing.Mapping[str, typing.Any] + type_ : str + """ + config_ = config + type__ = type_ + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + self.config = config_ + self.type_ = type__ + self.unknown_fields = unknown_fields + + +class QueryApplicationOffersResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ApplicationOfferAdminDetails]""" + results_ = [ApplicationOfferAdminDetails.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class QueryApplicationOffersResultsV5(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ApplicationOfferAdminDetailsV5]""" + results_ = [ApplicationOfferAdminDetailsV5.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class RedirectInfoResult(Type): + _toSchema = {"ca_cert": "ca-cert", "servers": "servers"} + _toPy = {"ca-cert": "ca_cert", "servers": "servers"} + + def __init__(self, ca_cert=None, servers=None, **unknown_fields): + """ca_cert : str + servers : typing.Sequence[~HostPort] + """ + ca_cert_ = ca_cert + servers_ = [HostPort.from_json(o) for o in servers or []] + + # Validate arguments against known Juju API types. + if ca_cert_ is not None and not isinstance(ca_cert_, (bytes, str)): + raise Exception( + f"Expected ca_cert_ to be a str, received: {type(ca_cert_)}" + ) + + if servers_ is not None and not isinstance(servers_, (bytes, str, list)): + raise Exception( + f"Expected servers_ to be a Sequence, received: {type(servers_)}" + ) + + self.ca_cert = ca_cert_ + self.servers = servers_ + self.unknown_fields = unknown_fields + + +class RegionDefaults(Type): + _toSchema = {"region_name": "region-name", "value": "value"} + _toPy = {"region-name": "region_name", "value": "value"} + + def __init__(self, region_name=None, value=None, **unknown_fields): + """region_name : str + value : Any + """ + region_name_ = region_name + value_ = value + + # Validate arguments against known Juju API types. + if region_name_ is not None and not isinstance(region_name_, (bytes, str)): + raise Exception( + f"Expected region_name_ to be a str, received: {type(region_name_)}" + ) + + self.region_name = region_name_ + self.value = value_ + self.unknown_fields = unknown_fields + + +class RelationData(Type): + _toSchema = {"inscope": "InScope", "unitdata": "UnitData"} + _toPy = {"InScope": "inscope", "UnitData": "unitdata"} + + def __init__(self, inscope=None, unitdata=None, **unknown_fields): + """Inscope : bool + unitdata : typing.Mapping[str, typing.Any] + """ + inscope_ = inscope + unitdata_ = unitdata + + # Validate arguments against known Juju API types. + if inscope_ is not None and not isinstance(inscope_, bool): + raise Exception( + f"Expected inscope_ to be a bool, received: {type(inscope_)}" + ) + + if unitdata_ is not None and not isinstance(unitdata_, dict): + raise Exception( + f"Expected unitdata_ to be a Mapping, received: {type(unitdata_)}" + ) + + self.inscope = inscope_ + self.unitdata = unitdata_ + self.unknown_fields = unknown_fields + + +class RelationStatus(Type): + _toSchema = { + "endpoints": "endpoints", + "id_": "id", + "interface": "interface", + "key": "key", + "scope": "scope", + "status": "status", + } + _toPy = { + "endpoints": "endpoints", + "id": "id_", + "interface": "interface", + "key": "key", + "scope": "scope", + "status": "status", + } + + def __init__( + self, + endpoints=None, + id_=None, + interface=None, + key=None, + scope=None, + status=None, + **unknown_fields, + ): + """Endpoints : typing.Sequence[~EndpointStatus] + id_ : int + interface : str + key : str + scope : str + status : DetailedStatus + """ + endpoints_ = [EndpointStatus.from_json(o) for o in endpoints or []] + id__ = id_ + interface_ = interface + key_ = key + scope_ = scope + status_ = DetailedStatus.from_json(status) if status else None + + # Validate arguments against known Juju API types. + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if id__ is not None and not isinstance(id__, int): + raise Exception(f"Expected id__ to be a int, received: {type(id__)}") + + if interface_ is not None and not isinstance(interface_, (bytes, str)): + raise Exception( + f"Expected interface_ to be a str, received: {type(interface_)}" + ) + + if key_ is not None and not isinstance(key_, (bytes, str)): + raise Exception(f"Expected key_ to be a str, received: {type(key_)}") + + if scope_ is not None and not isinstance(scope_, (bytes, str)): + raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") + + if status_ is not None and not isinstance(status_, (dict, DetailedStatus)): + raise Exception( + f"Expected status_ to be a DetailedStatus, received: {type(status_)}" + ) + + self.endpoints = endpoints_ + self.id_ = id__ + self.interface = interface_ + self.key = key_ + self.scope = scope_ + self.status = status_ + self.unknown_fields = unknown_fields + + +class RelationSuspendedArg(Type): + _toSchema = { + "message": "message", + "relation_id": "relation-id", + "suspended": "suspended", + } + _toPy = { + "message": "message", + "relation-id": "relation_id", + "suspended": "suspended", + } + + def __init__( + self, message=None, relation_id=None, suspended=None, **unknown_fields + ): + """Message : str + relation_id : int + suspended : bool + """ + message_ = message + relation_id_ = relation_id + suspended_ = suspended + + # Validate arguments against known Juju API types. + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + if relation_id_ is not None and not isinstance(relation_id_, int): + raise Exception( + f"Expected relation_id_ to be a int, received: {type(relation_id_)}" + ) + + if suspended_ is not None and not isinstance(suspended_, bool): + raise Exception( + f"Expected suspended_ to be a bool, received: {type(suspended_)}" + ) + + self.message = message_ + self.relation_id = relation_id_ + self.suspended = suspended_ + self.unknown_fields = unknown_fields + + +class RelationSuspendedArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~RelationSuspendedArg]""" + args_ = [RelationSuspendedArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class RemoteApplicationInfo(Type): + _toSchema = { + "description": "description", + "endpoints": "endpoints", + "icon_url_path": "icon-url-path", + "model_tag": "model-tag", + "name": "name", + "offer_url": "offer-url", + "source_model_label": "source-model-label", + } + _toPy = { + "description": "description", + "endpoints": "endpoints", + "icon-url-path": "icon_url_path", + "model-tag": "model_tag", + "name": "name", + "offer-url": "offer_url", + "source-model-label": "source_model_label", + } + + def __init__( + self, + description=None, + endpoints=None, + icon_url_path=None, + model_tag=None, + name=None, + offer_url=None, + source_model_label=None, + **unknown_fields, + ): + """Description : str + endpoints : typing.Sequence[~RemoteEndpoint] + icon_url_path : str + model_tag : str + name : str + offer_url : str + source_model_label : str + """ + description_ = description + endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] + icon_url_path_ = icon_url_path + model_tag_ = model_tag + name_ = name + offer_url_ = offer_url + source_model_label_ = source_model_label + + # Validate arguments against known Juju API types. + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if icon_url_path_ is not None and not isinstance(icon_url_path_, (bytes, str)): + raise Exception( + f"Expected icon_url_path_ to be a str, received: {type(icon_url_path_)}" + ) + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): + raise Exception( + f"Expected offer_url_ to be a str, received: {type(offer_url_)}" + ) + + if source_model_label_ is not None and not isinstance( + source_model_label_, (bytes, str) + ): + raise Exception( + f"Expected source_model_label_ to be a str, received: {type(source_model_label_)}" + ) + + self.description = description_ + self.endpoints = endpoints_ + self.icon_url_path = icon_url_path_ + self.model_tag = model_tag_ + self.name = name_ + self.offer_url = offer_url_ + self.source_model_label = source_model_label_ + self.unknown_fields = unknown_fields + + +class RemoteApplicationInfoResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : RemoteApplicationInfo + """ + error_ = Error.from_json(error) if error else None + result_ = RemoteApplicationInfo.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance( + result_, (dict, RemoteApplicationInfo) + ): + raise Exception( + f"Expected result_ to be a RemoteApplicationInfo, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class RemoteApplicationInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~RemoteApplicationInfoResult]""" + results_ = [RemoteApplicationInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class RemoteApplicationStatus(Type): + _toSchema = { + "endpoints": "endpoints", + "err": "err", + "life": "life", + "offer_name": "offer-name", + "offer_url": "offer-url", + "relations": "relations", + "status": "status", + } + _toPy = { + "endpoints": "endpoints", + "err": "err", + "life": "life", + "offer-name": "offer_name", + "offer-url": "offer_url", + "relations": "relations", + "status": "status", + } + + def __init__( + self, + endpoints=None, + err=None, + life=None, + offer_name=None, + offer_url=None, + relations=None, + status=None, + **unknown_fields, + ): + """Endpoints : typing.Sequence[~RemoteEndpoint] + err : Error + life : str + offer_name : str + offer_url : str + relations : typing.Mapping[str, typing.Sequence[str]] + status : DetailedStatus + """ + endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] + err_ = Error.from_json(err) if err else None + life_ = life + offer_name_ = offer_name + offer_url_ = offer_url + relations_ = relations + status_ = DetailedStatus.from_json(status) if status else None + + # Validate arguments against known Juju API types. + if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): + raise Exception( + f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" + ) + + if err_ is not None and not isinstance(err_, (dict, Error)): + raise Exception(f"Expected err_ to be a Error, received: {type(err_)}") + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): + raise Exception( + f"Expected offer_name_ to be a str, received: {type(offer_name_)}" + ) + + if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): + raise Exception( + f"Expected offer_url_ to be a str, received: {type(offer_url_)}" + ) + + if relations_ is not None and not isinstance(relations_, dict): + raise Exception( + f"Expected relations_ to be a Mapping, received: {type(relations_)}" + ) + + if status_ is not None and not isinstance(status_, (dict, DetailedStatus)): + raise Exception( + f"Expected status_ to be a DetailedStatus, received: {type(status_)}" + ) + + self.endpoints = endpoints_ + self.err = err_ + self.life = life_ + self.offer_name = offer_name_ + self.offer_url = offer_url_ + self.relations = relations_ + self.status = status_ + self.unknown_fields = unknown_fields + + +class RemoteEndpoint(Type): + _toSchema = { + "interface": "interface", + "limit": "limit", + "name": "name", + "role": "role", + } + _toPy = {"interface": "interface", "limit": "limit", "name": "name", "role": "role"} + + def __init__( + self, interface=None, limit=None, name=None, role=None, **unknown_fields + ): + """Interface : str + limit : int + name : str + role : str + """ + interface_ = interface + limit_ = limit + name_ = name + role_ = role + + # Validate arguments against known Juju API types. + if interface_ is not None and not isinstance(interface_, (bytes, str)): + raise Exception( + f"Expected interface_ to be a str, received: {type(interface_)}" + ) + + if limit_ is not None and not isinstance(limit_, int): + raise Exception(f"Expected limit_ to be a int, received: {type(limit_)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if role_ is not None and not isinstance(role_, (bytes, str)): + raise Exception(f"Expected role_ to be a str, received: {type(role_)}") + + self.interface = interface_ + self.limit = limit_ + self.name = name_ + self.role = role_ + self.unknown_fields = unknown_fields + + +class RemoteSpace(Type): + _toSchema = { + "cloud_type": "cloud-type", + "name": "name", + "provider_attributes": "provider-attributes", + "provider_id": "provider-id", + "subnets": "subnets", + } + _toPy = { + "cloud-type": "cloud_type", + "name": "name", + "provider-attributes": "provider_attributes", + "provider-id": "provider_id", + "subnets": "subnets", + } + + def __init__( + self, + cloud_type=None, + name=None, + provider_attributes=None, + provider_id=None, + subnets=None, + **unknown_fields, + ): + """cloud_type : str + name : str + provider_attributes : typing.Mapping[str, typing.Any] + provider_id : str + subnets : typing.Sequence[~Subnet] + """ + cloud_type_ = cloud_type + name_ = name + provider_attributes_ = provider_attributes + provider_id_ = provider_id + subnets_ = [Subnet.from_json(o) for o in subnets or []] + + # Validate arguments against known Juju API types. + if cloud_type_ is not None and not isinstance(cloud_type_, (bytes, str)): + raise Exception( + f"Expected cloud_type_ to be a str, received: {type(cloud_type_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if provider_attributes_ is not None and not isinstance( + provider_attributes_, dict + ): + raise Exception( + f"Expected provider_attributes_ to be a Mapping, received: {type(provider_attributes_)}" + ) + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if subnets_ is not None and not isinstance(subnets_, (bytes, str, list)): + raise Exception( + f"Expected subnets_ to be a Sequence, received: {type(subnets_)}" + ) + + self.cloud_type = cloud_type_ + self.name = name_ + self.provider_attributes = provider_attributes_ + self.provider_id = provider_id_ + self.subnets = subnets_ + self.unknown_fields = unknown_fields + + +class RemoveBlocksArgs(Type): + _toSchema = {"all_": "all"} + _toPy = {"all": "all_"} + + def __init__(self, all_=None, **unknown_fields): + """all_ : bool""" + all__ = all_ + + # Validate arguments against known Juju API types. + if all__ is not None and not isinstance(all__, bool): + raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") + + self.all_ = all__ + self.unknown_fields = unknown_fields + + +class RemoveSecretBackendArg(Type): + _toSchema = {"force": "force", "name": "name"} + _toPy = {"force": "force", "name": "name"} + + def __init__(self, force=None, name=None, **unknown_fields): + """Force : bool + name : str + """ + force_ = force + name_ = name + + # Validate arguments against known Juju API types. + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + self.force = force_ + self.name = name_ + self.unknown_fields = unknown_fields + + +class RemoveSecretBackendArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~RemoveSecretBackendArg]""" + args_ = [RemoveSecretBackendArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class RemoveSpaceParam(Type): + _toSchema = {"dry_run": "dry-run", "force": "force", "space": "space"} + _toPy = {"dry-run": "dry_run", "force": "force", "space": "space"} + + def __init__(self, dry_run=None, force=None, space=None, **unknown_fields): + """dry_run : bool + force : bool + space : Entity + """ + dry_run_ = dry_run + force_ = force + space_ = Entity.from_json(space) if space else None + + # Validate arguments against known Juju API types. + if dry_run_ is not None and not isinstance(dry_run_, bool): + raise Exception( + f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if space_ is not None and not isinstance(space_, (dict, Entity)): + raise Exception(f"Expected space_ to be a Entity, received: {type(space_)}") + + self.dry_run = dry_run_ + self.force = force_ + self.space = space_ + self.unknown_fields = unknown_fields + + +class RemoveSpaceParams(Type): + _toSchema = {"space_param": "space-param"} + _toPy = {"space-param": "space_param"} + + def __init__(self, space_param=None, **unknown_fields): + """space_param : typing.Sequence[~RemoveSpaceParam]""" + space_param_ = [RemoveSpaceParam.from_json(o) for o in space_param or []] + + # Validate arguments against known Juju API types. + if space_param_ is not None and not isinstance( + space_param_, (bytes, str, list) + ): + raise Exception( + f"Expected space_param_ to be a Sequence, received: {type(space_param_)}" + ) + + self.space_param = space_param_ + self.unknown_fields = unknown_fields + + +class RemoveSpaceResult(Type): + _toSchema = { + "bindings": "bindings", + "constraints": "constraints", + "controller_settings": "controller-settings", + "error": "error", + } + _toPy = { + "bindings": "bindings", + "constraints": "constraints", + "controller-settings": "controller_settings", + "error": "error", + } + + def __init__( + self, + bindings=None, + constraints=None, + controller_settings=None, + error=None, + **unknown_fields, + ): + """Bindings : typing.Sequence[~Entity] + constraints : typing.Sequence[~Entity] + controller_settings : typing.Sequence[str] + error : Error + """ + bindings_ = [Entity.from_json(o) for o in bindings or []] + constraints_ = [Entity.from_json(o) for o in constraints or []] + controller_settings_ = controller_settings + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if bindings_ is not None and not isinstance(bindings_, (bytes, str, list)): + raise Exception( + f"Expected bindings_ to be a Sequence, received: {type(bindings_)}" + ) + + if constraints_ is not None and not isinstance( + constraints_, (bytes, str, list) + ): + raise Exception( + f"Expected constraints_ to be a Sequence, received: {type(constraints_)}" + ) + + if controller_settings_ is not None and not isinstance( + controller_settings_, (bytes, str, list) + ): + raise Exception( + f"Expected controller_settings_ to be a Sequence, received: {type(controller_settings_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.bindings = bindings_ + self.constraints = constraints_ + self.controller_settings = controller_settings_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class RemoveSpaceResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~RemoveSpaceResult]""" + results_ = [RemoveSpaceResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class RemoveStorage(Type): + _toSchema = {"storage": "storage"} + _toPy = {"storage": "storage"} + + def __init__(self, storage=None, **unknown_fields): + """Storage : typing.Sequence[~RemoveStorageInstance]""" + storage_ = [RemoveStorageInstance.from_json(o) for o in storage or []] + + # Validate arguments against known Juju API types. + if storage_ is not None and not isinstance(storage_, (bytes, str, list)): + raise Exception( + f"Expected storage_ to be a Sequence, received: {type(storage_)}" + ) + + self.storage = storage_ + self.unknown_fields = unknown_fields + + +class RemoveStorageInstance(Type): + _toSchema = { + "destroy_attachments": "destroy-attachments", + "destroy_storage": "destroy-storage", + "force": "force", + "max_wait": "max-wait", + "tag": "tag", + } + _toPy = { + "destroy-attachments": "destroy_attachments", + "destroy-storage": "destroy_storage", + "force": "force", + "max-wait": "max_wait", + "tag": "tag", + } + + def __init__( + self, + destroy_attachments=None, + destroy_storage=None, + force=None, + max_wait=None, + tag=None, + **unknown_fields, + ): + """destroy_attachments : bool + destroy_storage : bool + force : bool + max_wait : int + tag : str + """ + destroy_attachments_ = destroy_attachments + destroy_storage_ = destroy_storage + force_ = force + max_wait_ = max_wait + tag_ = tag + + # Validate arguments against known Juju API types. + if destroy_attachments_ is not None and not isinstance( + destroy_attachments_, bool + ): + raise Exception( + f"Expected destroy_attachments_ to be a bool, received: {type(destroy_attachments_)}" + ) + + if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): + raise Exception( + f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if max_wait_ is not None and not isinstance(max_wait_, int): + raise Exception( + f"Expected max_wait_ to be a int, received: {type(max_wait_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.destroy_attachments = destroy_attachments_ + self.destroy_storage = destroy_storage_ + self.force = force_ + self.max_wait = max_wait_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class RenameSpaceParams(Type): + _toSchema = {"from_space_tag": "from-space-tag", "to_space_tag": "to-space-tag"} + _toPy = {"from-space-tag": "from_space_tag", "to-space-tag": "to_space_tag"} + + def __init__(self, from_space_tag=None, to_space_tag=None, **unknown_fields): + """from_space_tag : str + to_space_tag : str + """ + from_space_tag_ = from_space_tag + to_space_tag_ = to_space_tag + + # Validate arguments against known Juju API types. + if from_space_tag_ is not None and not isinstance( + from_space_tag_, (bytes, str) + ): + raise Exception( + f"Expected from_space_tag_ to be a str, received: {type(from_space_tag_)}" + ) + + if to_space_tag_ is not None and not isinstance(to_space_tag_, (bytes, str)): + raise Exception( + f"Expected to_space_tag_ to be a str, received: {type(to_space_tag_)}" + ) + + self.from_space_tag = from_space_tag_ + self.to_space_tag = to_space_tag_ + self.unknown_fields = unknown_fields + + +class RenameSpacesParams(Type): + _toSchema = {"changes": "changes"} + _toPy = {"changes": "changes"} + + def __init__(self, changes=None, **unknown_fields): + """Changes : typing.Sequence[~RenameSpaceParams]""" + changes_ = [RenameSpaceParams.from_json(o) for o in changes or []] + + # Validate arguments against known Juju API types. + if changes_ is not None and not isinstance(changes_, (bytes, str, list)): + raise Exception( + f"Expected changes_ to be a Sequence, received: {type(changes_)}" + ) + + self.changes = changes_ + self.unknown_fields = unknown_fields + + +class ResolveCharmWithChannel(Type): + _toSchema = { + "charm_origin": "charm-origin", + "reference": "reference", + "switch_charm": "switch-charm", + } + _toPy = { + "charm-origin": "charm_origin", + "reference": "reference", + "switch-charm": "switch_charm", + } + + def __init__( + self, charm_origin=None, reference=None, switch_charm=None, **unknown_fields + ): + """charm_origin : CharmOrigin + reference : str + switch_charm : bool + """ + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + reference_ = reference + switch_charm_ = switch_charm + + # Validate arguments against known Juju API types. + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if reference_ is not None and not isinstance(reference_, (bytes, str)): + raise Exception( + f"Expected reference_ to be a str, received: {type(reference_)}" + ) + + if switch_charm_ is not None and not isinstance(switch_charm_, bool): + raise Exception( + f"Expected switch_charm_ to be a bool, received: {type(switch_charm_)}" + ) + + self.charm_origin = charm_origin_ + self.reference = reference_ + self.switch_charm = switch_charm_ + self.unknown_fields = unknown_fields + + +class ResolveCharmWithChannelResult(Type): + _toSchema = { + "charm_origin": "charm-origin", + "error": "error", + "supported_bases": "supported-bases", + "url": "url", + } + _toPy = { + "charm-origin": "charm_origin", + "error": "error", + "supported-bases": "supported_bases", + "url": "url", + } + + def __init__( + self, + charm_origin=None, + error=None, + supported_bases=None, + url=None, + **unknown_fields, + ): + """charm_origin : CharmOrigin + error : Error + supported_bases : typing.Sequence[~Base] + url : str + """ + charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None + error_ = Error.from_json(error) if error else None + supported_bases_ = [Base.from_json(o) for o in supported_bases or []] + url_ = url + + # Validate arguments against known Juju API types. + if charm_origin_ is not None and not isinstance( + charm_origin_, (dict, CharmOrigin) + ): + raise Exception( + f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if supported_bases_ is not None and not isinstance( + supported_bases_, (bytes, str, list) + ): + raise Exception( + f"Expected supported_bases_ to be a Sequence, received: {type(supported_bases_)}" + ) + + if url_ is not None and not isinstance(url_, (bytes, str)): + raise Exception(f"Expected url_ to be a str, received: {type(url_)}") + + self.charm_origin = charm_origin_ + self.error = error_ + self.supported_bases = supported_bases_ + self.url = url_ + self.unknown_fields = unknown_fields + + +class ResolveCharmWithChannelResults(Type): + _toSchema = {"results": "Results"} + _toPy = {"Results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ResolveCharmWithChannelResult]""" + results_ = [ResolveCharmWithChannelResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ResolveCharmsWithChannel(Type): + _toSchema = {"macaroon": "macaroon", "resolve": "resolve"} + _toPy = {"macaroon": "macaroon", "resolve": "resolve"} + + def __init__(self, macaroon=None, resolve=None, **unknown_fields): + """Macaroon : Macaroon + resolve : typing.Sequence[~ResolveCharmWithChannel] + """ + macaroon_ = Macaroon.from_json(macaroon) if macaroon else None + resolve_ = [ResolveCharmWithChannel.from_json(o) for o in resolve or []] + + # Validate arguments against known Juju API types. + if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): + raise Exception( + f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" + ) + + if resolve_ is not None and not isinstance(resolve_, (bytes, str, list)): + raise Exception( + f"Expected resolve_ to be a Sequence, received: {type(resolve_)}" + ) + + self.macaroon = macaroon_ + self.resolve = resolve_ + self.unknown_fields = unknown_fields + + +class Resource(Type): + _toSchema = { + "application": "application", + "charmresource": "CharmResource", + "description": "description", + "fingerprint": "fingerprint", + "id_": "id", + "name": "name", + "origin": "origin", + "path": "path", + "pending_id": "pending-id", + "revision": "revision", + "size": "size", + "timestamp": "timestamp", + "type_": "type", + "username": "username", + } + _toPy = { + "CharmResource": "charmresource", + "application": "application", + "description": "description", + "fingerprint": "fingerprint", + "id": "id_", + "name": "name", + "origin": "origin", + "path": "path", + "pending-id": "pending_id", + "revision": "revision", + "size": "size", + "timestamp": "timestamp", + "type": "type_", + "username": "username", + } + + def __init__( + self, + charmresource=None, + application=None, + description=None, + fingerprint=None, + id_=None, + name=None, + origin=None, + path=None, + pending_id=None, + revision=None, + size=None, + timestamp=None, + type_=None, + username=None, + **unknown_fields, + ): + """Charmresource : CharmResource + application : str + description : str + fingerprint : typing.Sequence[int] + id_ : str + name : str + origin : str + path : str + pending_id : str + revision : int + size : int + timestamp : str + type_ : str + username : str + """ + charmresource_ = ( + CharmResource.from_json(charmresource) if charmresource else None + ) + application_ = application + description_ = description + fingerprint_ = fingerprint + id__ = id_ + name_ = name + origin_ = origin + path_ = path + pending_id_ = pending_id + revision_ = revision + size_ = size + timestamp_ = timestamp + type__ = type_ + username_ = username + + # Validate arguments against known Juju API types. + if charmresource_ is not None and not isinstance( + charmresource_, (dict, CharmResource) + ): + raise Exception( + f"Expected charmresource_ to be a CharmResource, received: {type(charmresource_)}" + ) + + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if fingerprint_ is not None and not isinstance( + fingerprint_, (bytes, str, list) + ): + raise Exception( + f"Expected fingerprint_ to be a Sequence, received: {type(fingerprint_)}" + ) + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if origin_ is not None and not isinstance(origin_, (bytes, str)): + raise Exception(f"Expected origin_ to be a str, received: {type(origin_)}") + + if path_ is not None and not isinstance(path_, (bytes, str)): + raise Exception(f"Expected path_ to be a str, received: {type(path_)}") + + if pending_id_ is not None and not isinstance(pending_id_, (bytes, str)): + raise Exception( + f"Expected pending_id_ to be a str, received: {type(pending_id_)}" + ) + + if revision_ is not None and not isinstance(revision_, int): + raise Exception( + f"Expected revision_ to be a int, received: {type(revision_)}" + ) + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + if timestamp_ is not None and not isinstance(timestamp_, (bytes, str)): + raise Exception( + f"Expected timestamp_ to be a str, received: {type(timestamp_)}" + ) + + if type__ is not None and not isinstance(type__, (bytes, str)): + raise Exception(f"Expected type__ to be a str, received: {type(type__)}") + + if username_ is not None and not isinstance(username_, (bytes, str)): + raise Exception( + f"Expected username_ to be a str, received: {type(username_)}" + ) + + self.charmresource = charmresource_ + self.application = application_ + self.description = description_ + self.fingerprint = fingerprint_ + self.id_ = id__ + self.name = name_ + self.origin = origin_ + self.path = path_ + self.pending_id = pending_id_ + self.revision = revision_ + self.size = size_ + self.timestamp = timestamp_ + self.type_ = type__ + self.username = username_ + self.unknown_fields = unknown_fields + + +class ResourcesResult(Type): + _toSchema = { + "charm_store_resources": "charm-store-resources", + "error": "error", + "errorresult": "ErrorResult", + "resources": "resources", + "unit_resources": "unit-resources", + } + _toPy = { + "ErrorResult": "errorresult", + "charm-store-resources": "charm_store_resources", + "error": "error", + "resources": "resources", + "unit-resources": "unit_resources", + } + + def __init__( + self, + errorresult=None, + charm_store_resources=None, + error=None, + resources=None, + unit_resources=None, + **unknown_fields, + ): + """Errorresult : ErrorResult + charm_store_resources : typing.Sequence[~CharmResource] + error : Error + resources : typing.Sequence[~Resource] + unit_resources : typing.Sequence[~UnitResources] + """ + errorresult_ = ErrorResult.from_json(errorresult) if errorresult else None + charm_store_resources_ = [ + CharmResource.from_json(o) for o in charm_store_resources or [] + ] + error_ = Error.from_json(error) if error else None + resources_ = [Resource.from_json(o) for o in resources or []] + unit_resources_ = [UnitResources.from_json(o) for o in unit_resources or []] + + # Validate arguments against known Juju API types. + if errorresult_ is not None and not isinstance( + errorresult_, (dict, ErrorResult) + ): + raise Exception( + f"Expected errorresult_ to be a ErrorResult, received: {type(errorresult_)}" + ) + + if charm_store_resources_ is not None and not isinstance( + charm_store_resources_, (bytes, str, list) + ): + raise Exception( + f"Expected charm_store_resources_ to be a Sequence, received: {type(charm_store_resources_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if resources_ is not None and not isinstance(resources_, (bytes, str, list)): + raise Exception( + f"Expected resources_ to be a Sequence, received: {type(resources_)}" + ) + + if unit_resources_ is not None and not isinstance( + unit_resources_, (bytes, str, list) + ): + raise Exception( + f"Expected unit_resources_ to be a Sequence, received: {type(unit_resources_)}" + ) + + self.errorresult = errorresult_ + self.charm_store_resources = charm_store_resources_ + self.error = error_ + self.resources = resources_ + self.unit_resources = unit_resources_ + self.unknown_fields = unknown_fields + + +class ResourcesResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ResourcesResult]""" + results_ = [ResourcesResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class RetryProvisioningArgs(Type): + _toSchema = {"all_": "all", "machines": "machines"} + _toPy = {"all": "all_", "machines": "machines"} + + def __init__(self, all_=None, machines=None, **unknown_fields): + """all_ : bool + machines : typing.Sequence[str] + """ + all__ = all_ + machines_ = machines + + # Validate arguments against known Juju API types. + if all__ is not None and not isinstance(all__, bool): + raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") + + if machines_ is not None and not isinstance(machines_, (bytes, str, list)): + raise Exception( + f"Expected machines_ to be a Sequence, received: {type(machines_)}" + ) + + self.all_ = all__ + self.machines = machines_ + self.unknown_fields = unknown_fields + + +class RevokeCredentialArg(Type): + _toSchema = {"force": "force", "tag": "tag"} + _toPy = {"force": "force", "tag": "tag"} + + def __init__(self, force=None, tag=None, **unknown_fields): + """Force : bool + tag : str + """ + force_ = force + tag_ = tag + + # Validate arguments against known Juju API types. + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.force = force_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class RevokeCredentialArgs(Type): + _toSchema = {"credentials": "credentials"} + _toPy = {"credentials": "credentials"} + + def __init__(self, credentials=None, **unknown_fields): + """Credentials : typing.Sequence[~RevokeCredentialArg]""" + credentials_ = [RevokeCredentialArg.from_json(o) for o in credentials or []] + + # Validate arguments against known Juju API types. + if credentials_ is not None and not isinstance( + credentials_, (bytes, str, list) + ): + raise Exception( + f"Expected credentials_ to be a Sequence, received: {type(credentials_)}" + ) + + self.credentials = credentials_ + self.unknown_fields = unknown_fields + + +class RunParams(Type): + _toSchema = { + "applications": "applications", + "commands": "commands", + "execution_group": "execution-group", + "machines": "machines", + "parallel": "parallel", + "timeout": "timeout", + "units": "units", + "workload_context": "workload-context", + } + _toPy = { + "applications": "applications", + "commands": "commands", + "execution-group": "execution_group", + "machines": "machines", + "parallel": "parallel", + "timeout": "timeout", + "units": "units", + "workload-context": "workload_context", + } + + def __init__( + self, + applications=None, + commands=None, + execution_group=None, + machines=None, + parallel=None, + timeout=None, + units=None, + workload_context=None, + **unknown_fields, + ): + """Applications : typing.Sequence[str] + commands : str + execution_group : str + machines : typing.Sequence[str] + parallel : bool + timeout : int + units : typing.Sequence[str] + workload_context : bool + """ + applications_ = applications + commands_ = commands + execution_group_ = execution_group + machines_ = machines + parallel_ = parallel + timeout_ = timeout + units_ = units + workload_context_ = workload_context + + # Validate arguments against known Juju API types. + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + if commands_ is not None and not isinstance(commands_, (bytes, str)): + raise Exception( + f"Expected commands_ to be a str, received: {type(commands_)}" + ) + + if execution_group_ is not None and not isinstance( + execution_group_, (bytes, str) + ): + raise Exception( + f"Expected execution_group_ to be a str, received: {type(execution_group_)}" + ) + + if machines_ is not None and not isinstance(machines_, (bytes, str, list)): + raise Exception( + f"Expected machines_ to be a Sequence, received: {type(machines_)}" + ) + + if parallel_ is not None and not isinstance(parallel_, bool): + raise Exception( + f"Expected parallel_ to be a bool, received: {type(parallel_)}" + ) + + if timeout_ is not None and not isinstance(timeout_, int): + raise Exception( + f"Expected timeout_ to be a int, received: {type(timeout_)}" + ) + + if units_ is not None and not isinstance(units_, (bytes, str, list)): + raise Exception( + f"Expected units_ to be a Sequence, received: {type(units_)}" + ) + + if workload_context_ is not None and not isinstance(workload_context_, bool): + raise Exception( + f"Expected workload_context_ to be a bool, received: {type(workload_context_)}" + ) + + self.applications = applications_ + self.commands = commands_ + self.execution_group = execution_group_ + self.machines = machines_ + self.parallel = parallel_ + self.timeout = timeout_ + self.units = units_ + self.workload_context = workload_context_ + self.unknown_fields = unknown_fields + + +class SSHAddressResult(Type): + _toSchema = {"address": "address", "error": "error"} + _toPy = {"address": "address", "error": "error"} + + def __init__(self, address=None, error=None, **unknown_fields): + """Address : str + error : Error + """ + address_ = address + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if address_ is not None and not isinstance(address_, (bytes, str)): + raise Exception( + f"Expected address_ to be a str, received: {type(address_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.address = address_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class SSHAddressResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~SSHAddressResult]""" + results_ = [SSHAddressResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class SSHAddressesResult(Type): + _toSchema = {"addresses": "addresses", "error": "error"} + _toPy = {"addresses": "addresses", "error": "error"} + + def __init__(self, addresses=None, error=None, **unknown_fields): + """Addresses : typing.Sequence[str] + error : Error + """ + addresses_ = addresses + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if addresses_ is not None and not isinstance(addresses_, (bytes, str, list)): + raise Exception( + f"Expected addresses_ to be a Sequence, received: {type(addresses_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.addresses = addresses_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class SSHAddressesResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~SSHAddressesResult]""" + results_ = [SSHAddressesResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class SSHProxyResult(Type): + _toSchema = {"use_proxy": "use-proxy"} + _toPy = {"use-proxy": "use_proxy"} + + def __init__(self, use_proxy=None, **unknown_fields): + """use_proxy : bool""" + use_proxy_ = use_proxy + + # Validate arguments against known Juju API types. + if use_proxy_ is not None and not isinstance(use_proxy_, bool): + raise Exception( + f"Expected use_proxy_ to be a bool, received: {type(use_proxy_)}" + ) + + self.use_proxy = use_proxy_ + self.unknown_fields = unknown_fields + + +class SSHPublicKeysResult(Type): + _toSchema = {"error": "error", "public_keys": "public-keys"} + _toPy = {"error": "error", "public-keys": "public_keys"} + + def __init__(self, error=None, public_keys=None, **unknown_fields): + """Error : Error + public_keys : typing.Sequence[str] + """ + error_ = Error.from_json(error) if error else None + public_keys_ = public_keys + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if public_keys_ is not None and not isinstance( + public_keys_, (bytes, str, list) + ): + raise Exception( + f"Expected public_keys_ to be a Sequence, received: {type(public_keys_)}" + ) + + self.error = error_ + self.public_keys = public_keys_ + self.unknown_fields = unknown_fields + + +class SSHPublicKeysResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~SSHPublicKeysResult]""" + results_ = [SSHPublicKeysResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ScaleApplicationInfo(Type): + _toSchema = {"num_units": "num-units"} + _toPy = {"num-units": "num_units"} + + def __init__(self, num_units=None, **unknown_fields): + """num_units : int""" + num_units_ = num_units + + # Validate arguments against known Juju API types. + if num_units_ is not None and not isinstance(num_units_, int): + raise Exception( + f"Expected num_units_ to be a int, received: {type(num_units_)}" + ) + + self.num_units = num_units_ + self.unknown_fields = unknown_fields + + +class ScaleApplicationParams(Type): + _toSchema = { + "application_tag": "application-tag", + "force": "force", + "scale": "scale", + "scale_change": "scale-change", + } + _toPy = { + "application-tag": "application_tag", + "force": "force", + "scale": "scale", + "scale-change": "scale_change", + } + + def __init__( + self, + application_tag=None, + force=None, + scale=None, + scale_change=None, + **unknown_fields, + ): + """application_tag : str + force : bool + scale : int + scale_change : int + """ + application_tag_ = application_tag + force_ = force + scale_ = scale + scale_change_ = scale_change + + # Validate arguments against known Juju API types. + if application_tag_ is not None and not isinstance( + application_tag_, (bytes, str) + ): + raise Exception( + f"Expected application_tag_ to be a str, received: {type(application_tag_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if scale_ is not None and not isinstance(scale_, int): + raise Exception(f"Expected scale_ to be a int, received: {type(scale_)}") + + if scale_change_ is not None and not isinstance(scale_change_, int): + raise Exception( + f"Expected scale_change_ to be a int, received: {type(scale_change_)}" + ) + + self.application_tag = application_tag_ + self.force = force_ + self.scale = scale_ + self.scale_change = scale_change_ + self.unknown_fields = unknown_fields + + +class ScaleApplicationResult(Type): + _toSchema = {"error": "error", "info": "info"} + _toPy = {"error": "error", "info": "info"} + + def __init__(self, error=None, info=None, **unknown_fields): + """Error : Error + info : ScaleApplicationInfo + """ + error_ = Error.from_json(error) if error else None + info_ = ScaleApplicationInfo.from_json(info) if info else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if info_ is not None and not isinstance(info_, (dict, ScaleApplicationInfo)): + raise Exception( + f"Expected info_ to be a ScaleApplicationInfo, received: {type(info_)}" + ) + + self.error = error_ + self.info = info_ + self.unknown_fields = unknown_fields + + +class ScaleApplicationResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ScaleApplicationResult]""" + results_ = [ScaleApplicationResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class ScaleApplicationsParams(Type): + _toSchema = {"applications": "applications"} + _toPy = {"applications": "applications"} + + def __init__(self, applications=None, **unknown_fields): + """Applications : typing.Sequence[~ScaleApplicationParams]""" + applications_ = [ + ScaleApplicationParams.from_json(o) for o in applications or [] + ] + + # Validate arguments against known Juju API types. + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + self.applications = applications_ + self.unknown_fields = unknown_fields + + +class SecretBackend(Type): + _toSchema = { + "backend_type": "backend-type", + "config": "config", + "name": "name", + "token_rotate_interval": "token-rotate-interval", + } + _toPy = { + "backend-type": "backend_type", + "config": "config", + "name": "name", + "token-rotate-interval": "token_rotate_interval", + } + + def __init__( + self, + backend_type=None, + config=None, + name=None, + token_rotate_interval=None, + **unknown_fields, + ): + """backend_type : str + config : typing.Mapping[str, typing.Any] + name : str + token_rotate_interval : int + """ + backend_type_ = backend_type + config_ = config + name_ = name + token_rotate_interval_ = token_rotate_interval + + # Validate arguments against known Juju API types. + if backend_type_ is not None and not isinstance(backend_type_, (bytes, str)): + raise Exception( + f"Expected backend_type_ to be a str, received: {type(backend_type_)}" + ) + + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if token_rotate_interval_ is not None and not isinstance( + token_rotate_interval_, int + ): + raise Exception( + f"Expected token_rotate_interval_ to be a int, received: {type(token_rotate_interval_)}" + ) + + self.backend_type = backend_type_ + self.config = config_ + self.name = name_ + self.token_rotate_interval = token_rotate_interval_ + self.unknown_fields = unknown_fields + + +class SecretBackendResult(Type): + _toSchema = { + "error": "error", + "id_": "id", + "message": "message", + "num_secrets": "num-secrets", + "result": "result", + "status": "status", + } + _toPy = { + "error": "error", + "id": "id_", + "message": "message", + "num-secrets": "num_secrets", + "result": "result", + "status": "status", + } + + def __init__( + self, + error=None, + id_=None, + message=None, + num_secrets=None, + result=None, + status=None, + **unknown_fields, + ): + """Error : Error + id_ : str + message : str + num_secrets : int + result : SecretBackend + status : str + """ + error_ = Error.from_json(error) if error else None + id__ = id_ + message_ = message + num_secrets_ = num_secrets + result_ = SecretBackend.from_json(result) if result else None + status_ = status + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if message_ is not None and not isinstance(message_, (bytes, str)): + raise Exception( + f"Expected message_ to be a str, received: {type(message_)}" + ) + + if num_secrets_ is not None and not isinstance(num_secrets_, int): + raise Exception( + f"Expected num_secrets_ to be a int, received: {type(num_secrets_)}" + ) + + if result_ is not None and not isinstance(result_, (dict, SecretBackend)): + raise Exception( + f"Expected result_ to be a SecretBackend, received: {type(result_)}" + ) + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + self.error = error_ + self.id_ = id__ + self.message = message_ + self.num_secrets = num_secrets_ + self.result = result_ + self.status = status_ + self.unknown_fields = unknown_fields + + +class SecretContentParams(Type): + _toSchema = {"checksum": "checksum", "data": "data", "value_ref": "value-ref"} + _toPy = {"checksum": "checksum", "data": "data", "value-ref": "value_ref"} + + def __init__(self, checksum=None, data=None, value_ref=None, **unknown_fields): + """Checksum : str + data : typing.Mapping[str, str] + value_ref : SecretValueRef + """ + checksum_ = checksum + data_ = data + value_ref_ = SecretValueRef.from_json(value_ref) if value_ref else None + + # Validate arguments against known Juju API types. + if checksum_ is not None and not isinstance(checksum_, (bytes, str)): + raise Exception( + f"Expected checksum_ to be a str, received: {type(checksum_)}" + ) + + if data_ is not None and not isinstance(data_, dict): + raise Exception(f"Expected data_ to be a Mapping, received: {type(data_)}") + + if value_ref_ is not None and not isinstance( + value_ref_, (dict, SecretValueRef) + ): + raise Exception( + f"Expected value_ref_ to be a SecretValueRef, received: {type(value_ref_)}" + ) + + self.checksum = checksum_ + self.data = data_ + self.value_ref = value_ref_ + self.unknown_fields = unknown_fields + + +class SecretRevision(Type): + _toSchema = { + "backend_name": "backend-name", + "create_time": "create-time", + "expire_time": "expire-time", + "revision": "revision", + "update_time": "update-time", + "value_ref": "value-ref", + } + _toPy = { + "backend-name": "backend_name", + "create-time": "create_time", + "expire-time": "expire_time", + "revision": "revision", + "update-time": "update_time", + "value-ref": "value_ref", + } + + def __init__( + self, + backend_name=None, + create_time=None, + expire_time=None, + revision=None, + update_time=None, + value_ref=None, + **unknown_fields, + ): + """backend_name : str + create_time : str + expire_time : str + revision : int + update_time : str + value_ref : SecretValueRef + """ + backend_name_ = backend_name + create_time_ = create_time + expire_time_ = expire_time + revision_ = revision + update_time_ = update_time + value_ref_ = SecretValueRef.from_json(value_ref) if value_ref else None + + # Validate arguments against known Juju API types. + if backend_name_ is not None and not isinstance(backend_name_, (bytes, str)): + raise Exception( + f"Expected backend_name_ to be a str, received: {type(backend_name_)}" + ) + + if create_time_ is not None and not isinstance(create_time_, (bytes, str)): + raise Exception( + f"Expected create_time_ to be a str, received: {type(create_time_)}" + ) + + if expire_time_ is not None and not isinstance(expire_time_, (bytes, str)): + raise Exception( + f"Expected expire_time_ to be a str, received: {type(expire_time_)}" + ) + + if revision_ is not None and not isinstance(revision_, int): + raise Exception( + f"Expected revision_ to be a int, received: {type(revision_)}" + ) + + if update_time_ is not None and not isinstance(update_time_, (bytes, str)): + raise Exception( + f"Expected update_time_ to be a str, received: {type(update_time_)}" + ) + + if value_ref_ is not None and not isinstance( + value_ref_, (dict, SecretValueRef) + ): + raise Exception( + f"Expected value_ref_ to be a SecretValueRef, received: {type(value_ref_)}" + ) + + self.backend_name = backend_name_ + self.create_time = create_time_ + self.expire_time = expire_time_ + self.revision = revision_ + self.update_time = update_time_ + self.value_ref = value_ref_ + self.unknown_fields = unknown_fields + + +class SecretValueRef(Type): + _toSchema = {"backend_id": "backend-id", "revision_id": "revision-id"} + _toPy = {"backend-id": "backend_id", "revision-id": "revision_id"} + + def __init__(self, backend_id=None, revision_id=None, **unknown_fields): + """backend_id : str + revision_id : str + """ + backend_id_ = backend_id + revision_id_ = revision_id + + # Validate arguments against known Juju API types. + if backend_id_ is not None and not isinstance(backend_id_, (bytes, str)): + raise Exception( + f"Expected backend_id_ to be a str, received: {type(backend_id_)}" + ) + + if revision_id_ is not None and not isinstance(revision_id_, (bytes, str)): + raise Exception( + f"Expected revision_id_ to be a str, received: {type(revision_id_)}" + ) + + self.backend_id = backend_id_ + self.revision_id = revision_id_ + self.unknown_fields = unknown_fields + + +class SecretValueResult(Type): + _toSchema = {"data": "data", "error": "error"} + _toPy = {"data": "data", "error": "error"} + + def __init__(self, data=None, error=None, **unknown_fields): + """Data : typing.Mapping[str, str] + error : Error + """ + data_ = data + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if data_ is not None and not isinstance(data_, dict): + raise Exception(f"Expected data_ to be a Mapping, received: {type(data_)}") + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.data = data_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class SecretsFilter(Type): + _toSchema = { + "label": "label", + "owner_tag": "owner-tag", + "revision": "revision", + "uri": "uri", + } + _toPy = { + "label": "label", + "owner-tag": "owner_tag", + "revision": "revision", + "uri": "uri", + } + + def __init__( + self, label=None, owner_tag=None, revision=None, uri=None, **unknown_fields + ): + """Label : str + owner_tag : str + revision : int + uri : str + """ + label_ = label + owner_tag_ = owner_tag + revision_ = revision + uri_ = uri + + # Validate arguments against known Juju API types. + if label_ is not None and not isinstance(label_, (bytes, str)): + raise Exception(f"Expected label_ to be a str, received: {type(label_)}") + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + if revision_ is not None and not isinstance(revision_, int): + raise Exception( + f"Expected revision_ to be a int, received: {type(revision_)}" + ) + + if uri_ is not None and not isinstance(uri_, (bytes, str)): + raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") + + self.label = label_ + self.owner_tag = owner_tag_ + self.revision = revision_ + self.uri = uri_ + self.unknown_fields = unknown_fields + + +class SetConstraints(Type): + _toSchema = {"application": "application", "constraints": "constraints"} + _toPy = {"application": "application", "constraints": "constraints"} + + def __init__(self, application=None, constraints=None, **unknown_fields): + """Application : str + constraints : Value + """ + application_ = application + constraints_ = Value.from_json(constraints) if constraints else None + + # Validate arguments against known Juju API types. + if application_ is not None and not isinstance(application_, (bytes, str)): + raise Exception( + f"Expected application_ to be a str, received: {type(application_)}" + ) + + if constraints_ is not None and not isinstance(constraints_, (dict, Value)): + raise Exception( + f"Expected constraints_ to be a Value, received: {type(constraints_)}" + ) + + self.application = application_ + self.constraints = constraints_ + self.unknown_fields = unknown_fields + + +class SetModelDefaults(Type): + _toSchema = {"config": "config"} + _toPy = {"config": "config"} + + def __init__(self, config=None, **unknown_fields): + """Config : typing.Sequence[~ModelDefaultValues]""" + config_ = [ModelDefaultValues.from_json(o) for o in config or []] + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, (bytes, str, list)): + raise Exception( + f"Expected config_ to be a Sequence, received: {type(config_)}" + ) + + self.config = config_ + self.unknown_fields = unknown_fields + + +class ShowSpaceResult(Type): + _toSchema = { + "applications": "applications", + "error": "error", + "machine_count": "machine-count", + "space": "space", + } + _toPy = { + "applications": "applications", + "error": "error", + "machine-count": "machine_count", + "space": "space", + } + + def __init__( + self, + applications=None, + error=None, + machine_count=None, + space=None, + **unknown_fields, + ): + """Applications : typing.Sequence[str] + error : Error + machine_count : int + space : Space + """ + applications_ = applications + error_ = Error.from_json(error) if error else None + machine_count_ = machine_count + space_ = Space.from_json(space) if space else None + + # Validate arguments against known Juju API types. + if applications_ is not None and not isinstance( + applications_, (bytes, str, list) + ): + raise Exception( + f"Expected applications_ to be a Sequence, received: {type(applications_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if machine_count_ is not None and not isinstance(machine_count_, int): + raise Exception( + f"Expected machine_count_ to be a int, received: {type(machine_count_)}" + ) + + if space_ is not None and not isinstance(space_, (dict, Space)): + raise Exception(f"Expected space_ to be a Space, received: {type(space_)}") + + self.applications = applications_ + self.error = error_ + self.machine_count = machine_count_ + self.space = space_ + self.unknown_fields = unknown_fields + + +class ShowSpaceResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ShowSpaceResult]""" + results_ = [ShowSpaceResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class Space(Type): + _toSchema = {"error": "error", "id_": "id", "name": "name", "subnets": "subnets"} + _toPy = {"error": "error", "id": "id_", "name": "name", "subnets": "subnets"} + + def __init__(self, error=None, id_=None, name=None, subnets=None, **unknown_fields): + """Error : Error + id_ : str + name : str + subnets : typing.Sequence[~Subnet] + """ + error_ = Error.from_json(error) if error else None + id__ = id_ + name_ = name + subnets_ = [Subnet.from_json(o) for o in subnets or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if subnets_ is not None and not isinstance(subnets_, (bytes, str, list)): + raise Exception( + f"Expected subnets_ to be a Sequence, received: {type(subnets_)}" + ) + + self.error = error_ + self.id_ = id__ + self.name = name_ + self.subnets = subnets_ + self.unknown_fields = unknown_fields + + +class StatusHistoryFilter(Type): + _toSchema = {"date": "date", "delta": "delta", "exclude": "exclude", "size": "size"} + _toPy = {"date": "date", "delta": "delta", "exclude": "exclude", "size": "size"} + + def __init__( + self, date=None, delta=None, exclude=None, size=None, **unknown_fields + ): + """Date : str + delta : int + exclude : typing.Sequence[str] + size : int + """ + date_ = date + delta_ = delta + exclude_ = exclude + size_ = size + + # Validate arguments against known Juju API types. + if date_ is not None and not isinstance(date_, (bytes, str)): + raise Exception(f"Expected date_ to be a str, received: {type(date_)}") + + if delta_ is not None and not isinstance(delta_, int): + raise Exception(f"Expected delta_ to be a int, received: {type(delta_)}") + + if exclude_ is not None and not isinstance(exclude_, (bytes, str, list)): + raise Exception( + f"Expected exclude_ to be a Sequence, received: {type(exclude_)}" + ) + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + self.date = date_ + self.delta = delta_ + self.exclude = exclude_ + self.size = size_ + self.unknown_fields = unknown_fields + + +class StatusHistoryRequest(Type): + _toSchema = { + "filter_": "filter", + "historykind": "historyKind", + "size": "size", + "tag": "tag", + } + _toPy = { + "filter": "filter_", + "historyKind": "historykind", + "size": "size", + "tag": "tag", + } + + def __init__( + self, filter_=None, historykind=None, size=None, tag=None, **unknown_fields + ): + """filter_ : StatusHistoryFilter + historykind : str + size : int + tag : str + """ + filter__ = StatusHistoryFilter.from_json(filter_) if filter_ else None + historykind_ = historykind + size_ = size + tag_ = tag + + # Validate arguments against known Juju API types. + if filter__ is not None and not isinstance( + filter__, (dict, StatusHistoryFilter) + ): + raise Exception( + f"Expected filter__ to be a StatusHistoryFilter, received: {type(filter__)}" + ) + + if historykind_ is not None and not isinstance(historykind_, (bytes, str)): + raise Exception( + f"Expected historykind_ to be a str, received: {type(historykind_)}" + ) + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.filter_ = filter__ + self.historykind = historykind_ + self.size = size_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class StatusHistoryRequests(Type): + _toSchema = {"requests": "requests"} + _toPy = {"requests": "requests"} + + def __init__(self, requests=None, **unknown_fields): + """Requests : typing.Sequence[~StatusHistoryRequest]""" + requests_ = [StatusHistoryRequest.from_json(o) for o in requests or []] + + # Validate arguments against known Juju API types. + if requests_ is not None and not isinstance(requests_, (bytes, str, list)): + raise Exception( + f"Expected requests_ to be a Sequence, received: {type(requests_)}" + ) + + self.requests = requests_ + self.unknown_fields = unknown_fields + + +class StatusHistoryResult(Type): + _toSchema = {"error": "error", "history": "history"} + _toPy = {"error": "error", "history": "history"} + + def __init__(self, error=None, history=None, **unknown_fields): + """Error : Error + history : History + """ + error_ = Error.from_json(error) if error else None + history_ = History.from_json(history) if history else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if history_ is not None and not isinstance(history_, (dict, History)): + raise Exception( + f"Expected history_ to be a History, received: {type(history_)}" + ) + + self.error = error_ + self.history = history_ + self.unknown_fields = unknown_fields + + +class StatusHistoryResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~StatusHistoryResult]""" + results_ = [StatusHistoryResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class StatusParams(Type): + _toSchema = {"include_storage": "include-storage", "patterns": "patterns"} + _toPy = {"include-storage": "include_storage", "patterns": "patterns"} + + def __init__(self, include_storage=None, patterns=None, **unknown_fields): + """include_storage : bool + patterns : typing.Sequence[str] + """ + include_storage_ = include_storage + patterns_ = patterns + + # Validate arguments against known Juju API types. + if include_storage_ is not None and not isinstance(include_storage_, bool): + raise Exception( + f"Expected include_storage_ to be a bool, received: {type(include_storage_)}" + ) + + if patterns_ is not None and not isinstance(patterns_, (bytes, str, list)): + raise Exception( + f"Expected patterns_ to be a Sequence, received: {type(patterns_)}" + ) + + self.include_storage = include_storage_ + self.patterns = patterns_ + self.unknown_fields = unknown_fields + + +class StorageAddParams(Type): + _toSchema = {"name": "name", "storage": "storage", "unit": "unit"} + _toPy = {"name": "name", "storage": "storage", "unit": "unit"} + + def __init__(self, name=None, storage=None, unit=None, **unknown_fields): + """Name : str + storage : StorageConstraints + unit : str + """ + name_ = name + storage_ = StorageConstraints.from_json(storage) if storage else None + unit_ = unit + + # Validate arguments against known Juju API types. + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if storage_ is not None and not isinstance( + storage_, (dict, StorageConstraints) + ): + raise Exception( + f"Expected storage_ to be a StorageConstraints, received: {type(storage_)}" + ) + + if unit_ is not None and not isinstance(unit_, (bytes, str)): + raise Exception(f"Expected unit_ to be a str, received: {type(unit_)}") + + self.name = name_ + self.storage = storage_ + self.unit = unit_ + self.unknown_fields = unknown_fields + + +class StorageAttachmentDetails(Type): + _toSchema = { + "life": "life", + "location": "location", + "machine_tag": "machine-tag", + "storage_tag": "storage-tag", + "unit_tag": "unit-tag", + } + _toPy = { + "life": "life", + "location": "location", + "machine-tag": "machine_tag", + "storage-tag": "storage_tag", + "unit-tag": "unit_tag", + } + + def __init__( + self, + life=None, + location=None, + machine_tag=None, + storage_tag=None, + unit_tag=None, + **unknown_fields, + ): + """Life : str + location : str + machine_tag : str + storage_tag : str + unit_tag : str + """ + life_ = life + location_ = location + machine_tag_ = machine_tag + storage_tag_ = storage_tag + unit_tag_ = unit_tag + + # Validate arguments against known Juju API types. + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if location_ is not None and not isinstance(location_, (bytes, str)): + raise Exception( + f"Expected location_ to be a str, received: {type(location_)}" + ) + + if machine_tag_ is not None and not isinstance(machine_tag_, (bytes, str)): + raise Exception( + f"Expected machine_tag_ to be a str, received: {type(machine_tag_)}" + ) + + if storage_tag_ is not None and not isinstance(storage_tag_, (bytes, str)): + raise Exception( + f"Expected storage_tag_ to be a str, received: {type(storage_tag_)}" + ) + + if unit_tag_ is not None and not isinstance(unit_tag_, (bytes, str)): + raise Exception( + f"Expected unit_tag_ to be a str, received: {type(unit_tag_)}" + ) + + self.life = life_ + self.location = location_ + self.machine_tag = machine_tag_ + self.storage_tag = storage_tag_ + self.unit_tag = unit_tag_ + self.unknown_fields = unknown_fields + + +class StorageAttachmentId(Type): + _toSchema = {"storage_tag": "storage-tag", "unit_tag": "unit-tag"} + _toPy = {"storage-tag": "storage_tag", "unit-tag": "unit_tag"} + + def __init__(self, storage_tag=None, unit_tag=None, **unknown_fields): + """storage_tag : str + unit_tag : str + """ + storage_tag_ = storage_tag + unit_tag_ = unit_tag + + # Validate arguments against known Juju API types. + if storage_tag_ is not None and not isinstance(storage_tag_, (bytes, str)): + raise Exception( + f"Expected storage_tag_ to be a str, received: {type(storage_tag_)}" + ) + + if unit_tag_ is not None and not isinstance(unit_tag_, (bytes, str)): + raise Exception( + f"Expected unit_tag_ to be a str, received: {type(unit_tag_)}" + ) + + self.storage_tag = storage_tag_ + self.unit_tag = unit_tag_ + self.unknown_fields = unknown_fields + + +class StorageAttachmentIds(Type): + _toSchema = {"ids": "ids"} + _toPy = {"ids": "ids"} + + def __init__(self, ids=None, **unknown_fields): + """Ids : typing.Sequence[~StorageAttachmentId]""" + ids_ = [StorageAttachmentId.from_json(o) for o in ids or []] + + # Validate arguments against known Juju API types. + if ids_ is not None and not isinstance(ids_, (bytes, str, list)): + raise Exception(f"Expected ids_ to be a Sequence, received: {type(ids_)}") + + self.ids = ids_ + self.unknown_fields = unknown_fields + + +class StorageConstraints(Type): + _toSchema = {"count": "count", "pool": "pool", "size": "size"} + _toPy = {"count": "count", "pool": "pool", "size": "size"} + + def __init__(self, count=None, pool=None, size=None, **unknown_fields): + """Count : int + pool : str + size : int + """ + count_ = count + pool_ = pool + size_ = size + + # Validate arguments against known Juju API types. + if count_ is not None and not isinstance(count_, int): + raise Exception(f"Expected count_ to be a int, received: {type(count_)}") + + if pool_ is not None and not isinstance(pool_, (bytes, str)): + raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + self.count = count_ + self.pool = pool_ + self.size = size_ + self.unknown_fields = unknown_fields + + +class StorageDetachmentParams(Type): + _toSchema = {"force": "force", "ids": "ids", "max_wait": "max-wait"} + _toPy = {"force": "force", "ids": "ids", "max-wait": "max_wait"} + + def __init__(self, force=None, ids=None, max_wait=None, **unknown_fields): + """Force : bool + ids : StorageAttachmentIds + max_wait : int + """ + force_ = force + ids_ = StorageAttachmentIds.from_json(ids) if ids else None + max_wait_ = max_wait + + # Validate arguments against known Juju API types. + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if ids_ is not None and not isinstance(ids_, (dict, StorageAttachmentIds)): + raise Exception( + f"Expected ids_ to be a StorageAttachmentIds, received: {type(ids_)}" + ) + + if max_wait_ is not None and not isinstance(max_wait_, int): + raise Exception( + f"Expected max_wait_ to be a int, received: {type(max_wait_)}" + ) + + self.force = force_ + self.ids = ids_ + self.max_wait = max_wait_ + self.unknown_fields = unknown_fields + + +class StorageDetails(Type): + _toSchema = { + "attachments": "attachments", + "kind": "kind", + "life": "life", + "owner_tag": "owner-tag", + "persistent": "persistent", + "status": "status", + "storage_tag": "storage-tag", + } + _toPy = { + "attachments": "attachments", + "kind": "kind", + "life": "life", + "owner-tag": "owner_tag", + "persistent": "persistent", + "status": "status", + "storage-tag": "storage_tag", + } + + def __init__( + self, + attachments=None, + kind=None, + life=None, + owner_tag=None, + persistent=None, + status=None, + storage_tag=None, + **unknown_fields, + ): + """Attachments : typing.Mapping[str, ~StorageAttachmentDetails] + kind : int + life : str + owner_tag : str + persistent : bool + status : EntityStatus + storage_tag : str + """ + attachments_ = { + k: StorageAttachmentDetails.from_json(v) + for k, v in (attachments or dict()).items() + } + kind_ = kind + life_ = life + owner_tag_ = owner_tag + persistent_ = persistent + status_ = EntityStatus.from_json(status) if status else None + storage_tag_ = storage_tag + + # Validate arguments against known Juju API types. + if attachments_ is not None and not isinstance(attachments_, dict): + raise Exception( + f"Expected attachments_ to be a Mapping, received: {type(attachments_)}" + ) + + if kind_ is not None and not isinstance(kind_, int): + raise Exception(f"Expected kind_ to be a int, received: {type(kind_)}") + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): + raise Exception( + f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" + ) + + if persistent_ is not None and not isinstance(persistent_, bool): + raise Exception( + f"Expected persistent_ to be a bool, received: {type(persistent_)}" + ) + + if status_ is not None and not isinstance(status_, (dict, EntityStatus)): + raise Exception( + f"Expected status_ to be a EntityStatus, received: {type(status_)}" + ) + + if storage_tag_ is not None and not isinstance(storage_tag_, (bytes, str)): + raise Exception( + f"Expected storage_tag_ to be a str, received: {type(storage_tag_)}" + ) + + self.attachments = attachments_ + self.kind = kind_ + self.life = life_ + self.owner_tag = owner_tag_ + self.persistent = persistent_ + self.status = status_ + self.storage_tag = storage_tag_ + self.unknown_fields = unknown_fields + + +class StorageDetailsListResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : typing.Sequence[~StorageDetails] + """ + error_ = Error.from_json(error) if error else None + result_ = [StorageDetails.from_json(o) for o in result or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (bytes, str, list)): + raise Exception( + f"Expected result_ to be a Sequence, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class StorageDetailsListResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~StorageDetailsListResult]""" + results_ = [StorageDetailsListResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class StorageDetailsResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : StorageDetails + """ + error_ = Error.from_json(error) if error else None + result_ = StorageDetails.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, StorageDetails)): + raise Exception( + f"Expected result_ to be a StorageDetails, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class StorageDetailsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~StorageDetailsResult]""" + results_ = [StorageDetailsResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class StorageFilter(Type): + _toSchema = {} + _toPy = {} + + def __init__(self, **unknown_fields): + """ """ + self.unknown_fields = unknown_fields + + +class StorageFilters(Type): + _toSchema = {"filters": "filters"} + _toPy = {"filters": "filters"} + + def __init__(self, filters=None, **unknown_fields): + """Filters : typing.Sequence[~StorageFilter]""" + filters_ = [StorageFilter.from_json(o) for o in filters or []] + + # Validate arguments against known Juju API types. + if filters_ is not None and not isinstance(filters_, (bytes, str, list)): + raise Exception( + f"Expected filters_ to be a Sequence, received: {type(filters_)}" + ) + + self.filters = filters_ + self.unknown_fields = unknown_fields + + +class StoragePool(Type): + _toSchema = {"attrs": "attrs", "name": "name", "provider": "provider"} + _toPy = {"attrs": "attrs", "name": "name", "provider": "provider"} + + def __init__(self, attrs=None, name=None, provider=None, **unknown_fields): + """Attrs : typing.Mapping[str, typing.Any] + name : str + provider : str + """ + attrs_ = attrs + name_ = name + provider_ = provider + + # Validate arguments against known Juju API types. + if attrs_ is not None and not isinstance(attrs_, dict): + raise Exception( + f"Expected attrs_ to be a Mapping, received: {type(attrs_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if provider_ is not None and not isinstance(provider_, (bytes, str)): + raise Exception( + f"Expected provider_ to be a str, received: {type(provider_)}" + ) + + self.attrs = attrs_ + self.name = name_ + self.provider = provider_ + self.unknown_fields = unknown_fields + + +class StoragePoolArgs(Type): + _toSchema = {"pools": "pools"} + _toPy = {"pools": "pools"} + + def __init__(self, pools=None, **unknown_fields): + """Pools : typing.Sequence[~StoragePool]""" + pools_ = [StoragePool.from_json(o) for o in pools or []] + + # Validate arguments against known Juju API types. + if pools_ is not None and not isinstance(pools_, (bytes, str, list)): + raise Exception( + f"Expected pools_ to be a Sequence, received: {type(pools_)}" + ) + + self.pools = pools_ + self.unknown_fields = unknown_fields + + +class StoragePoolDeleteArg(Type): + _toSchema = {"name": "name"} + _toPy = {"name": "name"} + + def __init__(self, name=None, **unknown_fields): + """Name : str""" + name_ = name + + # Validate arguments against known Juju API types. + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + self.name = name_ + self.unknown_fields = unknown_fields + + +class StoragePoolDeleteArgs(Type): + _toSchema = {"pools": "pools"} + _toPy = {"pools": "pools"} + + def __init__(self, pools=None, **unknown_fields): + """Pools : typing.Sequence[~StoragePoolDeleteArg]""" + pools_ = [StoragePoolDeleteArg.from_json(o) for o in pools or []] + + # Validate arguments against known Juju API types. + if pools_ is not None and not isinstance(pools_, (bytes, str, list)): + raise Exception( + f"Expected pools_ to be a Sequence, received: {type(pools_)}" + ) + + self.pools = pools_ + self.unknown_fields = unknown_fields + + +class StoragePoolFilter(Type): + _toSchema = {"names": "names", "providers": "providers"} + _toPy = {"names": "names", "providers": "providers"} + + def __init__(self, names=None, providers=None, **unknown_fields): + """Names : typing.Sequence[str] + providers : typing.Sequence[str] + """ + names_ = names + providers_ = providers + + # Validate arguments against known Juju API types. + if names_ is not None and not isinstance(names_, (bytes, str, list)): + raise Exception( + f"Expected names_ to be a Sequence, received: {type(names_)}" + ) + + if providers_ is not None and not isinstance(providers_, (bytes, str, list)): + raise Exception( + f"Expected providers_ to be a Sequence, received: {type(providers_)}" + ) + + self.names = names_ + self.providers = providers_ + self.unknown_fields = unknown_fields + + +class StoragePoolFilters(Type): + _toSchema = {"filters": "filters"} + _toPy = {"filters": "filters"} + + def __init__(self, filters=None, **unknown_fields): + """Filters : typing.Sequence[~StoragePoolFilter]""" + filters_ = [StoragePoolFilter.from_json(o) for o in filters or []] + + # Validate arguments against known Juju API types. + if filters_ is not None and not isinstance(filters_, (bytes, str, list)): + raise Exception( + f"Expected filters_ to be a Sequence, received: {type(filters_)}" + ) + + self.filters = filters_ + self.unknown_fields = unknown_fields + + +class StoragePoolsResult(Type): + _toSchema = {"error": "error", "storage_pools": "storage-pools"} + _toPy = {"error": "error", "storage-pools": "storage_pools"} + + def __init__(self, error=None, storage_pools=None, **unknown_fields): + """Error : Error + storage_pools : typing.Sequence[~StoragePool] + """ + error_ = Error.from_json(error) if error else None + storage_pools_ = [StoragePool.from_json(o) for o in storage_pools or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if storage_pools_ is not None and not isinstance( + storage_pools_, (bytes, str, list) + ): + raise Exception( + f"Expected storage_pools_ to be a Sequence, received: {type(storage_pools_)}" + ) + + self.error = error_ + self.storage_pools = storage_pools_ + self.unknown_fields = unknown_fields + + +class StoragePoolsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~StoragePoolsResult]""" + results_ = [StoragePoolsResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class StoragesAddParams(Type): + _toSchema = {"storages": "storages"} + _toPy = {"storages": "storages"} + + def __init__(self, storages=None, **unknown_fields): + """Storages : typing.Sequence[~StorageAddParams]""" + storages_ = [StorageAddParams.from_json(o) for o in storages or []] + + # Validate arguments against known Juju API types. + if storages_ is not None and not isinstance(storages_, (bytes, str, list)): + raise Exception( + f"Expected storages_ to be a Sequence, received: {type(storages_)}" + ) + + self.storages = storages_ + self.unknown_fields = unknown_fields + + +class StringResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : str + """ + error_ = Error.from_json(error) if error else None + result_ = result + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (bytes, str)): + raise Exception(f"Expected result_ to be a str, received: {type(result_)}") + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class StringResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~StringResult]""" + results_ = [StringResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class StringsResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : typing.Sequence[str] + """ + error_ = Error.from_json(error) if error else None + result_ = result + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (bytes, str, list)): + raise Exception( + f"Expected result_ to be a Sequence, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class StringsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~StringsResult]""" + results_ = [StringsResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class StringsWatchResult(Type): + _toSchema = {"changes": "changes", "error": "error", "watcher_id": "watcher-id"} + _toPy = {"changes": "changes", "error": "error", "watcher-id": "watcher_id"} + + def __init__(self, changes=None, error=None, watcher_id=None, **unknown_fields): + """Changes : typing.Sequence[str] + error : Error + watcher_id : str + """ + changes_ = changes + error_ = Error.from_json(error) if error else None + watcher_id_ = watcher_id + + # Validate arguments against known Juju API types. + if changes_ is not None and not isinstance(changes_, (bytes, str, list)): + raise Exception( + f"Expected changes_ to be a Sequence, received: {type(changes_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if watcher_id_ is not None and not isinstance(watcher_id_, (bytes, str)): + raise Exception( + f"Expected watcher_id_ to be a str, received: {type(watcher_id_)}" + ) + + self.changes = changes_ + self.error = error_ + self.watcher_id = watcher_id_ + self.unknown_fields = unknown_fields + + +class StringsWatchResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~StringsWatchResult]""" + results_ = [StringsWatchResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class Subnet(Type): + _toSchema = { + "cidr": "cidr", + "life": "life", + "provider_id": "provider-id", + "provider_network_id": "provider-network-id", + "provider_space_id": "provider-space-id", + "space_tag": "space-tag", + "status": "status", + "vlan_tag": "vlan-tag", + "zones": "zones", + } + _toPy = { + "cidr": "cidr", + "life": "life", + "provider-id": "provider_id", + "provider-network-id": "provider_network_id", + "provider-space-id": "provider_space_id", + "space-tag": "space_tag", + "status": "status", + "vlan-tag": "vlan_tag", + "zones": "zones", + } + + def __init__( + self, + cidr=None, + life=None, + provider_id=None, + provider_network_id=None, + provider_space_id=None, + space_tag=None, + status=None, + vlan_tag=None, + zones=None, + **unknown_fields, + ): + """Cidr : str + life : str + provider_id : str + provider_network_id : str + provider_space_id : str + space_tag : str + status : str + vlan_tag : int + zones : typing.Sequence[str] + """ + cidr_ = cidr + life_ = life + provider_id_ = provider_id + provider_network_id_ = provider_network_id + provider_space_id_ = provider_space_id + space_tag_ = space_tag + status_ = status + vlan_tag_ = vlan_tag + zones_ = zones + + # Validate arguments against known Juju API types. + if cidr_ is not None and not isinstance(cidr_, (bytes, str)): + raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if provider_network_id_ is not None and not isinstance( + provider_network_id_, (bytes, str) + ): + raise Exception( + f"Expected provider_network_id_ to be a str, received: {type(provider_network_id_)}" + ) + + if provider_space_id_ is not None and not isinstance( + provider_space_id_, (bytes, str) + ): + raise Exception( + f"Expected provider_space_id_ to be a str, received: {type(provider_space_id_)}" + ) + + if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): + raise Exception( + f"Expected space_tag_ to be a str, received: {type(space_tag_)}" + ) + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + if vlan_tag_ is not None and not isinstance(vlan_tag_, int): + raise Exception( + f"Expected vlan_tag_ to be a int, received: {type(vlan_tag_)}" + ) + + if zones_ is not None and not isinstance(zones_, (bytes, str, list)): + raise Exception( + f"Expected zones_ to be a Sequence, received: {type(zones_)}" + ) + + self.cidr = cidr_ + self.life = life_ + self.provider_id = provider_id_ + self.provider_network_id = provider_network_id_ + self.provider_space_id = provider_space_id_ + self.space_tag = space_tag_ + self.status = status_ + self.vlan_tag = vlan_tag_ + self.zones = zones_ + self.unknown_fields = unknown_fields + + +class SubnetV2(Type): + _toSchema = { + "cidr": "cidr", + "id_": "id", + "life": "life", + "provider_id": "provider-id", + "provider_network_id": "provider-network-id", + "provider_space_id": "provider-space-id", + "space_tag": "space-tag", + "status": "status", + "subnet": "Subnet", + "vlan_tag": "vlan-tag", + "zones": "zones", + } + _toPy = { + "Subnet": "subnet", + "cidr": "cidr", + "id": "id_", + "life": "life", + "provider-id": "provider_id", + "provider-network-id": "provider_network_id", + "provider-space-id": "provider_space_id", + "space-tag": "space_tag", + "status": "status", + "vlan-tag": "vlan_tag", + "zones": "zones", + } + + def __init__( + self, + subnet=None, + cidr=None, + id_=None, + life=None, + provider_id=None, + provider_network_id=None, + provider_space_id=None, + space_tag=None, + status=None, + vlan_tag=None, + zones=None, + **unknown_fields, + ): + """Subnet : Subnet + cidr : str + id_ : str + life : str + provider_id : str + provider_network_id : str + provider_space_id : str + space_tag : str + status : str + vlan_tag : int + zones : typing.Sequence[str] + """ + subnet_ = Subnet.from_json(subnet) if subnet else None + cidr_ = cidr + id__ = id_ + life_ = life + provider_id_ = provider_id + provider_network_id_ = provider_network_id + provider_space_id_ = provider_space_id + space_tag_ = space_tag + status_ = status + vlan_tag_ = vlan_tag + zones_ = zones + + # Validate arguments against known Juju API types. + if subnet_ is not None and not isinstance(subnet_, (dict, Subnet)): + raise Exception( + f"Expected subnet_ to be a Subnet, received: {type(subnet_)}" + ) + + if cidr_ is not None and not isinstance(cidr_, (bytes, str)): + raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") + + if id__ is not None and not isinstance(id__, (bytes, str)): + raise Exception(f"Expected id__ to be a str, received: {type(id__)}") + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if provider_network_id_ is not None and not isinstance( + provider_network_id_, (bytes, str) + ): + raise Exception( + f"Expected provider_network_id_ to be a str, received: {type(provider_network_id_)}" + ) + + if provider_space_id_ is not None and not isinstance( + provider_space_id_, (bytes, str) + ): + raise Exception( + f"Expected provider_space_id_ to be a str, received: {type(provider_space_id_)}" + ) + + if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): + raise Exception( + f"Expected space_tag_ to be a str, received: {type(space_tag_)}" + ) + + if status_ is not None and not isinstance(status_, (bytes, str)): + raise Exception(f"Expected status_ to be a str, received: {type(status_)}") + + if vlan_tag_ is not None and not isinstance(vlan_tag_, int): + raise Exception( + f"Expected vlan_tag_ to be a int, received: {type(vlan_tag_)}" + ) + + if zones_ is not None and not isinstance(zones_, (bytes, str, list)): + raise Exception( + f"Expected zones_ to be a Sequence, received: {type(zones_)}" + ) + + self.subnet = subnet_ + self.cidr = cidr_ + self.id_ = id__ + self.life = life_ + self.provider_id = provider_id_ + self.provider_network_id = provider_network_id_ + self.provider_space_id = provider_space_id_ + self.space_tag = space_tag_ + self.status = status_ + self.vlan_tag = vlan_tag_ + self.zones = zones_ + self.unknown_fields = unknown_fields + + +class SubnetsFilters(Type): + _toSchema = {"space_tag": "space-tag", "zone": "zone"} + _toPy = {"space-tag": "space_tag", "zone": "zone"} + + def __init__(self, space_tag=None, zone=None, **unknown_fields): + """space_tag : str + zone : str + """ + space_tag_ = space_tag + zone_ = zone + + # Validate arguments against known Juju API types. + if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): + raise Exception( + f"Expected space_tag_ to be a str, received: {type(space_tag_)}" + ) + + if zone_ is not None and not isinstance(zone_, (bytes, str)): + raise Exception(f"Expected zone_ to be a str, received: {type(zone_)}") + + self.space_tag = space_tag_ + self.zone = zone_ + self.unknown_fields = unknown_fields + + +class SubnetsResult(Type): + _toSchema = {"error": "error", "subnets": "subnets"} + _toPy = {"error": "error", "subnets": "subnets"} + + def __init__(self, error=None, subnets=None, **unknown_fields): + """Error : Error + subnets : typing.Sequence[~SubnetV2] + """ + error_ = Error.from_json(error) if error else None + subnets_ = [SubnetV2.from_json(o) for o in subnets or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if subnets_ is not None and not isinstance(subnets_, (bytes, str, list)): + raise Exception( + f"Expected subnets_ to be a Sequence, received: {type(subnets_)}" + ) + + self.error = error_ + self.subnets = subnets_ + self.unknown_fields = unknown_fields + + +class SubnetsResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~SubnetsResult]""" + results_ = [SubnetsResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class SummaryWatcherID(Type): + _toSchema = {"watcher_id": "watcher-id"} + _toPy = {"watcher-id": "watcher_id"} + + def __init__(self, watcher_id=None, **unknown_fields): + """watcher_id : str""" + watcher_id_ = watcher_id + + # Validate arguments against known Juju API types. + if watcher_id_ is not None and not isinstance(watcher_id_, (bytes, str)): + raise Exception( + f"Expected watcher_id_ to be a str, received: {type(watcher_id_)}" + ) + + self.watcher_id = watcher_id_ + self.unknown_fields = unknown_fields + + +class SupportedFeature(Type): + _toSchema = {"description": "description", "name": "name", "version": "version"} + _toPy = {"description": "description", "name": "name", "version": "version"} + + def __init__(self, description=None, name=None, version=None, **unknown_fields): + """Description : str + name : str + version : str + """ + description_ = description + name_ = name + version_ = version + + # Validate arguments against known Juju API types. + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if version_ is not None and not isinstance(version_, (bytes, str)): + raise Exception( + f"Expected version_ to be a str, received: {type(version_)}" + ) + + self.description = description_ + self.name = name_ + self.version = version_ + self.unknown_fields = unknown_fields + + +class TaggedCredential(Type): + _toSchema = {"credential": "credential", "tag": "tag"} + _toPy = {"credential": "credential", "tag": "tag"} + + def __init__(self, credential=None, tag=None, **unknown_fields): + """Credential : CloudCredential + tag : str + """ + credential_ = CloudCredential.from_json(credential) if credential else None + tag_ = tag + + # Validate arguments against known Juju API types. + if credential_ is not None and not isinstance( + credential_, (dict, CloudCredential) + ): + raise Exception( + f"Expected credential_ to be a CloudCredential, received: {type(credential_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.credential = credential_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class TaggedCredentials(Type): + _toSchema = {"credentials": "credentials"} + _toPy = {"credentials": "credentials"} + + def __init__(self, credentials=None, **unknown_fields): + """Credentials : typing.Sequence[~TaggedCredential]""" + credentials_ = [TaggedCredential.from_json(o) for o in credentials or []] + + # Validate arguments against known Juju API types. + if credentials_ is not None and not isinstance( + credentials_, (bytes, str, list) + ): + raise Exception( + f"Expected credentials_ to be a Sequence, received: {type(credentials_)}" + ) + + self.credentials = credentials_ + self.unknown_fields = unknown_fields + + +class Tools(Type): + _toSchema = {"sha256": "sha256", "size": "size", "url": "url", "version": "version"} + _toPy = {"sha256": "sha256", "size": "size", "url": "url", "version": "version"} + + def __init__( + self, sha256=None, size=None, url=None, version=None, **unknown_fields + ): + """sha256 : str + size : int + url : str + version : Binary + """ + sha256_ = sha256 + size_ = size + url_ = url + version_ = Binary.from_json(version) if version else None + + # Validate arguments against known Juju API types. + if sha256_ is not None and not isinstance(sha256_, (bytes, str)): + raise Exception(f"Expected sha256_ to be a str, received: {type(sha256_)}") + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + if url_ is not None and not isinstance(url_, (bytes, str)): + raise Exception(f"Expected url_ to be a str, received: {type(url_)}") + + if version_ is not None and not isinstance(version_, (dict, Binary)): + raise Exception( + f"Expected version_ to be a Binary, received: {type(version_)}" + ) + + self.sha256 = sha256_ + self.size = size_ + self.url = url_ + self.version = version_ + self.unknown_fields = unknown_fields + + +class UnitInfoResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : UnitResult + """ + error_ = Error.from_json(error) if error else None + result_ = UnitResult.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, UnitResult)): + raise Exception( + f"Expected result_ to be a UnitResult, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class UnitInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~UnitInfoResult]""" + results_ = [UnitInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class UnitResources(Type): + _toSchema = { + "download_progress": "download-progress", + "entity": "Entity", + "resources": "resources", + "tag": "tag", + } + _toPy = { + "Entity": "entity", + "download-progress": "download_progress", + "resources": "resources", + "tag": "tag", + } + + def __init__( + self, + entity=None, + download_progress=None, + resources=None, + tag=None, + **unknown_fields, + ): + """Entity : Entity + download_progress : typing.Mapping[str, int] + resources : typing.Sequence[~Resource] + tag : str + """ + entity_ = Entity.from_json(entity) if entity else None + download_progress_ = download_progress + resources_ = [Resource.from_json(o) for o in resources or []] + tag_ = tag + + # Validate arguments against known Juju API types. + if entity_ is not None and not isinstance(entity_, (dict, Entity)): + raise Exception( + f"Expected entity_ to be a Entity, received: {type(entity_)}" + ) + + if download_progress_ is not None and not isinstance(download_progress_, dict): + raise Exception( + f"Expected download_progress_ to be a Mapping, received: {type(download_progress_)}" + ) + + if resources_ is not None and not isinstance(resources_, (bytes, str, list)): + raise Exception( + f"Expected resources_ to be a Sequence, received: {type(resources_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.entity = entity_ + self.download_progress = download_progress_ + self.resources = resources_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class UnitResult(Type): + _toSchema = { + "address": "address", + "charm": "charm", + "leader": "leader", + "life": "life", + "machine": "machine", + "opened_ports": "opened-ports", + "provider_id": "provider-id", + "public_address": "public-address", + "relation_data": "relation-data", + "tag": "tag", + "workload_version": "workload-version", + } + _toPy = { + "address": "address", + "charm": "charm", + "leader": "leader", + "life": "life", + "machine": "machine", + "opened-ports": "opened_ports", + "provider-id": "provider_id", + "public-address": "public_address", + "relation-data": "relation_data", + "tag": "tag", + "workload-version": "workload_version", + } + + def __init__( + self, + address=None, + charm=None, + leader=None, + life=None, + machine=None, + opened_ports=None, + provider_id=None, + public_address=None, + relation_data=None, + tag=None, + workload_version=None, + **unknown_fields, + ): + """Address : str + charm : str + leader : bool + life : str + machine : str + opened_ports : typing.Sequence[str] + provider_id : str + public_address : str + relation_data : typing.Sequence[~EndpointRelationData] + tag : str + workload_version : str + """ + address_ = address + charm_ = charm + leader_ = leader + life_ = life + machine_ = machine + opened_ports_ = opened_ports + provider_id_ = provider_id + public_address_ = public_address + relation_data_ = [ + EndpointRelationData.from_json(o) for o in relation_data or [] + ] + tag_ = tag + workload_version_ = workload_version + + # Validate arguments against known Juju API types. + if address_ is not None and not isinstance(address_, (bytes, str)): + raise Exception( + f"Expected address_ to be a str, received: {type(address_)}" + ) + + if charm_ is not None and not isinstance(charm_, (bytes, str)): + raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") + + if leader_ is not None and not isinstance(leader_, bool): + raise Exception(f"Expected leader_ to be a bool, received: {type(leader_)}") + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if machine_ is not None and not isinstance(machine_, (bytes, str)): + raise Exception( + f"Expected machine_ to be a str, received: {type(machine_)}" + ) + + if opened_ports_ is not None and not isinstance( + opened_ports_, (bytes, str, list) + ): + raise Exception( + f"Expected opened_ports_ to be a Sequence, received: {type(opened_ports_)}" + ) + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if public_address_ is not None and not isinstance( + public_address_, (bytes, str) + ): + raise Exception( + f"Expected public_address_ to be a str, received: {type(public_address_)}" + ) + + if relation_data_ is not None and not isinstance( + relation_data_, (bytes, str, list) + ): + raise Exception( + f"Expected relation_data_ to be a Sequence, received: {type(relation_data_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + if workload_version_ is not None and not isinstance( + workload_version_, (bytes, str) + ): + raise Exception( + f"Expected workload_version_ to be a str, received: {type(workload_version_)}" + ) + + self.address = address_ + self.charm = charm_ + self.leader = leader_ + self.life = life_ + self.machine = machine_ + self.opened_ports = opened_ports_ + self.provider_id = provider_id_ + self.public_address = public_address_ + self.relation_data = relation_data_ + self.tag = tag_ + self.workload_version = workload_version_ + self.unknown_fields = unknown_fields + + +class UnitStatus(Type): + _toSchema = { + "address": "address", + "agent_status": "agent-status", + "charm": "charm", + "leader": "leader", + "machine": "machine", + "opened_ports": "opened-ports", + "provider_id": "provider-id", + "public_address": "public-address", + "subordinates": "subordinates", + "workload_status": "workload-status", + "workload_version": "workload-version", + } + _toPy = { + "address": "address", + "agent-status": "agent_status", + "charm": "charm", + "leader": "leader", + "machine": "machine", + "opened-ports": "opened_ports", + "provider-id": "provider_id", + "public-address": "public_address", + "subordinates": "subordinates", + "workload-status": "workload_status", + "workload-version": "workload_version", + } + + def __init__( + self, + address=None, + agent_status=None, + charm=None, + leader=None, + machine=None, + opened_ports=None, + provider_id=None, + public_address=None, + subordinates=None, + workload_status=None, + workload_version=None, + **unknown_fields, + ): + """Address : str + agent_status : DetailedStatus + charm : str + leader : bool + machine : str + opened_ports : typing.Sequence[str] + provider_id : str + public_address : str + subordinates : typing.Mapping[str, ~UnitStatus] + workload_status : DetailedStatus + workload_version : str + """ + address_ = address + agent_status_ = DetailedStatus.from_json(agent_status) if agent_status else None + charm_ = charm + leader_ = leader + machine_ = machine + opened_ports_ = opened_ports + provider_id_ = provider_id + public_address_ = public_address + subordinates_ = { + k: UnitStatus.from_json(v) for k, v in (subordinates or dict()).items() + } + workload_status_ = ( + DetailedStatus.from_json(workload_status) if workload_status else None + ) + workload_version_ = workload_version + + # Validate arguments against known Juju API types. + if address_ is not None and not isinstance(address_, (bytes, str)): + raise Exception( + f"Expected address_ to be a str, received: {type(address_)}" + ) + + if agent_status_ is not None and not isinstance( + agent_status_, (dict, DetailedStatus) + ): + raise Exception( + f"Expected agent_status_ to be a DetailedStatus, received: {type(agent_status_)}" + ) + + if charm_ is not None and not isinstance(charm_, (bytes, str)): + raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") + + if leader_ is not None and not isinstance(leader_, bool): + raise Exception(f"Expected leader_ to be a bool, received: {type(leader_)}") + + if machine_ is not None and not isinstance(machine_, (bytes, str)): + raise Exception( + f"Expected machine_ to be a str, received: {type(machine_)}" + ) + + if opened_ports_ is not None and not isinstance( + opened_ports_, (bytes, str, list) + ): + raise Exception( + f"Expected opened_ports_ to be a Sequence, received: {type(opened_ports_)}" + ) + + if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): + raise Exception( + f"Expected provider_id_ to be a str, received: {type(provider_id_)}" + ) + + if public_address_ is not None and not isinstance( + public_address_, (bytes, str) + ): + raise Exception( + f"Expected public_address_ to be a str, received: {type(public_address_)}" + ) + + if subordinates_ is not None and not isinstance(subordinates_, dict): + raise Exception( + f"Expected subordinates_ to be a Mapping, received: {type(subordinates_)}" + ) + + if workload_status_ is not None and not isinstance( + workload_status_, (dict, DetailedStatus) + ): + raise Exception( + f"Expected workload_status_ to be a DetailedStatus, received: {type(workload_status_)}" + ) + + if workload_version_ is not None and not isinstance( + workload_version_, (bytes, str) + ): + raise Exception( + f"Expected workload_version_ to be a str, received: {type(workload_version_)}" + ) + + self.address = address_ + self.agent_status = agent_status_ + self.charm = charm_ + self.leader = leader_ + self.machine = machine_ + self.opened_ports = opened_ports_ + self.provider_id = provider_id_ + self.public_address = public_address_ + self.subordinates = subordinates_ + self.workload_status = workload_status_ + self.workload_version = workload_version_ + self.unknown_fields = unknown_fields + + +class UnitsResolved(Type): + _toSchema = {"all_": "all", "retry": "retry", "tags": "tags"} + _toPy = {"all": "all_", "retry": "retry", "tags": "tags"} + + def __init__(self, all_=None, retry=None, tags=None, **unknown_fields): + """all_ : bool + retry : bool + tags : Entities + """ + all__ = all_ + retry_ = retry + tags_ = Entities.from_json(tags) if tags else None + + # Validate arguments against known Juju API types. + if all__ is not None and not isinstance(all__, bool): + raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") + + if retry_ is not None and not isinstance(retry_, bool): + raise Exception(f"Expected retry_ to be a bool, received: {type(retry_)}") + + if tags_ is not None and not isinstance(tags_, (dict, Entities)): + raise Exception(f"Expected tags_ to be a Entities, received: {type(tags_)}") + + self.all_ = all__ + self.retry = retry_ + self.tags = tags_ + self.unknown_fields = unknown_fields + + +class UnsetModelDefaults(Type): + _toSchema = {"keys": "keys"} + _toPy = {"keys": "keys"} + + def __init__(self, keys=None, **unknown_fields): + """Keys : typing.Sequence[~ModelUnsetKeys]""" + keys_ = [ModelUnsetKeys.from_json(o) for o in keys or []] + + # Validate arguments against known Juju API types. + if keys_ is not None and not isinstance(keys_, (bytes, str, list)): + raise Exception(f"Expected keys_ to be a Sequence, received: {type(keys_)}") + + self.keys = keys_ + self.unknown_fields = unknown_fields + + +class UpdateChannelArg(Type): + _toSchema = {"channel": "channel", "force": "force", "tag": "tag"} + _toPy = {"channel": "channel", "force": "force", "tag": "tag"} + + def __init__(self, channel=None, force=None, tag=None, **unknown_fields): + """Channel : str + force : bool + tag : Entity + """ + channel_ = channel + force_ = force + tag_ = Entity.from_json(tag) if tag else None + + # Validate arguments against known Juju API types. + if channel_ is not None and not isinstance(channel_, (bytes, str)): + raise Exception( + f"Expected channel_ to be a str, received: {type(channel_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if tag_ is not None and not isinstance(tag_, (dict, Entity)): + raise Exception(f"Expected tag_ to be a Entity, received: {type(tag_)}") + + self.channel = channel_ + self.force = force_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class UpdateChannelArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~UpdateChannelArg]""" + args_ = [UpdateChannelArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class UpdateCloudArgs(Type): + _toSchema = {"clouds": "clouds"} + _toPy = {"clouds": "clouds"} + + def __init__(self, clouds=None, **unknown_fields): + """Clouds : typing.Sequence[~AddCloudArgs]""" + clouds_ = [AddCloudArgs.from_json(o) for o in clouds or []] + + # Validate arguments against known Juju API types. + if clouds_ is not None and not isinstance(clouds_, (bytes, str, list)): + raise Exception( + f"Expected clouds_ to be a Sequence, received: {type(clouds_)}" + ) + + self.clouds = clouds_ + self.unknown_fields = unknown_fields + + +class UpdateCredentialArgs(Type): + _toSchema = {"credentials": "credentials", "force": "force"} + _toPy = {"credentials": "credentials", "force": "force"} + + def __init__(self, credentials=None, force=None, **unknown_fields): + """Credentials : typing.Sequence[~TaggedCredential] + force : bool + """ + credentials_ = [TaggedCredential.from_json(o) for o in credentials or []] + force_ = force + + # Validate arguments against known Juju API types. + if credentials_ is not None and not isinstance( + credentials_, (bytes, str, list) + ): + raise Exception( + f"Expected credentials_ to be a Sequence, received: {type(credentials_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + self.credentials = credentials_ + self.force = force_ + self.unknown_fields = unknown_fields + + +class UpdateCredentialModelResult(Type): + _toSchema = {"errors": "errors", "name": "name", "uuid": "uuid"} + _toPy = {"errors": "errors", "name": "name", "uuid": "uuid"} + + def __init__(self, errors=None, name=None, uuid=None, **unknown_fields): + """Errors : typing.Sequence[~ErrorResult] + name : str + uuid : str + """ + errors_ = [ErrorResult.from_json(o) for o in errors or []] + name_ = name + uuid_ = uuid + + # Validate arguments against known Juju API types. + if errors_ is not None and not isinstance(errors_, (bytes, str, list)): + raise Exception( + f"Expected errors_ to be a Sequence, received: {type(errors_)}" + ) + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if uuid_ is not None and not isinstance(uuid_, (bytes, str)): + raise Exception(f"Expected uuid_ to be a str, received: {type(uuid_)}") + + self.errors = errors_ + self.name = name_ + self.uuid = uuid_ + self.unknown_fields = unknown_fields + + +class UpdateCredentialResult(Type): + _toSchema = {"error": "error", "models": "models", "tag": "tag"} + _toPy = {"error": "error", "models": "models", "tag": "tag"} + + def __init__(self, error=None, models=None, tag=None, **unknown_fields): + """Error : Error + models : typing.Sequence[~UpdateCredentialModelResult] + tag : str + """ + error_ = Error.from_json(error) if error else None + models_ = [UpdateCredentialModelResult.from_json(o) for o in models or []] + tag_ = tag + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if models_ is not None and not isinstance(models_, (bytes, str, list)): + raise Exception( + f"Expected models_ to be a Sequence, received: {type(models_)}" + ) + + if tag_ is not None and not isinstance(tag_, (bytes, str)): + raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") + + self.error = error_ + self.models = models_ + self.tag = tag_ + self.unknown_fields = unknown_fields + + +class UpdateCredentialResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~UpdateCredentialResult]""" + results_ = [UpdateCredentialResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class UpdateSecretBackendArg(Type): + _toSchema = { + "config": "config", + "force": "force", + "name": "name", + "name_change": "name-change", + "reset": "reset", + "token_rotate_interval": "token-rotate-interval", + } + _toPy = { + "config": "config", + "force": "force", + "name": "name", + "name-change": "name_change", + "reset": "reset", + "token-rotate-interval": "token_rotate_interval", + } + + def __init__( + self, + config=None, + force=None, + name=None, + name_change=None, + reset=None, + token_rotate_interval=None, + **unknown_fields, + ): + """Config : typing.Mapping[str, typing.Any] + force : bool + name : str + name_change : str + reset : typing.Sequence[str] + token_rotate_interval : int + """ + config_ = config + force_ = force + name_ = name + name_change_ = name_change + reset_ = reset + token_rotate_interval_ = token_rotate_interval + + # Validate arguments against known Juju API types. + if config_ is not None and not isinstance(config_, dict): + raise Exception( + f"Expected config_ to be a Mapping, received: {type(config_)}" + ) + + if force_ is not None and not isinstance(force_, bool): + raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + if name_change_ is not None and not isinstance(name_change_, (bytes, str)): + raise Exception( + f"Expected name_change_ to be a str, received: {type(name_change_)}" + ) + + if reset_ is not None and not isinstance(reset_, (bytes, str, list)): + raise Exception( + f"Expected reset_ to be a Sequence, received: {type(reset_)}" + ) + + if token_rotate_interval_ is not None and not isinstance( + token_rotate_interval_, int + ): + raise Exception( + f"Expected token_rotate_interval_ to be a int, received: {type(token_rotate_interval_)}" + ) + + self.config = config_ + self.force = force_ + self.name = name_ + self.name_change = name_change_ + self.reset = reset_ + self.token_rotate_interval = token_rotate_interval_ + self.unknown_fields = unknown_fields + + +class UpdateSecretBackendArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~UpdateSecretBackendArg]""" + args_ = [UpdateSecretBackendArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class UpdateUserSecretArg(Type): + _toSchema = { + "auto_prune": "auto-prune", + "content": "content", + "description": "description", + "existing_label": "existing-label", + "expire_time": "expire-time", + "label": "label", + "params": "params", + "rotate_policy": "rotate-policy", + "upsertsecretarg": "UpsertSecretArg", + "uri": "uri", + } + _toPy = { + "UpsertSecretArg": "upsertsecretarg", + "auto-prune": "auto_prune", + "content": "content", + "description": "description", + "existing-label": "existing_label", + "expire-time": "expire_time", + "label": "label", + "params": "params", + "rotate-policy": "rotate_policy", + "uri": "uri", + } + + def __init__( + self, + upsertsecretarg=None, + auto_prune=None, + content=None, + description=None, + existing_label=None, + expire_time=None, + label=None, + params=None, + rotate_policy=None, + uri=None, + **unknown_fields, + ): + """Upsertsecretarg : UpsertSecretArg + auto_prune : bool + content : SecretContentParams + description : str + existing_label : str + expire_time : str + label : str + params : typing.Mapping[str, typing.Any] + rotate_policy : str + uri : str + """ + upsertsecretarg_ = ( + UpsertSecretArg.from_json(upsertsecretarg) if upsertsecretarg else None + ) + auto_prune_ = auto_prune + content_ = SecretContentParams.from_json(content) if content else None + description_ = description + existing_label_ = existing_label + expire_time_ = expire_time + label_ = label + params_ = params + rotate_policy_ = rotate_policy + uri_ = uri + + # Validate arguments against known Juju API types. + if upsertsecretarg_ is not None and not isinstance( + upsertsecretarg_, (dict, UpsertSecretArg) + ): + raise Exception( + f"Expected upsertsecretarg_ to be a UpsertSecretArg, received: {type(upsertsecretarg_)}" + ) + + if auto_prune_ is not None and not isinstance(auto_prune_, bool): + raise Exception( + f"Expected auto_prune_ to be a bool, received: {type(auto_prune_)}" + ) + + if content_ is not None and not isinstance( + content_, (dict, SecretContentParams) + ): + raise Exception( + f"Expected content_ to be a SecretContentParams, received: {type(content_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if existing_label_ is not None and not isinstance( + existing_label_, (bytes, str) + ): + raise Exception( + f"Expected existing_label_ to be a str, received: {type(existing_label_)}" + ) + + if expire_time_ is not None and not isinstance(expire_time_, (bytes, str)): + raise Exception( + f"Expected expire_time_ to be a str, received: {type(expire_time_)}" + ) + + if label_ is not None and not isinstance(label_, (bytes, str)): + raise Exception(f"Expected label_ to be a str, received: {type(label_)}") + + if params_ is not None and not isinstance(params_, dict): + raise Exception( + f"Expected params_ to be a Mapping, received: {type(params_)}" + ) + + if rotate_policy_ is not None and not isinstance(rotate_policy_, (bytes, str)): + raise Exception( + f"Expected rotate_policy_ to be a str, received: {type(rotate_policy_)}" + ) + + if uri_ is not None and not isinstance(uri_, (bytes, str)): + raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") + + self.upsertsecretarg = upsertsecretarg_ + self.auto_prune = auto_prune_ + self.content = content_ + self.description = description_ + self.existing_label = existing_label_ + self.expire_time = expire_time_ + self.label = label_ + self.params = params_ + self.rotate_policy = rotate_policy_ + self.uri = uri_ + self.unknown_fields = unknown_fields + + +class UpdateUserSecretArgs(Type): + _toSchema = {"args": "args"} + _toPy = {"args": "args"} + + def __init__(self, args=None, **unknown_fields): + """Args : typing.Sequence[~UpdateUserSecretArg]""" + args_ = [UpdateUserSecretArg.from_json(o) for o in args or []] + + # Validate arguments against known Juju API types. + if args_ is not None and not isinstance(args_, (bytes, str, list)): + raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") + + self.args = args_ + self.unknown_fields = unknown_fields + + +class UpgradeModelParams(Type): + _toSchema = { + "agent_stream": "agent-stream", + "dry_run": "dry-run", + "ignore_agent_versions": "ignore-agent-versions", + "model_tag": "model-tag", + "target_version": "target-version", + } + _toPy = { + "agent-stream": "agent_stream", + "dry-run": "dry_run", + "ignore-agent-versions": "ignore_agent_versions", + "model-tag": "model_tag", + "target-version": "target_version", + } + + def __init__( + self, + agent_stream=None, + dry_run=None, + ignore_agent_versions=None, + model_tag=None, + target_version=None, + **unknown_fields, + ): + """agent_stream : str + dry_run : bool + ignore_agent_versions : bool + model_tag : str + target_version : Number + """ + agent_stream_ = agent_stream + dry_run_ = dry_run + ignore_agent_versions_ = ignore_agent_versions + model_tag_ = model_tag + target_version_ = Number.from_json(target_version) if target_version else None + + # Validate arguments against known Juju API types. + if agent_stream_ is not None and not isinstance(agent_stream_, (bytes, str)): + raise Exception( + f"Expected agent_stream_ to be a str, received: {type(agent_stream_)}" + ) + + if dry_run_ is not None and not isinstance(dry_run_, bool): + raise Exception( + f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" + ) + + if ignore_agent_versions_ is not None and not isinstance( + ignore_agent_versions_, bool + ): + raise Exception( + f"Expected ignore_agent_versions_ to be a bool, received: {type(ignore_agent_versions_)}" + ) + + if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): + raise Exception( + f"Expected model_tag_ to be a str, received: {type(model_tag_)}" + ) + + if target_version_ is not None and not isinstance( + target_version_, (dict, Number) + ): + raise Exception( + f"Expected target_version_ to be a Number, received: {type(target_version_)}" + ) + + self.agent_stream = agent_stream_ + self.dry_run = dry_run_ + self.ignore_agent_versions = ignore_agent_versions_ + self.model_tag = model_tag_ + self.target_version = target_version_ + self.unknown_fields = unknown_fields + + +class UpgradeModelResult(Type): + _toSchema = {"chosen_version": "chosen-version", "error": "error"} + _toPy = {"chosen-version": "chosen_version", "error": "error"} + + def __init__(self, chosen_version=None, error=None, **unknown_fields): + """chosen_version : Number + error : Error + """ + chosen_version_ = Number.from_json(chosen_version) if chosen_version else None + error_ = Error.from_json(error) if error else None + + # Validate arguments against known Juju API types. + if chosen_version_ is not None and not isinstance( + chosen_version_, (dict, Number) + ): + raise Exception( + f"Expected chosen_version_ to be a Number, received: {type(chosen_version_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + self.chosen_version = chosen_version_ + self.error = error_ + self.unknown_fields = unknown_fields + + +class UpgradeSeriesNotificationParam(Type): + _toSchema = {"entity": "entity", "watcher_id": "watcher-id"} + _toPy = {"entity": "entity", "watcher-id": "watcher_id"} + + def __init__(self, entity=None, watcher_id=None, **unknown_fields): + """Entity : Entity + watcher_id : str + """ + entity_ = Entity.from_json(entity) if entity else None + watcher_id_ = watcher_id + + # Validate arguments against known Juju API types. + if entity_ is not None and not isinstance(entity_, (dict, Entity)): + raise Exception( + f"Expected entity_ to be a Entity, received: {type(entity_)}" + ) + + if watcher_id_ is not None and not isinstance(watcher_id_, (bytes, str)): + raise Exception( + f"Expected watcher_id_ to be a str, received: {type(watcher_id_)}" + ) + + self.entity = entity_ + self.watcher_id = watcher_id_ + self.unknown_fields = unknown_fields + + +class UpgradeSeriesNotificationParams(Type): + _toSchema = {"params": "params"} + _toPy = {"params": "params"} + + def __init__(self, params=None, **unknown_fields): + """Params : typing.Sequence[~UpgradeSeriesNotificationParam]""" + params_ = [UpgradeSeriesNotificationParam.from_json(o) for o in params or []] + + # Validate arguments against known Juju API types. + if params_ is not None and not isinstance(params_, (bytes, str, list)): + raise Exception( + f"Expected params_ to be a Sequence, received: {type(params_)}" + ) + + self.params = params_ + self.unknown_fields = unknown_fields + + +class UpgradeSeriesUnitsResult(Type): + _toSchema = {"error": "error", "unit_names": "unit-names"} + _toPy = {"error": "error", "unit-names": "unit_names"} + + def __init__(self, error=None, unit_names=None, **unknown_fields): + """Error : Error + unit_names : typing.Sequence[str] + """ + error_ = Error.from_json(error) if error else None + unit_names_ = unit_names + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if unit_names_ is not None and not isinstance(unit_names_, (bytes, str, list)): + raise Exception( + f"Expected unit_names_ to be a Sequence, received: {type(unit_names_)}" + ) + + self.error = error_ + self.unit_names = unit_names_ + self.unknown_fields = unknown_fields + + +class UpgradeSeriesUnitsResults(Type): + _toSchema = {"results": "Results"} + _toPy = {"Results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~UpgradeSeriesUnitsResult]""" + results_ = [UpgradeSeriesUnitsResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class UpsertSecretArg(Type): + _toSchema = { + "content": "content", + "description": "description", + "expire_time": "expire-time", + "label": "label", + "params": "params", + "rotate_policy": "rotate-policy", + } + _toPy = { + "content": "content", + "description": "description", + "expire-time": "expire_time", + "label": "label", + "params": "params", + "rotate-policy": "rotate_policy", + } + + def __init__( + self, + content=None, + description=None, + expire_time=None, + label=None, + params=None, + rotate_policy=None, + **unknown_fields, + ): + """Content : SecretContentParams + description : str + expire_time : str + label : str + params : typing.Mapping[str, typing.Any] + rotate_policy : str + """ + content_ = SecretContentParams.from_json(content) if content else None + description_ = description + expire_time_ = expire_time + label_ = label + params_ = params + rotate_policy_ = rotate_policy + + # Validate arguments against known Juju API types. + if content_ is not None and not isinstance( + content_, (dict, SecretContentParams) + ): + raise Exception( + f"Expected content_ to be a SecretContentParams, received: {type(content_)}" + ) + + if description_ is not None and not isinstance(description_, (bytes, str)): + raise Exception( + f"Expected description_ to be a str, received: {type(description_)}" + ) + + if expire_time_ is not None and not isinstance(expire_time_, (bytes, str)): + raise Exception( + f"Expected expire_time_ to be a str, received: {type(expire_time_)}" + ) + + if label_ is not None and not isinstance(label_, (bytes, str)): + raise Exception(f"Expected label_ to be a str, received: {type(label_)}") + + if params_ is not None and not isinstance(params_, dict): + raise Exception( + f"Expected params_ to be a Mapping, received: {type(params_)}" + ) + + if rotate_policy_ is not None and not isinstance(rotate_policy_, (bytes, str)): + raise Exception( + f"Expected rotate_policy_ to be a str, received: {type(rotate_policy_)}" + ) + + self.content = content_ + self.description = description_ + self.expire_time = expire_time_ + self.label = label_ + self.params = params_ + self.rotate_policy = rotate_policy_ + self.unknown_fields = unknown_fields + + +class UserAccess(Type): + _toSchema = {"access": "access", "user_tag": "user-tag"} + _toPy = {"access": "access", "user-tag": "user_tag"} + + def __init__(self, access=None, user_tag=None, **unknown_fields): + """Access : str + user_tag : str + """ + access_ = access + user_tag_ = user_tag + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): + raise Exception( + f"Expected user_tag_ to be a str, received: {type(user_tag_)}" + ) + + self.access = access_ + self.user_tag = user_tag_ + self.unknown_fields = unknown_fields + + +class UserAccessResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : UserAccess + """ + error_ = Error.from_json(error) if error else None + result_ = UserAccess.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, UserAccess)): + raise Exception( + f"Expected result_ to be a UserAccess, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class UserAccessResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~UserAccessResult]""" + results_ = [UserAccessResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class UserCloud(Type): + _toSchema = {"cloud_tag": "cloud-tag", "user_tag": "user-tag"} + _toPy = {"cloud-tag": "cloud_tag", "user-tag": "user_tag"} + + def __init__(self, cloud_tag=None, user_tag=None, **unknown_fields): + """cloud_tag : str + user_tag : str + """ + cloud_tag_ = cloud_tag + user_tag_ = user_tag + + # Validate arguments against known Juju API types. + if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): + raise Exception( + f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" + ) + + if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): + raise Exception( + f"Expected user_tag_ to be a str, received: {type(user_tag_)}" + ) + + self.cloud_tag = cloud_tag_ + self.user_tag = user_tag_ + self.unknown_fields = unknown_fields + + +class UserClouds(Type): + _toSchema = {"user_clouds": "user-clouds"} + _toPy = {"user-clouds": "user_clouds"} + + def __init__(self, user_clouds=None, **unknown_fields): + """user_clouds : typing.Sequence[~UserCloud]""" + user_clouds_ = [UserCloud.from_json(o) for o in user_clouds or []] + + # Validate arguments against known Juju API types. + if user_clouds_ is not None and not isinstance( + user_clouds_, (bytes, str, list) + ): + raise Exception( + f"Expected user_clouds_ to be a Sequence, received: {type(user_clouds_)}" + ) + + self.user_clouds = user_clouds_ + self.unknown_fields = unknown_fields + + +class UserInfo(Type): + _toSchema = { + "access": "access", + "created_by": "created-by", + "date_created": "date-created", + "disabled": "disabled", + "display_name": "display-name", + "last_connection": "last-connection", + "username": "username", + } + _toPy = { + "access": "access", + "created-by": "created_by", + "date-created": "date_created", + "disabled": "disabled", + "display-name": "display_name", + "last-connection": "last_connection", + "username": "username", + } + + def __init__( + self, + access=None, + created_by=None, + date_created=None, + disabled=None, + display_name=None, + last_connection=None, + username=None, + **unknown_fields, + ): + """Access : str + created_by : str + date_created : str + disabled : bool + display_name : str + last_connection : str + username : str + """ + access_ = access + created_by_ = created_by + date_created_ = date_created + disabled_ = disabled + display_name_ = display_name + last_connection_ = last_connection + username_ = username + + # Validate arguments against known Juju API types. + if access_ is not None and not isinstance(access_, (bytes, str)): + raise Exception(f"Expected access_ to be a str, received: {type(access_)}") + + if created_by_ is not None and not isinstance(created_by_, (bytes, str)): + raise Exception( + f"Expected created_by_ to be a str, received: {type(created_by_)}" + ) + + if date_created_ is not None and not isinstance(date_created_, (bytes, str)): + raise Exception( + f"Expected date_created_ to be a str, received: {type(date_created_)}" + ) + + if disabled_ is not None and not isinstance(disabled_, bool): + raise Exception( + f"Expected disabled_ to be a bool, received: {type(disabled_)}" + ) + + if display_name_ is not None and not isinstance(display_name_, (bytes, str)): + raise Exception( + f"Expected display_name_ to be a str, received: {type(display_name_)}" + ) + + if last_connection_ is not None and not isinstance( + last_connection_, (bytes, str) + ): + raise Exception( + f"Expected last_connection_ to be a str, received: {type(last_connection_)}" + ) + + if username_ is not None and not isinstance(username_, (bytes, str)): + raise Exception( + f"Expected username_ to be a str, received: {type(username_)}" + ) + + self.access = access_ + self.created_by = created_by_ + self.date_created = date_created_ + self.disabled = disabled_ + self.display_name = display_name_ + self.last_connection = last_connection_ + self.username = username_ + self.unknown_fields = unknown_fields + + +class UserInfoRequest(Type): + _toSchema = {"entities": "entities", "include_disabled": "include-disabled"} + _toPy = {"entities": "entities", "include-disabled": "include_disabled"} + + def __init__(self, entities=None, include_disabled=None, **unknown_fields): + """Entities : typing.Sequence[~Entity] + include_disabled : bool + """ + entities_ = [Entity.from_json(o) for o in entities or []] + include_disabled_ = include_disabled + + # Validate arguments against known Juju API types. + if entities_ is not None and not isinstance(entities_, (bytes, str, list)): + raise Exception( + f"Expected entities_ to be a Sequence, received: {type(entities_)}" + ) + + if include_disabled_ is not None and not isinstance(include_disabled_, bool): + raise Exception( + f"Expected include_disabled_ to be a bool, received: {type(include_disabled_)}" + ) + + self.entities = entities_ + self.include_disabled = include_disabled_ + self.unknown_fields = unknown_fields + + +class UserInfoResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : UserInfo + """ + error_ = Error.from_json(error) if error else None + result_ = UserInfo.from_json(result) if result else None + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (dict, UserInfo)): + raise Exception( + f"Expected result_ to be a UserInfo, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class UserInfoResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~UserInfoResult]""" + results_ = [UserInfoResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class UserModel(Type): + _toSchema = {"last_connection": "last-connection", "model": "model"} + _toPy = {"last-connection": "last_connection", "model": "model"} + + def __init__(self, last_connection=None, model=None, **unknown_fields): + """last_connection : str + model : Model + """ + last_connection_ = last_connection + model_ = Model.from_json(model) if model else None + + # Validate arguments against known Juju API types. + if last_connection_ is not None and not isinstance( + last_connection_, (bytes, str) + ): + raise Exception( + f"Expected last_connection_ to be a str, received: {type(last_connection_)}" + ) + + if model_ is not None and not isinstance(model_, (dict, Model)): + raise Exception(f"Expected model_ to be a Model, received: {type(model_)}") + + self.last_connection = last_connection_ + self.model = model_ + self.unknown_fields = unknown_fields + + +class UserModelList(Type): + _toSchema = {"user_models": "user-models"} + _toPy = {"user-models": "user_models"} + + def __init__(self, user_models=None, **unknown_fields): + """user_models : typing.Sequence[~UserModel]""" + user_models_ = [UserModel.from_json(o) for o in user_models or []] + + # Validate arguments against known Juju API types. + if user_models_ is not None and not isinstance( + user_models_, (bytes, str, list) + ): + raise Exception( + f"Expected user_models_ to be a Sequence, received: {type(user_models_)}" + ) + + self.user_models = user_models_ + self.unknown_fields = unknown_fields + + +class Value(Type): + _toSchema = { + "allocate_public_ip": "allocate-public-ip", + "arch": "arch", + "container": "container", + "cores": "cores", + "cpu_power": "cpu-power", + "image_id": "image-id", + "instance_role": "instance-role", + "instance_type": "instance-type", + "mem": "mem", + "root_disk": "root-disk", + "root_disk_source": "root-disk-source", + "spaces": "spaces", + "tags": "tags", + "virt_type": "virt-type", + "zones": "zones", + } + _toPy = { + "allocate-public-ip": "allocate_public_ip", + "arch": "arch", + "container": "container", + "cores": "cores", + "cpu-power": "cpu_power", + "image-id": "image_id", + "instance-role": "instance_role", + "instance-type": "instance_type", + "mem": "mem", + "root-disk": "root_disk", + "root-disk-source": "root_disk_source", + "spaces": "spaces", + "tags": "tags", + "virt-type": "virt_type", + "zones": "zones", + } + + def __init__( + self, + allocate_public_ip=None, + arch=None, + container=None, + cores=None, + cpu_power=None, + image_id=None, + instance_role=None, + instance_type=None, + mem=None, + root_disk=None, + root_disk_source=None, + spaces=None, + tags=None, + virt_type=None, + zones=None, + **unknown_fields, + ): + """allocate_public_ip : bool + arch : str + container : str + cores : int + cpu_power : int + image_id : str + instance_role : str + instance_type : str + mem : int + root_disk : int + root_disk_source : str + spaces : typing.Sequence[str] + tags : typing.Sequence[str] + virt_type : str + zones : typing.Sequence[str] + """ + allocate_public_ip_ = allocate_public_ip + arch_ = arch + container_ = container + cores_ = cores + cpu_power_ = cpu_power + image_id_ = image_id + instance_role_ = instance_role + instance_type_ = instance_type + mem_ = mem + root_disk_ = root_disk + root_disk_source_ = root_disk_source + spaces_ = spaces + tags_ = tags + virt_type_ = virt_type + zones_ = zones + + # Validate arguments against known Juju API types. + if allocate_public_ip_ is not None and not isinstance( + allocate_public_ip_, bool + ): + raise Exception( + f"Expected allocate_public_ip_ to be a bool, received: {type(allocate_public_ip_)}" + ) + + if arch_ is not None and not isinstance(arch_, (bytes, str)): + raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") + + if container_ is not None and not isinstance(container_, (bytes, str)): + raise Exception( + f"Expected container_ to be a str, received: {type(container_)}" + ) + + if cores_ is not None and not isinstance(cores_, int): + raise Exception(f"Expected cores_ to be a int, received: {type(cores_)}") + + if cpu_power_ is not None and not isinstance(cpu_power_, int): + raise Exception( + f"Expected cpu_power_ to be a int, received: {type(cpu_power_)}" + ) + + if image_id_ is not None and not isinstance(image_id_, (bytes, str)): + raise Exception( + f"Expected image_id_ to be a str, received: {type(image_id_)}" + ) + + if instance_role_ is not None and not isinstance(instance_role_, (bytes, str)): + raise Exception( + f"Expected instance_role_ to be a str, received: {type(instance_role_)}" + ) + + if instance_type_ is not None and not isinstance(instance_type_, (bytes, str)): + raise Exception( + f"Expected instance_type_ to be a str, received: {type(instance_type_)}" + ) + + if mem_ is not None and not isinstance(mem_, int): + raise Exception(f"Expected mem_ to be a int, received: {type(mem_)}") + + if root_disk_ is not None and not isinstance(root_disk_, int): + raise Exception( + f"Expected root_disk_ to be a int, received: {type(root_disk_)}" + ) + + if root_disk_source_ is not None and not isinstance( + root_disk_source_, (bytes, str) + ): + raise Exception( + f"Expected root_disk_source_ to be a str, received: {type(root_disk_source_)}" + ) + + if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): + raise Exception( + f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" + ) + + if tags_ is not None and not isinstance(tags_, (bytes, str, list)): + raise Exception(f"Expected tags_ to be a Sequence, received: {type(tags_)}") + + if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): + raise Exception( + f"Expected virt_type_ to be a str, received: {type(virt_type_)}" + ) + + if zones_ is not None and not isinstance(zones_, (bytes, str, list)): + raise Exception( + f"Expected zones_ to be a Sequence, received: {type(zones_)}" + ) + + self.allocate_public_ip = allocate_public_ip_ + self.arch = arch_ + self.container = container_ + self.cores = cores_ + self.cpu_power = cpu_power_ + self.image_id = image_id_ + self.instance_role = instance_role_ + self.instance_type = instance_type_ + self.mem = mem_ + self.root_disk = root_disk_ + self.root_disk_source = root_disk_source_ + self.spaces = spaces_ + self.tags = tags_ + self.virt_type = virt_type_ + self.zones = zones_ + self.unknown_fields = unknown_fields + + +class VolumeAttachmentDetails(Type): + _toSchema = { + "bus_address": "bus-address", + "device_link": "device-link", + "device_name": "device-name", + "life": "life", + "plan_info": "plan-info", + "read_only": "read-only", + "volumeattachmentinfo": "VolumeAttachmentInfo", + } + _toPy = { + "VolumeAttachmentInfo": "volumeattachmentinfo", + "bus-address": "bus_address", + "device-link": "device_link", + "device-name": "device_name", + "life": "life", + "plan-info": "plan_info", + "read-only": "read_only", + } + + def __init__( + self, + volumeattachmentinfo=None, + bus_address=None, + device_link=None, + device_name=None, + life=None, + plan_info=None, + read_only=None, + **unknown_fields, + ): + """Volumeattachmentinfo : VolumeAttachmentInfo + bus_address : str + device_link : str + device_name : str + life : str + plan_info : VolumeAttachmentPlanInfo + read_only : bool + """ + volumeattachmentinfo_ = ( + VolumeAttachmentInfo.from_json(volumeattachmentinfo) + if volumeattachmentinfo + else None + ) + bus_address_ = bus_address + device_link_ = device_link + device_name_ = device_name + life_ = life + plan_info_ = ( + VolumeAttachmentPlanInfo.from_json(plan_info) if plan_info else None + ) + read_only_ = read_only + + # Validate arguments against known Juju API types. + if volumeattachmentinfo_ is not None and not isinstance( + volumeattachmentinfo_, (dict, VolumeAttachmentInfo) + ): + raise Exception( + f"Expected volumeattachmentinfo_ to be a VolumeAttachmentInfo, received: {type(volumeattachmentinfo_)}" + ) + + if bus_address_ is not None and not isinstance(bus_address_, (bytes, str)): + raise Exception( + f"Expected bus_address_ to be a str, received: {type(bus_address_)}" + ) + + if device_link_ is not None and not isinstance(device_link_, (bytes, str)): + raise Exception( + f"Expected device_link_ to be a str, received: {type(device_link_)}" + ) + + if device_name_ is not None and not isinstance(device_name_, (bytes, str)): + raise Exception( + f"Expected device_name_ to be a str, received: {type(device_name_)}" + ) + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if plan_info_ is not None and not isinstance( + plan_info_, (dict, VolumeAttachmentPlanInfo) + ): + raise Exception( + f"Expected plan_info_ to be a VolumeAttachmentPlanInfo, received: {type(plan_info_)}" + ) + + if read_only_ is not None and not isinstance(read_only_, bool): + raise Exception( + f"Expected read_only_ to be a bool, received: {type(read_only_)}" + ) + + self.volumeattachmentinfo = volumeattachmentinfo_ + self.bus_address = bus_address_ + self.device_link = device_link_ + self.device_name = device_name_ + self.life = life_ + self.plan_info = plan_info_ + self.read_only = read_only_ + self.unknown_fields = unknown_fields + + +class VolumeAttachmentInfo(Type): + _toSchema = { + "bus_address": "bus-address", + "device_link": "device-link", + "device_name": "device-name", + "plan_info": "plan-info", + "read_only": "read-only", + } + _toPy = { + "bus-address": "bus_address", + "device-link": "device_link", + "device-name": "device_name", + "plan-info": "plan_info", + "read-only": "read_only", + } + + def __init__( + self, + bus_address=None, + device_link=None, + device_name=None, + plan_info=None, + read_only=None, + **unknown_fields, + ): + """bus_address : str + device_link : str + device_name : str + plan_info : VolumeAttachmentPlanInfo + read_only : bool + """ + bus_address_ = bus_address + device_link_ = device_link + device_name_ = device_name + plan_info_ = ( + VolumeAttachmentPlanInfo.from_json(plan_info) if plan_info else None + ) + read_only_ = read_only + + # Validate arguments against known Juju API types. + if bus_address_ is not None and not isinstance(bus_address_, (bytes, str)): + raise Exception( + f"Expected bus_address_ to be a str, received: {type(bus_address_)}" + ) + + if device_link_ is not None and not isinstance(device_link_, (bytes, str)): + raise Exception( + f"Expected device_link_ to be a str, received: {type(device_link_)}" + ) + + if device_name_ is not None and not isinstance(device_name_, (bytes, str)): + raise Exception( + f"Expected device_name_ to be a str, received: {type(device_name_)}" + ) + + if plan_info_ is not None and not isinstance( + plan_info_, (dict, VolumeAttachmentPlanInfo) + ): + raise Exception( + f"Expected plan_info_ to be a VolumeAttachmentPlanInfo, received: {type(plan_info_)}" + ) + + if read_only_ is not None and not isinstance(read_only_, bool): + raise Exception( + f"Expected read_only_ to be a bool, received: {type(read_only_)}" + ) + + self.bus_address = bus_address_ + self.device_link = device_link_ + self.device_name = device_name_ + self.plan_info = plan_info_ + self.read_only = read_only_ + self.unknown_fields = unknown_fields + + +class VolumeAttachmentPlanInfo(Type): + _toSchema = {"device_attributes": "device-attributes", "device_type": "device-type"} + _toPy = {"device-attributes": "device_attributes", "device-type": "device_type"} + + def __init__(self, device_attributes=None, device_type=None, **unknown_fields): + """device_attributes : typing.Mapping[str, str] + device_type : str + """ + device_attributes_ = device_attributes + device_type_ = device_type + + # Validate arguments against known Juju API types. + if device_attributes_ is not None and not isinstance(device_attributes_, dict): + raise Exception( + f"Expected device_attributes_ to be a Mapping, received: {type(device_attributes_)}" + ) + + if device_type_ is not None and not isinstance(device_type_, (bytes, str)): + raise Exception( + f"Expected device_type_ to be a str, received: {type(device_type_)}" + ) + + self.device_attributes = device_attributes_ + self.device_type = device_type_ + self.unknown_fields = unknown_fields + + +class VolumeDetails(Type): + _toSchema = { + "info": "info", + "life": "life", + "machine_attachments": "machine-attachments", + "status": "status", + "storage": "storage", + "unit_attachments": "unit-attachments", + "volume_tag": "volume-tag", + } + _toPy = { + "info": "info", + "life": "life", + "machine-attachments": "machine_attachments", + "status": "status", + "storage": "storage", + "unit-attachments": "unit_attachments", + "volume-tag": "volume_tag", + } + + def __init__( + self, + info=None, + life=None, + machine_attachments=None, + status=None, + storage=None, + unit_attachments=None, + volume_tag=None, + **unknown_fields, + ): + """Info : VolumeInfo + life : str + machine_attachments : typing.Mapping[str, ~VolumeAttachmentDetails] + status : EntityStatus + storage : StorageDetails + unit_attachments : typing.Mapping[str, ~VolumeAttachmentDetails] + volume_tag : str + """ + info_ = VolumeInfo.from_json(info) if info else None + life_ = life + machine_attachments_ = { + k: VolumeAttachmentDetails.from_json(v) + for k, v in (machine_attachments or dict()).items() + } + status_ = EntityStatus.from_json(status) if status else None + storage_ = StorageDetails.from_json(storage) if storage else None + unit_attachments_ = { + k: VolumeAttachmentDetails.from_json(v) + for k, v in (unit_attachments or dict()).items() + } + volume_tag_ = volume_tag + + # Validate arguments against known Juju API types. + if info_ is not None and not isinstance(info_, (dict, VolumeInfo)): + raise Exception( + f"Expected info_ to be a VolumeInfo, received: {type(info_)}" + ) + + if life_ is not None and not isinstance(life_, (bytes, str)): + raise Exception(f"Expected life_ to be a str, received: {type(life_)}") + + if machine_attachments_ is not None and not isinstance( + machine_attachments_, dict + ): + raise Exception( + f"Expected machine_attachments_ to be a Mapping, received: {type(machine_attachments_)}" + ) + + if status_ is not None and not isinstance(status_, (dict, EntityStatus)): + raise Exception( + f"Expected status_ to be a EntityStatus, received: {type(status_)}" + ) + + if storage_ is not None and not isinstance(storage_, (dict, StorageDetails)): + raise Exception( + f"Expected storage_ to be a StorageDetails, received: {type(storage_)}" + ) + + if unit_attachments_ is not None and not isinstance(unit_attachments_, dict): + raise Exception( + f"Expected unit_attachments_ to be a Mapping, received: {type(unit_attachments_)}" + ) + + if volume_tag_ is not None and not isinstance(volume_tag_, (bytes, str)): + raise Exception( + f"Expected volume_tag_ to be a str, received: {type(volume_tag_)}" + ) + + self.info = info_ + self.life = life_ + self.machine_attachments = machine_attachments_ + self.status = status_ + self.storage = storage_ + self.unit_attachments = unit_attachments_ + self.volume_tag = volume_tag_ + self.unknown_fields = unknown_fields + + +class VolumeDetailsListResult(Type): + _toSchema = {"error": "error", "result": "result"} + _toPy = {"error": "error", "result": "result"} + + def __init__(self, error=None, result=None, **unknown_fields): + """Error : Error + result : typing.Sequence[~VolumeDetails] + """ + error_ = Error.from_json(error) if error else None + result_ = [VolumeDetails.from_json(o) for o in result or []] + + # Validate arguments against known Juju API types. + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if result_ is not None and not isinstance(result_, (bytes, str, list)): + raise Exception( + f"Expected result_ to be a Sequence, received: {type(result_)}" + ) + + self.error = error_ + self.result = result_ + self.unknown_fields = unknown_fields + + +class VolumeDetailsListResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~VolumeDetailsListResult]""" + results_ = [VolumeDetailsListResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields + + +class VolumeFilter(Type): + _toSchema = {"machines": "machines"} + _toPy = {"machines": "machines"} + + def __init__(self, machines=None, **unknown_fields): + """Machines : typing.Sequence[str]""" + machines_ = machines + + # Validate arguments against known Juju API types. + if machines_ is not None and not isinstance(machines_, (bytes, str, list)): + raise Exception( + f"Expected machines_ to be a Sequence, received: {type(machines_)}" + ) + + self.machines = machines_ + self.unknown_fields = unknown_fields + + +class VolumeFilters(Type): + _toSchema = {"filters": "filters"} + _toPy = {"filters": "filters"} + + def __init__(self, filters=None, **unknown_fields): + """Filters : typing.Sequence[~VolumeFilter]""" + filters_ = [VolumeFilter.from_json(o) for o in filters or []] + + # Validate arguments against known Juju API types. + if filters_ is not None and not isinstance(filters_, (bytes, str, list)): + raise Exception( + f"Expected filters_ to be a Sequence, received: {type(filters_)}" + ) + + self.filters = filters_ + self.unknown_fields = unknown_fields + + +class VolumeInfo(Type): + _toSchema = { + "hardware_id": "hardware-id", + "persistent": "persistent", + "pool": "pool", + "size": "size", + "volume_id": "volume-id", + "wwn": "wwn", + } + _toPy = { + "hardware-id": "hardware_id", + "persistent": "persistent", + "pool": "pool", + "size": "size", + "volume-id": "volume_id", + "wwn": "wwn", + } + + def __init__( + self, + hardware_id=None, + persistent=None, + pool=None, + size=None, + volume_id=None, + wwn=None, + **unknown_fields, + ): + """hardware_id : str + persistent : bool + pool : str + size : int + volume_id : str + wwn : str + """ + hardware_id_ = hardware_id + persistent_ = persistent + pool_ = pool + size_ = size + volume_id_ = volume_id + wwn_ = wwn + + # Validate arguments against known Juju API types. + if hardware_id_ is not None and not isinstance(hardware_id_, (bytes, str)): + raise Exception( + f"Expected hardware_id_ to be a str, received: {type(hardware_id_)}" + ) + + if persistent_ is not None and not isinstance(persistent_, bool): + raise Exception( + f"Expected persistent_ to be a bool, received: {type(persistent_)}" + ) + + if pool_ is not None and not isinstance(pool_, (bytes, str)): + raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") + + if size_ is not None and not isinstance(size_, int): + raise Exception(f"Expected size_ to be a int, received: {type(size_)}") + + if volume_id_ is not None and not isinstance(volume_id_, (bytes, str)): + raise Exception( + f"Expected volume_id_ to be a str, received: {type(volume_id_)}" + ) + + if wwn_ is not None and not isinstance(wwn_, (bytes, str)): + raise Exception(f"Expected wwn_ to be a str, received: {type(wwn_)}") + + self.hardware_id = hardware_id_ + self.persistent = persistent_ + self.pool = pool_ + self.size = size_ + self.volume_id = volume_id_ + self.wwn = wwn_ + self.unknown_fields = unknown_fields + + +class ZoneResult(Type): + _toSchema = {"available": "available", "error": "error", "name": "name"} + _toPy = {"available": "available", "error": "error", "name": "name"} + + def __init__(self, available=None, error=None, name=None, **unknown_fields): + """Available : bool + error : Error + name : str + """ + available_ = available + error_ = Error.from_json(error) if error else None + name_ = name + + # Validate arguments against known Juju API types. + if available_ is not None and not isinstance(available_, bool): + raise Exception( + f"Expected available_ to be a bool, received: {type(available_)}" + ) + + if error_ is not None and not isinstance(error_, (dict, Error)): + raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") + + if name_ is not None and not isinstance(name_, (bytes, str)): + raise Exception(f"Expected name_ to be a str, received: {type(name_)}") + + self.available = available_ + self.error = error_ + self.name = name_ + self.unknown_fields = unknown_fields + + +class ZoneResults(Type): + _toSchema = {"results": "results"} + _toPy = {"results": "results"} + + def __init__(self, results=None, **unknown_fields): + """Results : typing.Sequence[~ZoneResult]""" + results_ = [ZoneResult.from_json(o) for o in results or []] + + # Validate arguments against known Juju API types. + if results_ is not None and not isinstance(results_, (bytes, str, list)): + raise Exception( + f"Expected results_ to be a Sequence, received: {type(results_)}" + ) + + self.results = results_ + self.unknown_fields = unknown_fields diff --git a/build/lib/juju/client/client.py b/build/lib/juju/client/client.py new file mode 100644 index 00000000..e7e073b2 --- /dev/null +++ b/build/lib/juju/client/client.py @@ -0,0 +1,35 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +"""Replace auto-generated classes with our own, where necessary.""" + +from . import _client, _definitions, overrides # isort:skip + +for o in overrides.__all__: + if "Facade" not in o: + # Override stuff in _definitions, which is all imported + # into _client. We Monkey patch both the original class and + # the ref in _client (import shenanigans are fun!) + setattr(_definitions, o, getattr(overrides, o)) + setattr(_client, o, getattr(overrides, o)) + # We shouldn't be overriding Facades! + else: + raise ValueError( + "Cannot override a versioned Facade class -- you must patch it instead." + ) + +for o in overrides.__patches__: + # Patch a versioned Facade. + for client_version in _client.CLIENTS.values(): + try: + c_type = getattr(client_version, o) + except AttributeError: + # Not all the _client modules may have the + # facade. That's okay -- we just skip over them. + continue + o_type = getattr(overrides, o) + for a in dir(o_type): + if not a.startswith("_"): + setattr(c_type, a, getattr(o_type, a)) + +from ._client import * # noqa: F403,E402, isort:skip diff --git a/build/lib/juju/client/codegen.py b/build/lib/juju/client/codegen.py new file mode 100644 index 00000000..eda73d02 --- /dev/null +++ b/build/lib/juju/client/codegen.py @@ -0,0 +1,53 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +from collections import defaultdict +from io import StringIO +from textwrap import indent + + +class CodeWriter(StringIO): + """Blob of text that, when used in the context of facade.py, ends up + holding the source code for a Python class and associated methods. + + """ + + INDENT = " " + + CLASS = 0 + METHOD = 1 + + def write(self, msg, depth=0): + if depth: + prefix = self.INDENT * depth + msg = indent(msg, prefix) + + return super().write(msg) + + def __str__(self): + return super().getvalue() + + +class Capture(defaultdict): + """A collection of CodeWriter objects, together representing a Python + module. + + """ + + def __init__(self, default_factory=CodeWriter, *args, **kwargs): + super().__init__(default_factory, *args, **kwargs) + + def clear(self, name): + """Reset one of the keys in this class, if it exists. + + This is necessary, because we don't worry about de-duplicating + the schemas for each version of juju up front, and this gives + us a way to sort of de-duplicate on the fly, by resetting a + specific CodeWriter instance before we start to write a class + into it. + + """ + try: + del self[name] + except KeyError: + pass diff --git a/build/lib/juju/client/connection.py b/build/lib/juju/client/connection.py new file mode 100644 index 00000000..03eb5096 --- /dev/null +++ b/build/lib/juju/client/connection.py @@ -0,0 +1,924 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +import base64 +import json +import logging +import ssl +import urllib.request +import warnings +import weakref +from http.client import HTTPSConnection +from typing import Any, Literal, Sequence + +import macaroonbakery.bakery as bakery +import macaroonbakery.httpbakery as httpbakery +import websockets +from dateutil.parser import parse +from typing_extensions import Self, TypeAlias, overload + +from juju import errors, jasyncio, tag, utils +from juju.client import client +from juju.utils import IdQueue +from juju.version import CLIENT_VERSION + +from .facade import TypeEncoder, _Json, _RichJson +from .facade_versions import client_facade_versions, known_unsupported_facades + +SpecifiedFacades: TypeAlias = "dict[str, dict[Literal['versions'], Sequence[int]]]" +_WebSocket: TypeAlias = websockets.WebSocketClientProtocol + +LEVELS = ["TRACE", "DEBUG", "INFO", "WARNING", "ERROR"] +log = logging.getLogger("juju.client.connection") + + +class Monitor: + """Monitor helper class for our Connection class. + + Contains a reference to an instantiated Connection, along with a + reference to the Connection.receiver Future. Upon inspection of + these objects, this class determines whether the connection is in + an 'error', 'connected' or 'disconnected' state. + + Use this class to stay up to date on the health of a connection, + and take appropriate action if the connection errors out due to + network issues or other unexpected circumstances. + + """ + + ERROR = "error" + CONNECTED = "connected" + DISCONNECTING = "disconnecting" + DISCONNECTED = "disconnected" + + def __init__(self, connection: Connection): + self.connection = weakref.ref(connection) + self.reconnecting = jasyncio.Lock() + self.close_called = jasyncio.Event() + + @property + def status(self): + """Determine the status of the connection and receiver, and return + ERROR, CONNECTED, or DISCONNECTED as appropriate. + + For simplicity, we only consider ourselves to be connected + after the Connection class has setup a receiver task. This + only happens after the websocket is open, and the connection + isn't usable until that receiver has been started. + + """ + connection = self.connection() + + # the connection instance was destroyed but someone kept + # a separate reference to the monitor for some reason + if not connection: + return self.DISCONNECTED + + # connection cleanly disconnected or not yet opened + if not connection._ws: + return self.DISCONNECTED + + # close called but not yet complete + if self.close_called.is_set(): + return self.DISCONNECTING + + # connection closed uncleanly (we didn't call connection.close) + if connection.is_debug_log_connection: + stopped = connection._debug_log_task.cancelled() + else: + stopped = ( + connection._receiver_task is not None + and connection._receiver_task.cancelled() + ) + + if stopped or connection._ws.state is not websockets.protocol.State.OPEN: + return self.ERROR + + # everything is fine! + return self.CONNECTED + + +class Connection: + """Usage:: + + # Connect to an arbitrary api server + client = await Connection.connect( + api_endpoint, model_uuid, username, password, cacert) + + """ + + MAX_FRAME_SIZE = 2**22 + "Maximum size for a single frame. Defaults to 4MB." + facades: dict[str, int] + _specified_facades: dict[str, Sequence[int]] + bakery_client: Any + usertag: str | None + password: str | None + name: str + __request_id__: int + endpoints: list[tuple[str, str]] | None # Set by juju/controller.py + is_debug_log_connection: bool + monitor: Monitor + proxy: Any # Need to find types for this library + max_frame_size: int + _retries: int + _retry_backoff: float + uuid: str | None + messages: IdQueue + _ws: _WebSocket | None + + @classmethod + async def connect( + cls, + endpoint=None, + uuid: str | None = None, + username: str | None = None, + password: str | None = None, + cacert=None, + bakery_client=None, + max_frame_size: int | None = None, + retries=3, + retry_backoff=10, + specified_facades: SpecifiedFacades | None = None, + proxy=None, + debug_log_conn=None, + debug_log_params={}, + ) -> Self: + """Connect to the websocket. + + If uuid is None, the connection will be to the controller. Otherwise it + will be to the model. + + :param str endpoint: The hostname:port of the controller to connect to (or list of strings). + :param str uuid: The model UUID to connect to (None for a + controller-only connection). + :param str username: The username for controller-local users (or None + to use macaroon-based login.) + :param str password: The password for controller-local users. + :param str cacert: The CA certificate of the controller + (PEM formatted). + :param httpbakery.Client bakery_client: The macaroon bakery client to + to use when performing macaroon-based login. Macaroon tokens + acquired when logging will be saved to bakery_client.cookies. + If this is None, a default bakery_client will be used. + :param int max_frame_size: The maximum websocket frame size to allow. + :param int retries: When connecting or reconnecting, and all endpoints + fail, how many times to retry the connection before giving up. + :param int retry_backoff: Number of seconds to increase the wait + between connection retry attempts (a backoff of 10 with 3 retries + would wait 10s, 20s, and 30s). + :param specified_facades: (deprecated) define a series of facade versions you wish to override + to prevent using the conservative client pinning with in the client. + :param TextIOWrapper debug_log_conn: target if this is a debug log connection + :param dict debug_log_params: filtering parameters for the debug-log output + """ + self = cls() + if endpoint is None: + raise ValueError("no endpoint provided") + if not isinstance(endpoint, str) and not isinstance(endpoint, list): + raise TypeError("Endpoint should be either str or list") + self.uuid = uuid + if bakery_client is None: + bakery_client = httpbakery.Client() + self.bakery_client = bakery_client + if username and "@" in username and not username.endswith("@local"): + # We're trying to log in as an external user - we need to use + # macaroon authentication with no username or password. + if password is not None: + raise errors.JujuAuthError( + "cannot log in as external user with a password" + ) + username = None + self.usertag = tag.user(username) + self.password = password + + self.__request_id__ = 0 + + # The following instance variables are initialized by the + # _connect_with_redirect method, but create them here + # as a reminder that they will exist. + self.addr = None + self._ws = None + self.endpoint = None + self.endpoints = None + self.cacert = None + self.info = None + + self.debug_log_target = debug_log_conn + self.is_debug_log_connection = debug_log_conn is not None + self.debug_log_params = debug_log_params + self.debug_log_shown_lines = 0 # number of lines + + # Create that _Task objects but don't start the tasks yet. + self._pinger_task = None + self._receiver_task = None + self._debug_log_task = None + + self._retries = retries + self._retry_backoff = retry_backoff + + self.facades = {} + + if specified_facades: + warnings.warn( + "The `specified_facades` argument is deprecated and will be removed soon", + DeprecationWarning, + stacklevel=3, + ) + self._specified_facades = { + name: d["versions"] for name, d in specified_facades.items() + } + else: + self._specified_facades = {} + + self.messages = IdQueue() + self.monitor = Monitor(connection=self) + if max_frame_size is None: + max_frame_size = self.MAX_FRAME_SIZE + self.max_frame_size = max_frame_size + + self.proxy = proxy + if self.proxy is not None: + self.proxy.connect() + + _endpoints = ( + [(endpoint, cacert)] + if isinstance(endpoint, str) + else [(e, cacert) for e in endpoint] + ) + last_error = None + for _ep in _endpoints: + try: + if self.is_debug_log_connection: + # make a direct connection with basic auth if + # debug-log (i.e. no redirection or login) + await self._connect([_ep]) + else: + await self._connect_with_redirect([_ep]) + return self + except ssl.SSLError as e: + last_error = e + continue + except OSError as e: + logging.debug(f"Cannot access endpoint {_ep}: {e.strerror}") + last_error = e + continue + if last_error is not None: + raise last_error + raise Exception("Unable to connect to websocket") + + @property + def ws(self): + log.warning( + "Direct access to the websocket object may cause disruptions in asyncio event handling." + ) + return self._ws + + @property + def username(self) -> str | None: + if not self.usertag: + return None + return self.usertag[len("user-") :] + + @property + def is_using_old_client(self): + if self.info is None: + raise errors.JujuError("Not connected yet.") + return self.info["server-version"].startswith("2.") + + @property + def is_open(self): + return self.monitor.status == Monitor.CONNECTED + + def _get_ssl(self, cert: str | None = None) -> ssl.SSLContext: + context = ssl.create_default_context( + purpose=ssl.Purpose.SERVER_AUTH, cadata=cert + ) + if cert: + # Disable hostname checking if and only if we have an explicit cert + # to validate against, because the cert doesn't contain the IP addr + # of the controller, which is what self-bootstrapped controllers + # use. And because we pre-share and trust both the cert and + # endpoint address anyway, it's safe to skip that check. + # See: https://github.com/juju/python-libjuju/issues/302 + context.check_hostname = False + return context + + async def _open( + self, endpoint: str, cacert: str + ) -> tuple[_WebSocket, str, str, str]: + if self.is_debug_log_connection: + assert self.uuid + url = f"wss://user-{self.username}:{self.password}@{endpoint}/model/{self.uuid}/log" + elif self.uuid: + url = f"wss://{endpoint}/model/{self.uuid}/api" + else: + url = f"wss://{endpoint}/api" + + # We need to establish a server_hostname here for TLS sni if we are + # connecting through a proxy as the Juju controller certificates will + # not be covering the proxy + sock = None + server_hostname = None + if self.proxy is not None: + sock = self.proxy.socket() + server_hostname = "juju-app" + + return ( + ( + await websockets.connect( + url, + ssl=self._get_ssl(cacert), + max_size=self.max_frame_size, + server_hostname=server_hostname, + sock=sock, + ) + ), + url, + endpoint, + cacert, + ) + + async def close(self, to_reconnect: bool = False): + if not self._ws: + return + self.monitor.close_called.set() + + # Cancel all the tasks (that we started): + tasks_need_to_be_gathered = [] + if self._pinger_task: + tasks_need_to_be_gathered.append(self._pinger_task) + self._pinger_task.cancel() + if self._receiver_task: + tasks_need_to_be_gathered.append(self._receiver_task) + self._receiver_task.cancel() + if self._debug_log_task: + tasks_need_to_be_gathered.append(self._debug_log_task) + self._debug_log_task.cancel() + + await self._ws.close() + + if not to_reconnect: + try: + log.debug("Gathering all tasks for connection close") + await jasyncio.gather(*tasks_need_to_be_gathered) + except jasyncio.CancelledError: + pass + except websockets.exceptions.ConnectionClosed: + pass + + self._pinger_task = None + self._receiver_task = None + self._debug_log_task = None + + if self.proxy is not None: + self.proxy.close() + + async def _recv(self, request_id: int) -> dict[str, Any]: + if not self.is_open: + raise websockets.exceptions.ConnectionClosedOK(None, None) + try: + return await self.messages.get(request_id) + except GeneratorExit: + return {} + + def debug_log_filter_write(self, result): + write_or_not = True + + entity = result["tag"] + msg_lev = result["sev"] + mod = result["mod"] + msg = result["msg"] + + excluded_entities = self.debug_log_params["exclude"] + excluded_modules = self.debug_log_params["exclude_module"] + write_or_not = ( + write_or_not + and (mod not in excluded_modules) + and (entity not in excluded_entities) + ) + + included_entities = self.debug_log_params["include"] + only_these_modules = self.debug_log_params["include_module"] + write_or_not = ( + write_or_not + and (only_these_modules == [] or mod in only_these_modules) + and (included_entities == [] or entity in included_entities) + ) + + log_level = self.debug_log_params["level"] + + if log_level != "" and log_level not in LEVELS: + log.warning( + "Debug Logger: level should be one of %s, given %s" + % (LEVELS, log_level) + ) + else: + write_or_not = write_or_not and ( + log_level == "" or (LEVELS.index(msg_lev) >= LEVELS.index(log_level)) + ) + + # TODO + # lines = self.debug_log_params['lines'] + # no_tail = self.debug_log_params['no_tail'] + + if write_or_not: + ts = parse(result["ts"]) + + self.debug_log_target.write( + "%s %02d:%02d:%02d %s %s %s\n" + % (entity, ts.hour, ts.minute, ts.second, msg_lev, mod, msg) + ) + return 1 + else: + return 0 + + async def _debug_logger(self): + try: + while self.is_open: + result = await utils.run_with_interrupt( + self._ws.recv(), self.monitor.close_called, log=log + ) + if self.monitor.close_called.is_set(): + break + if result is not None and result != "{}\n": + result = json.loads(result) + + number_of_lines_written = self.debug_log_filter_write(result) + + self.debug_log_shown_lines += number_of_lines_written + + if self.debug_log_shown_lines >= self.debug_log_params["limit"]: + jasyncio.create_task(self.close(), name="Task_Close") + return + + except KeyError as e: + log.exception("Unexpected debug line -- %s" % e) + jasyncio.create_task(self.close(), name="Task_Close") + raise + except jasyncio.CancelledError: + jasyncio.create_task(self.close(), name="Task_Close") + raise + except websockets.exceptions.ConnectionClosed: + log.warning("Debug Logger: Connection closed, reconnecting") + # the reconnect has to be done as a task because the receiver will + # be cancelled by the reconnect and we don't want the reconnect + # to be aborted half-way through + jasyncio.ensure_future(self.reconnect()) + return + except Exception as e: + log.exception("Error in debug logger : %s" % e) + jasyncio.create_task(self.close(), name="Task_Close") + raise + + async def _receiver(self): + try: + while self.is_open: + result = await utils.run_with_interrupt( + self._ws.recv(), self.monitor.close_called, log=log + ) + if self.monitor.close_called.is_set(): + break + if result is not None: + result = json.loads(result) + await self.messages.put(result["request-id"], result) + except jasyncio.CancelledError: + log.debug("Receiver: Cancelled") + pass + except websockets.exceptions.ConnectionClosed as e: + log.warning("Receiver: Connection closed, reconnecting") + await self.messages.put_all(e) + # the reconnect has to be done as a task because the receiver will + # be cancelled by the reconnect and we don't want the reconnect + # to be aborted half-way through + jasyncio.ensure_future(self.reconnect()) + return + except Exception as e: + log.exception("Error in receiver") + # make pending listeners aware of the error + await self.messages.put_all(e) + raise + + async def _pinger(self): + """A Controller can time us out if we are silent for too long. This + is especially true in JaaS, which has a fairly strict timeout. + + To prevent timing out, we send a ping every ten seconds. + + """ + + async def _do_ping(): + try: + log.debug(f"Pinger {self._pinger_task}: pinging") + await pinger_facade.Ping() + except jasyncio.CancelledError: + raise + + pinger_facade = client.PingerFacade.from_connection(self) + try: + while True: + await utils.run_with_interrupt( + _do_ping(), self.monitor.close_called, log=log + ) + if self.monitor.close_called.is_set(): + break + await jasyncio.sleep(10) + except jasyncio.CancelledError: + log.debug("Pinger: Cancelled") + pass + except websockets.exceptions.ConnectionClosed: + # The connection has closed - we can't do anything + # more until the connection is restarted. + log.debug("ping failed because of closed connection") + pass + + @overload + async def rpc( + self, msg: dict[str, _Json], encoder: None = None + ) -> dict[str, _Json]: ... + + @overload + async def rpc( + self, msg: dict[str, _RichJson], encoder: TypeEncoder + ) -> dict[str, _Json]: ... + + async def rpc( + self, msg: dict[str, Any], encoder: json.JSONEncoder | None = None + ) -> dict[str, _Json]: + """Make an RPC to the API. The message is encoded as JSON + using the given encoder if any. + :param msg: Parameters for the call (will be encoded as JSON). + :param encoder: Encoder to be used when encoding the message. + :return: The result of the call. + :raises JujuAPIError: When there's an error returned. + :raises JujuError: + """ + self.__request_id__ += 1 + msg["request-id"] = self.__request_id__ + if "params" not in msg: + msg["params"] = {} + if "version" not in msg: + msg["version"] = self.facades[msg["type"]] + outgoing = json.dumps(msg, indent=2, cls=encoder) + log.debug(f"connection id: {id(self)} ---> {outgoing}") + for attempt in range(3): + if self.monitor.status == Monitor.DISCONNECTED: + # closed cleanly; shouldn't try to reconnect + raise websockets.exceptions.ConnectionClosed( + websockets.frames.Close( + websockets.frames.CloseCode.NORMAL_CLOSURE, "websocket closed" + ) + ) + try: + await self._ws.send(outgoing) + break + except websockets.ConnectionClosed: + if attempt == 2: + raise + log.warning("RPC: Connection closed, reconnecting") + # the reconnect has to be done in a separate task because, + # if it is triggered by the pinger, then this RPC call will + # be cancelled when the pinger is cancelled by the reconnect, + # and we don't want the reconnect to be aborted halfway through + await jasyncio.wait([jasyncio.create_task(self.reconnect())]) + if self.monitor.status != Monitor.CONNECTED: + # reconnect failed; abort and shutdown + log.error("RPC: Automatic reconnect failed") + raise + result = await self._recv(msg["request-id"]) + log.debug(f"connection id : {id(self)} <--- {result}") + + if not result: + return result + + if "error" in result: + # API Error Response + raise errors.JujuAPIError(result) + + if "response" not in result: + # This may never happen + return result + + if "results" in result["response"]: + # Check for errors in a result list. + # TODO This loses the results that might have succeeded. + # Perhaps JujuError should return all the results including + # errors, or perhaps a keyword parameter to the rpc method + # could be added to trigger this behaviour. + err_results = [ + res["error"]["message"] + for res in (result["response"]["results"] or []) + if res.get("error", {}).get("message") + ] + if err_results: + raise errors.JujuError(err_results) + + elif result["response"].get("error", {}).get("message"): + raise errors.JujuError(result["response"]["error"]["message"]) + + return result + + def _http_headers(self) -> dict[str, str]: + """Return dictionary of http headers necessary for making an http + connection to the endpoint of this Connection. + + :return: Dictionary of headers + + """ + if not self.usertag: + return {} + + creds = "{}:{}".format(self.usertag, self.password or "") + token = base64.b64encode(creds.encode()) + return {"Authorization": f"Basic {token.decode()}"} + + def https_connection(self) -> tuple[HTTPSConnection, dict[str, str], str]: + """Return an https connection to this Connection's endpoint. + + Returns a 3-tuple containing:: + + 1. The :class:`HTTPSConnection` instance + 2. Dictionary of auth headers to be used with the connection + 3. The root url path (str) to be used for requests. + + """ + endpoint = self.endpoint + # Support IPv6 by right splitting on : and removing [] around IP address for host + host, remainder = endpoint.rsplit(":", 1) + host = host.strip("[]") + port = remainder + if "/" in remainder: + port, _ = remainder.split("/", 1) + + conn = HTTPSConnection( + host, + int(port), + context=self._get_ssl(self.cacert), + ) + + path = f"/model/{self.uuid}" if self.uuid else "" + return conn, self._http_headers(), path + + async def clone(self): + """Return a new Connection, connected to the same websocket endpoint + as this one. + + """ + return await Connection.connect(**self.connect_params()) + + def connect_params(self): + """Return a dict of parameters suitable for passing to + Connection.connect that can be used to make a new connection + to the same controller (and model if specified). + """ + return { + "endpoint": self.endpoint, + "uuid": self.uuid, + "username": self.username, + "password": self.password, + "cacert": self.cacert, + "bakery_client": self.bakery_client, + "max_frame_size": self.max_frame_size, + "proxy": self.proxy, + } + + async def controller(self): + """Return a Connection to the controller at self.endpoint""" + return await Connection.connect( + self.endpoint, + username=self.username, + password=self.password, + cacert=self.cacert, + bakery_client=self.bakery_client, + max_frame_size=self.max_frame_size, + ) + + async def reconnect(self): + """Force a reconnection.""" + monitor = self.monitor + if monitor.reconnecting.locked() or monitor.close_called.is_set(): + return + async with monitor.reconnecting: + await self.close(to_reconnect=True) + connector = ( + self._connect + if self.is_debug_log_connection + else self._connect_with_login + ) + res = await connector( + [(self.endpoint, self.cacert)] if not self.endpoints else self.endpoints + ) + if not self.is_debug_log_connection: + self._build_facades(res.get("facades", {})) + if not self._pinger_task: + log.debug("reconnect: scheduling a pinger task") + self._pinger_task = jasyncio.create_task( + self._pinger(), name="Task_Pinger" + ) + + async def _connect(self, endpoints): + if len(endpoints) == 0: + raise errors.JujuConnectionError("no endpoints to connect to") + + async def _try_endpoint( + endpoint, cacert, delay + ) -> tuple[_WebSocket, str, str, str]: + if delay: + await jasyncio.sleep(delay) + return await self._open(endpoint, cacert) + + # Try all endpoints in parallel, with slight increasing delay (+100ms + # for each subsequent endpoint); the delay allows us to prefer the + # earlier endpoints over the latter. Use first successful connection. + tasks = [ + jasyncio.ensure_future(_try_endpoint(endpoint, cacert, 0.1 * i)) + for i, (endpoint, cacert) in enumerate(endpoints) + ] + result: tuple[_WebSocket, str, str, str] | None = None + + for attempt in range(self._retries + 1): + for task in jasyncio.as_completed(tasks): + try: + result = await task + break + except ConnectionError: + continue # ignore; try another endpoint + else: + _endpoints_str = ", ".join([endpoint for endpoint, cacert in endpoints]) + if attempt < self._retries: + log.debug( + f"Retrying connection to endpoints: {_endpoints_str}; attempt {attempt + 1} of {self._retries + 1}" + ) + await jasyncio.sleep((attempt + 1) * self._retry_backoff) + continue + else: + raise errors.JujuConnectionError( + f"Unable to connect to any endpoint: {_endpoints_str}" + ) + # only executed if inner loop's else did not continue + # (i.e., inner loop did break due to successful connection) + break + + for task in tasks: + task.cancel() + + assert result # loop raises or sets the result + + self._ws = result[0] + self.addr = result[1] + self.endpoint = result[2] + self.cacert = result[3] + + # If this is a debug-log connection, and the _debug_log_task + # is not created yet, then go ahead and schedule it + if self.is_debug_log_connection and not self._debug_log_task: + self._debug_log_task = jasyncio.create_task( + self._debug_logger(), name="Task_Debug_Log" + ) + + # If this is regular connection, and we dont have a + # receiver_task yet, then schedule a _receiver_task + elif not self.is_debug_log_connection and not self._receiver_task: + log.debug("_connect: scheduling a receiver task") + self._receiver_task = jasyncio.create_task( + self._receiver(), name="Task_Receiver" + ) + + log.debug("Driver connected to juju %s", self.addr) + self.monitor.close_called.clear() + + async def _connect_with_login(self, endpoints): + """Connect to the websocket. + + If uuid is None, the connection will be to the controller. Otherwise it + will be to the model. + :return: The response field of login response JSON object. + """ + success = False + try: + await self._connect(endpoints) + # It's possible that we may get several discharge-required errors, + # corresponding to different levels of authentication, so retry + # a few times. + for _ in range(0, 2): + result = (await self.login())["response"] + macaroon_json = result.get("discharge-required") + if macaroon_json is None: + self.info = result + success = True + return result + macaroon = bakery.Macaroon.from_dict(macaroon_json) + self.bakery_client.handle_error( + httpbakery.Error( + code=httpbakery.ERR_DISCHARGE_REQUIRED, + message=result.get("discharge-required-error"), + version=macaroon.version, + info=httpbakery.ErrorInfo( + macaroon=macaroon, + macaroon_path=result.get("macaroon-path"), + ), + ), + # note: remove the port number. + "https://" + self.endpoint + "/", + ) + raise errors.JujuAuthError("failed to authenticate after several attempts") + finally: + if not success: + await self.close() + + async def _connect_with_redirect(self, endpoints): + try: + login_result = await self._connect_with_login(endpoints) + except errors.JujuRedirectException as e: + # Bubble up exception if the client should not follow the redirect + if e.follow_redirect is False: + raise + login_result = await self._connect_with_login(e.endpoints) + self._build_facades(login_result.get("facades", {})) + if not self._pinger_task: + log.debug("_connect_with_redirect: scheduling a pinger task") + self._pinger_task = jasyncio.create_task(self._pinger(), name="Task_Pinger") + + # _build_facades takes the facade list that comes from the connection with the controller, + # validates that the client knows about them (client_facade_versions) and builds the facade list + # (into the self._specified facades) with the max versions that both the client and the controller + # can negotiate on + def _build_facades(self, facades_from_connection): + self.facades.clear() + for facade in facades_from_connection: + name = facade["name"] + if name in self._specified_facades: + client_versions = self._specified_facades[name] + elif name in client_facade_versions: + client_versions = client_facade_versions[name] + elif name in known_unsupported_facades: + continue + else: + log.warning(f"unexpected facade {name} received from the controller") + continue + + controller_versions = facade["versions"] + candidates = set(client_versions) & set(controller_versions) + if not candidates: + log.warning( + f"unknown common facade version for {name},\n" + f"versions known to client : {client_versions}\n" + f"versions known to controller : {controller_versions}" + ) + continue + self.facades[name] = max(candidates) + + async def login(self): + params = {} + # Set the client version + params["client-version"] = CLIENT_VERSION + params["auth-tag"] = self.usertag + if self.password: + params["credentials"] = self.password + else: + macaroons = _macaroons_for_domain(self.bakery_client.cookies, self.endpoint) + params["macaroons"] = [ + [bakery.macaroon_to_dict(m) for m in ms] for ms in macaroons + ] + + try: + return await self.rpc({ + "type": "Admin", + "request": "Login", + "version": 3, + "params": params, + }) + except errors.JujuAPIError as e: + if e.error_code != "redirection required": + raise + log.info("Controller requested redirect") + # Check if the redirect error provides a payload with embedded + # redirection info (juju 2.6+ controller). In this case, return a + # redirect exception which the library should not automatically + # follow but rather bubble up to the user. This matches the + # behaviour of juju cli whereas for JAAS-like redirects we will + # need to make an extra RPC call to get the redirect info. + if e.error_info is not None: + raise errors.JujuRedirectException(e.error_info, False) from e + + # Fetch additional redirection information now so that + # we can safely close the connection after login + # fails. + redirect_info = ( + await self.rpc({ + "type": "Admin", + "request": "RedirectInfo", + "version": 3, + }) + )["response"] + raise errors.JujuRedirectException(redirect_info, True) from e + + +def _macaroons_for_domain(cookies, domain): + """Return any macaroons from the given cookie jar that + apply to the given domain name. + """ + req = urllib.request.Request("https://" + domain + "/") + cookies.add_cookie_header(req) + return httpbakery.extract_macaroons(req) diff --git a/build/lib/juju/client/connector.py b/build/lib/juju/client/connector.py new file mode 100644 index 00000000..cc307902 --- /dev/null +++ b/build/lib/juju/client/connector.py @@ -0,0 +1,232 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +import copy +import logging +from typing import Any + +import macaroonbakery.httpbakery as httpbakery +from packaging import version + +from juju.client import client +from juju.client.connection import Connection +from juju.client.gocookies import GoCookieJar, go_to_py_cookie +from juju.client.jujudata import API_ENDPOINTS_KEY, FileJujuData +from juju.client.proxy.factory import proxy_from_config +from juju.errors import JujuConnectionError, JujuError, JujuUnknownVersion +from juju.version import CLIENT_VERSION + +log = logging.getLogger("connector") + + +class NoConnectionException(Exception): + """Raised by Connector when the connection method is called + and there is no current connection. + """ + + pass + + +class Connector: + """Abstracts out a reconnectable client that can connect + to controllers and models found in the Juju data files. + """ + + def __init__( + self, + max_frame_size: int | None = None, + bakery_client: Any | None = None, + jujudata: Any | None = None, + ): + """Initialize a connector that will use the given parameters + by default when making a new connection + """ + self.max_frame_size = max_frame_size + self.bakery_client = bakery_client + self._connection = None + self._log_connection = None + self.controller_uuid = None + self.model_name = None + self.jujudata = jujudata or FileJujuData() + + def is_connected(self) -> bool: + """Report whether there is a currently connected controller or not""" + return self._connection is not None + + def connection(self) -> Connection: + """Return the current connection; raises an exception if there + is no current connection. + """ + if not self.is_connected(): + raise NoConnectionException("not connected") + assert self._connection + return self._connection + + async def connect(self, **kwargs): + """Connect to an arbitrary Juju model. + + kwargs are passed through to Connection.connect() + """ + kwargs.setdefault("max_frame_size", self.max_frame_size) + kwargs.setdefault("bakery_client", self.bakery_client) + if "macaroons" in kwargs: + if not kwargs["bakery_client"]: + kwargs["bakery_client"] = httpbakery.Client() + if not kwargs["bakery_client"].cookies: + kwargs["bakery_client"].cookies = GoCookieJar() + jar = kwargs["bakery_client"].cookies + for macaroon in kwargs.pop("macaroons"): + jar.set_cookie(go_to_py_cookie(macaroon)) + if "debug_log_conn" in kwargs: + assert self._connection + self._log_connection = await Connection.connect(**kwargs) + else: + # TODO (cderici): we need to investigate how to reuse/share + # connections between Model & Controller objects + # At least either avoid overwriting self._connection with + # different types of connections (model, controller), or + # have an indication of which type of entity this is + # connected to. + if self._connection: + await self._connection.close() + + account = kwargs.pop("account", {}) + # Prioritize the username and password that user provided + # If not enough, try to patch it with info from accounts.yaml + if "username" not in kwargs and account.get("user"): + kwargs.update(username=account.get("user")) + if "password" not in kwargs and account.get("password"): + kwargs.update(password=account.get("password")) + + if not ({"username", "password"}.issubset(kwargs)): + required = {"username", "password"}.difference(kwargs) + raise ValueError( + f"Some authentication parameters are required : {','.join(required)}" + ) + self._connection = await Connection.connect(**kwargs) + + # Check if we support the target controller + server_version = self._connection.info["server-version"] + try: + juju_server_version = version.parse(server_version) + except version.InvalidVersion as err: + # We're only interested in the major version, so + # we attempt to clean up versions such as 3.4-rc1.2 as just 3.4 + if "-" not in server_version: + raise JujuUnknownVersion(err) + juju_server_version = version.parse(server_version.split("-")[0]) + + # CLIENT_VERSION statically comes from the VERSION file in the repo + client_version = version.parse(CLIENT_VERSION) + + if juju_server_version.major != client_version.major: + raise JujuConnectionError( + "juju server-version %s not supported" % juju_server_version + ) + + async def disconnect(self, entity): + """Shut down the watcher task and close websockets.""" + if self._connection: + log.debug(f"Connector: closing {entity} connection") + await self._connection.close() + self._connection = None + if self._log_connection: + log.debug("Also closing debug-log connection") + await self._log_connection.close() + self._log_connection = None + + async def connect_controller( + self, controller_name=None, specified_facades=None, **kwargs + ): + """Connect to a controller by name. If the name is empty, it + connect to the current controller. + """ + if not controller_name: + controller_name = self.jujudata.current_controller() + + controller = self.jujudata.controllers()[controller_name] + endpoints = controller[API_ENDPOINTS_KEY] + accounts = self.jujudata.accounts().get(controller_name, {}) + + proxy = proxy_from_config(controller.get("proxy-config", None)) + + kwargs.update( + endpoint=endpoints, + uuid=None, + account=accounts, + cacert=controller.get("ca-cert"), + bakery_client=self.bakery_client_for_controller(controller_name), + specified_facades=specified_facades, + proxy=proxy, + ) + await self.connect(**kwargs) + self.controller_name = controller_name + self.controller_uuid = controller["uuid"] + + async def connect_model(self, _model_name=None, **kwargs): + """Connect to a model by name. If either controller or model + parts of the name are empty, the current controller and/or model + will be used. + + :param str model: : + """ + try: + controller_name, _model_name = self.jujudata.parse_model(_model_name) + controller = self.jujudata.controllers().get(controller_name) + except JujuError as e: + raise JujuConnectionError(e.message) from e + if controller is None: + raise JujuConnectionError(f"Controller {controller_name} not found") + endpoints = controller[API_ENDPOINTS_KEY] + account = self.jujudata.accounts().get(controller_name, {}) + models = self.jujudata.models().get(controller_name, {}).get("models", {}) + model_uuid = None + if _model_name in models: + model_uuid = models[_model_name]["uuid"] + else: + # let's try to find it through the actual controller + await self.connect_controller(controller_name=controller_name) + # get the facade + controller_facade = client.ControllerFacade.from_connection( + self.connection() + ) + # get all the user models from the api + response = await controller_facade.AllModels() + # search the one that contains admin/model_name + for user_model in response.user_models: + if "admin/" + user_model.model.name == _model_name: + model_uuid = user_model.model.uuid + + if model_uuid is None: + raise JujuConnectionError(f"Model not found: {_model_name}") + + proxy = proxy_from_config(controller.get("proxy-config", None)) + + # TODO remove the need for base.CleanModel to subclass + # JujuData. + kwargs.update( + endpoint=endpoints, + uuid=model_uuid, + account=account, + cacert=controller.get("ca-cert"), + bakery_client=self.bakery_client_for_controller(controller_name), + proxy=proxy, + ) + await self.connect(**kwargs) + # TODO this might be a good spot to trigger refreshing the + # local cache (the connection to the model might help) + self.model_name = controller_name + ":" + _model_name + return model_uuid + + def bakery_client_for_controller(self, controller_name): + """Make a copy of the bakery client with a the appropriate controller's + cookiejar in it. + """ + bakery_client = self.bakery_client + if bakery_client: + bakery_client = copy.copy(bakery_client) + else: + bakery_client = httpbakery.Client() + bakery_client.cookies = self.jujudata.cookies_for_controller(controller_name) + return bakery_client diff --git a/build/lib/juju/client/facade.py b/build/lib/juju/client/facade.py new file mode 100644 index 00000000..f0ee7513 --- /dev/null +++ b/build/lib/juju/client/facade.py @@ -0,0 +1,989 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +import argparse +import builtins +import functools +import json +import keyword +import pprint +import re +import textwrap +import typing +from collections import defaultdict +from glob import glob +from pathlib import Path +from typing import Any, Mapping, Sequence + +import packaging.version +import typing_inspect +from typing_extensions import TypeAlias + +from . import codegen + +# Plain JSON, what is received from Juju +_JsonLeaf: TypeAlias = "None | bool | int | float | str" +_Json: TypeAlias = "_JsonLeaf|list[_Json]|dict[str, _Json]" + +# Type-enriched JSON, what can be sent to Juju +_RichLeaf: TypeAlias = "_JsonLeaf|Type" +_RichJson: TypeAlias = "_RichLeaf|list[_RichJson]|dict[str, _RichJson]" + +_marker = object() + +JUJU_VERSION = re.compile(r"[0-9]+\.[0-9-]+[\.\-][0-9a-z]+(\.[0-9]+)?") +# Workaround for https://bugs.launchpad.net/juju/+bug/1683906 +NAUGHTY_CLASSES = ["ClientFacade", "Client", "ModelStatusInfo"] + + +# Map basic types to Python's typing with a callable +SCHEMA_TO_PYTHON = { + "string": str, + "integer": int, + "float": float, + "number": float, + "boolean": bool, + "object": Any, +} + + +# Friendly warning message to stick at the top of generated files. +HEADER = """\ +# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. +# Changes will be overwritten/lost when the file is regenerated. + +""" + + +# Classes and helper functions that we'll write to _client.py +LOOKUP_FACADE = ''' +def lookup_facade(name, version): + """ + Given a facade name and version, attempt to pull that facade out + of the correct client.py file. + + """ + for _version in range(int(version), 0, -1): + try: + facade = getattr(CLIENTS[str(_version)], name) + return facade + except (KeyError, AttributeError): + continue + else: + raise ImportError("No supported version for facade: " + "{}".format(name)) + +''' + +TYPE_FACTORY = ''' +class TypeFactory: + @classmethod + def from_connection(cls, connection): + """ + Given a connected Connection object, return an initialized and + connected instance of an API Interface matching the name of + this class. + + @param connection: initialized Connection object. + + """ + facade_name = cls.__name__ + if not facade_name.endswith('Facade'): + raise TypeError('Unexpected class name: {}'.format(facade_name)) + facade_name = facade_name[:-len('Facade')] + version = connection.facades.get(facade_name) + if version is None: + raise Exception('No facade {} in facades {}'.format(facade_name, + connection.facades)) + + c = lookup_facade(cls.__name__, version) + c = c() + c.connect(connection) + + return c + + @classmethod + def best_facade_version(cls, connection): + """ + Returns the best facade version for a given facade. This will help with + trying to provide different functionality for different facade versions. + + @param connection: initialized Connection object. + """ + facade_name = cls.__name__ + if not facade_name.endswith('Facade'): + raise TypeError('Unexpected class name: {}'.format(facade_name)) + facade_name = facade_name[:-len('Facade')] + return connection.facades.get(facade_name) + + +''' + +CLIENT_TABLE = """ +CLIENTS = {{ + {clients} +}} + +""" + + +class KindRegistry(dict): + def register(self, name, version, obj): + self[name] = { + version: { + "object": obj, + } + } + + def lookup(self, name, version=None): + """If version is omitted, max version is used""" + versions = self.get(name) + if not versions: + return None + if version: + return versions[version] + return versions[max(versions)] + + def get_obj(self, name, version=None): + result = self.lookup(name, version) + if result: + obj = result["object"] + return obj + return None + + +class TypeRegistry(dict): + def __init__(self, schema): + self.schema = schema + + def get(self, name): + # Two way mapping + refname = self.schema.reference_name(name) + if refname not in self: + result = typing.TypeVar(refname) + self[refname] = result + self[result] = refname + + return self[refname] + + def get_ref_type(self, ref): + return self.get(ref) + + def obj_type(self, obj): + kind = obj.get("type") + if not kind: + raise ValueError("%s has no type" % obj) + result = SCHEMA_TO_PYTHON.get(kind) + if not result: + raise ValueError("%s has type %s" % (obj, kind)) + return result + + def ref_type(self, obj): + return self.get_ref_type(obj["$ref"]) + + +CLASSES = {} +factories = codegen.Capture() + + +def booler(v): + if v == "false": + return False + return bool(v) + + +basic_types = [str, bool, int, float] + + +type_mapping = { + "str": "(bytes, str)", + "Sequence": "(bytes, str, list)", + "Union": "dict", + "Mapping": "dict", +} + + +def name_to_py(name): + result = name.replace("-", "_") + result = result.lower() + if keyword.iskeyword(result) or result in dir(builtins): + result += "_" + return result + + +def var_type_to_py(kind): + return "None" + + +def kind_to_py(kind): + if kind is None or kind is typing.Any: + return "None", "", False + + name = "" + if typing_inspect.is_generic_type(kind): + origin = typing_inspect.get_origin(kind) + name = origin.__name__ + else: + name = kind.__name__ + + if kind in basic_types or type(kind) in basic_types: + return name, type_mapping.get(name) or name, True + if name in type_mapping: + return name, type_mapping[name], True + + suffix = name.lstrip("~") + return suffix, f"(dict, {suffix})", True + + +def strcast(kind, keep_builtins=False): + if (kind in basic_types or type(kind) in basic_types) and keep_builtins is False: + return kind.__name__ + if str(kind).startswith("~"): + return str(kind)[1:] + if kind is typing.Any: + return "Any" + try: + if issubclass(kind, typing.GenericMeta): + return str(kind)[1:] + except AttributeError: + pass + return kind + + +class Args(list): + def __init__(self, schema, defs): + self.schema = schema + self.defs = defs + if defs: + rtypes = schema.registry.get_obj(schema.types[defs]) + if len(rtypes) == 1: + if not self.do_explode(rtypes[0][1]): + for name, rtype in rtypes: + self.append((name, rtype)) + else: + for name, rtype in rtypes: + self.append((name, rtype)) + + def do_explode(self, kind): + if kind is Any: + return False + if kind in basic_types or type(kind) is typing.TypeVar: + return False + if typing_inspect.is_generic_type(kind) and issubclass( + typing_inspect.get_origin(kind), Sequence + ): + return False + if typing_inspect.is_generic_type(kind) and issubclass( + typing_inspect.get_origin(kind), Mapping + ): + return False + self.clear() + self.extend(Args(self.schema, kind)) + return True + + def py_to_schema_mapping(self): + m = {} + for n, _ in self: + m[name_to_py(n)] = n + return m + + def schema_to_py_mapping(self): + m = {} + for n, _ in self: + m[n] = name_to_py(n) + return m + + def _format(self, name, rtype, typed=True): + if typed: + return f"{name_to_py(name)} : {strcast(rtype)}" + else: + return name_to_py(name) + + def _get_arg_str(self, typed=False, joined=", "): + if self: + parts = [self._format(item[0], item[1], typed) for item in self] + if joined: + return joined.join(parts) + return parts + return "" + + def as_kwargs(self): + if self: + parts = [] + for item in self: + var_name = name_to_py(item[0]) + var_type = var_type_to_py(item[1]) + parts.append(f"{var_name}={var_type}") + return ", ".join(parts) + return "" + + def as_validation(self): + """as_validation returns a series of validation statements for every item + in the the Args. + """ + parts = [] + for item in self: + var_name = name_to_py(item[0]) + var_type, var_sub_type, ok = kind_to_py(item[1]) + if ok: + parts.append(build_validation(var_name, var_type, var_sub_type)) + return "\n".join(parts) + + def typed(self): + return self._get_arg_str(True) + + def __str__(self): + return self._get_arg_str(False) + + def get_doc(self): + return self._get_arg_str(True, "\n") + + +def build_validation(name, instance_type, instance_sub_type, ident=None): + indent = ident or " " + source = f"""{indent}if {name} is not None and not isinstance({name}, {instance_sub_type}): +{indent} raise Exception("Expected {name} to be a {instance_type}, received: {{}}".format(type({name}))) +""" + return source + + +def build_types(schema, capture): + indent = " " + for kind in sorted( + (k for k in schema.types if not isinstance(k, str)), key=lambda x: str(x) + ): + name = schema.types[kind] + if not name: + # when running on juju 3.1.0 client-only schemas, we get a seemingly empty entry with no name + # this breaks codegen when generating a class with no name so we explicitly skip it here + # note that this is not a problem with the original 3.1.0 full schema (client + others) + # nor is it a problem with client-only schemas for latter juju versions (e.g. 3.3.0) + continue + if name in capture and name not in NAUGHTY_CLASSES: + continue + args = Args(schema, kind) + # Write Factory class for _client.py + make_factory(name) + # Write actual class + source = [ + """ +class {}(Type): + _toSchema = {} + _toPy = {} + def __init__(self{}{}, **unknown_fields): + ''' +{} + '''""".format( + name, + # pprint these to get stable ordering across regens + pprint.pformat(args.py_to_schema_mapping(), width=999), + pprint.pformat(args.schema_to_py_mapping(), width=999), + ", " if args else "", + args.as_kwargs(), + textwrap.indent(args.get_doc(), indent * 2), + ) + ] + + if not args: + source.append(f"{indent * 2}self.unknown_fields = unknown_fields") + else: + # do the validation first, before setting the variables + for arg in args: + arg_name = name_to_py(arg[0]) + arg_type = arg[1] + arg_type_name = strcast(arg_type) + if arg_type in basic_types or arg_type is typing.Any: + source.append(f"{indent * 2}{arg_name}_ = {arg_name}") + elif type(arg_type) is typing.TypeVar: + source.append( + f"{indent * 2}{arg_name}_ = {arg_type_name}.from_json({arg_name}) if {arg_name} else None" + ) + elif typing_inspect.is_generic_type(arg_type) and issubclass( + typing_inspect.get_origin(arg_type), Sequence + ): + parameters = typing_inspect.get_parameters(arg_type) + value_type = parameters[0] if len(parameters) else None + if type(value_type) is typing.TypeVar: + source.append( + f"{indent * 2}{arg_name}_ = [{strcast(value_type)}.from_json(o) for o in {arg_name} or []]" + ) + else: + source.append(f"{indent * 2}{arg_name}_ = {arg_name}") + elif typing_inspect.is_generic_type(arg_type) and issubclass( + typing_inspect.get_origin(arg_type), Mapping + ): + parameters = typing_inspect.get_parameters(arg_type) + value_type = parameters[0] if len(parameters) else None + if type(value_type) is typing.TypeVar: + source.append( + f"{indent * 2}{arg_name}_ = {{k: {strcast(value_type)}.from_json(v) " + f"for k, v in ({arg_name} or dict()).items()}}" + ) + else: + source.append(f"{indent * 2}{arg_name}_ = {arg_name}") + else: + source.append(f"{indent * 2}{arg_name}_ = {arg_name}") + if len(args) > 0: + source.append( + f"\n{indent * 2}# Validate arguments against known Juju API types." + ) + for arg in args: + arg_name = f"{name_to_py(arg[0])}_" + arg_type, arg_sub_type, ok = kind_to_py(arg[1]) + if ok: + source.append( + "{}".format( + build_validation( + arg_name, arg_type, arg_sub_type, ident=indent * 2 + ) + ) + ) + + for arg in args: + arg_name = name_to_py(arg[0]) + source.append(f"{indent * 2}self.{arg_name} = {arg_name}_") + # Ensure that we take the kwargs (unknown_fields) and put it on the + # Results/Params so we can inspect it. + source.append(f"{indent * 2}self.unknown_fields = unknown_fields") + + source = "\n".join(source) + capture.clear(name) + capture[name].write(source) + capture[name].write("\n\n") + if name is None: + print(source) + co = compile(source, __name__, "exec") + ns = _getns(schema) + exec(co, ns) # noqa: S102 + cls = ns[name] + CLASSES[name] = cls + + +def retspec(schema, defs): + # return specs + # only return 1, so if there is more than one type + # we need to include a union + # In truth there is only 1 return + # Error or the expected Type + if not defs: + return None + if defs in basic_types: + return strcast(defs, False) + return strcast(defs, False) + + +def ReturnMapping(cls): # noqa: N802 + # Annotate the method with a return Type + # so the value can be cast + def decorator(f): + @functools.wraps(f) + async def wrapper(*args, **kwargs): + nonlocal cls + reply = await f(*args, **kwargs) + if cls is None: + return reply + if "error" in reply: + cls = CLASSES["Error"] + if typing_inspect.is_generic_type(cls) and issubclass( + typing_inspect.get_origin(cls), Sequence + ): + parameters = typing_inspect.get_parameters(cls) + result = [] + item_cls = parameters[0] + for item in reply: + result.append(item_cls.from_json(item)) + """ + if 'error' in item: + cls = CLASSES['Error'] + else: + cls = item_cls + result.append(cls.from_json(item)) + """ + else: + result = cls.from_json(reply["response"]) + + return result + + return wrapper + + return decorator + + +def make_func(cls, name, description, params, result, _async=True): + indent = " " + args = Args(cls.schema, params) + toschema = args.py_to_schema_mapping() + assignments = [ + f"{indent}_params['{toschema[arg]}'] = {arg}" + for arg in args._get_arg_str(False, False) + ] + assignments = "\n".join(assignments) + res = retspec(cls.schema, result) + source = """ + +@ReturnMapping({rettype}) +{_async}def {name}(self{argsep}{args}): + ''' +{docstring} + Returns -> {res} + ''' +{validation} + # map input types to rpc msg + _params = dict() + msg = dict(type='{cls.name}', + request='{name}', + version={cls.version}, + params=_params) +{assignments} + reply = {_await}self.rpc(msg) + return reply + +""" + + if description != "": + description = f"{description}\n" + if args_doc := args.get_doc(): + args_doc = f"\n{args_doc}" + doc_string = f"{description}{args_doc}" + fsource = source.format( + _async="async " if _async else "", + name=name, + argsep=", " if args else "", + args=args.as_kwargs(), + res=res, + validation=args.as_validation(), + rettype=result.__name__ if result else None, + docstring=textwrap.indent(doc_string, indent), + cls=cls, + assignments=assignments, + _await="await " if _async else "", + ) + ns = _getns(cls.schema) + exec(fsource, ns) # noqa: S102 + func = ns[name] + return func, fsource + + +def make_rpc_func(cls): + source = """ + +async def rpc(self, msg): + ''' + Patch rpc method to add Id. + ''' + if not hasattr(self, 'Id'): + raise RuntimeError('Missing "Id" field') + msg['Id'] = id + + from .facade import TypeEncoder + reply = await self.connection.rpc(msg, encoder=TypeEncoder) + return reply + +""" + ns = _getns(cls.schema) + exec(source, ns) # noqa: S102 + func = ns["rpc"] + return func, source + + +def build_methods(cls, capture): + properties = cls.schema["properties"] + for methodname in sorted(properties): + method, source = _build_method(cls, methodname) + setattr(cls, methodname, method) + capture[f"{cls.__name__}Facade"].write(source, depth=1) + + +def _build_method(cls, name): + params = None + result = None + method = cls.schema["properties"][name] + description = "" + if "description" in method: + description = method["description"] + if "properties" in method: + prop = method["properties"] + spec = prop.get("Params") + if spec: + params = cls.schema.types.get(spec["$ref"]) + spec = prop.get("Result") + if spec: + if "$ref" in spec: + result = cls.schema.types.get(spec["$ref"]) + else: + result = SCHEMA_TO_PYTHON[spec["type"]] + return make_func(cls, name, description, params, result) + + +def build_watcher_methods(cls, capture): + properties = cls.schema["properties"] + if "Next" in properties and "Stop" in properties: + method, source = make_rpc_func(cls) + cls.rpc = method + capture[f"{cls.__name__}Facade"].write(source, depth=1) + + +def build_facade(schema): + cls = type( + schema.name, + (Type,), + dict(name=schema.name, version=schema.version, schema=schema), + ) + source = f""" +class {schema.name}Facade(Type): + name = '{schema.name}' + version = {schema.version} + """ + return cls, source + + +class TypeEncoder(json.JSONEncoder): + def default(self, obj: _RichJson) -> _Json: + if isinstance(obj, Type): + return obj.serialize() + return json.JSONEncoder.default(self, obj) + + +class Type: + def connect(self, connection): + self.connection = connection + + def __repr__(self): + return f"{self.__class__}({self.__dict__})" + + def __eq__(self, other): + if not isinstance(other, Type): + return NotImplemented + + return self.__dict__ == other.__dict__ + + async def rpc(self, msg: dict[str, _RichJson]) -> _Json: + result = await self.connection.rpc(msg, encoder=TypeEncoder) + return result + + @classmethod + def from_json(cls, data): + def _parse_nested_list_entry(expr, result_dict): + if isinstance(expr, str): + if ">" in expr or ">=" in expr: + # something like juju >= 2.9.31 + i = expr.index(">") + _key = expr[:i].strip() + _value = expr[i:].strip() + result_dict[_key] = _value + else: + # this is a simple entry + result_dict[expr] = "" + elif isinstance(expr, dict): + for v in expr.values(): + _parse_nested_list_entry(v, result_dict) + elif isinstance(expr, list): + for v in expr: + _parse_nested_list_entry(v, result_dict) + else: + raise TypeError( + f"Unexpected type of entry in assumes expression: {expr}" + ) + + if isinstance(data, cls): + return data + if isinstance(data, str): + try: + data = json.loads(data) + except json.JSONDecodeError: + raise + if isinstance(data, dict): + d = {} + for k, v in (data or {}).items(): + d[cls._toPy.get(k, k)] = v + try: + return cls(**d) + except TypeError: + raise + if isinstance(data, list): + # check: https://juju.is/docs/sdk/assumes + # assumes are in the form of a list + d = {} + _parse_nested_list_entry(data, d) + return cls(**d) + return None + + def serialize(self) -> dict[str, _Json]: + d = {} + for attr, tgt in self._toSchema.items(): + d[tgt] = getattr(self, attr) + return d + + def to_json(self) -> str: + return json.dumps(self.serialize(), cls=TypeEncoder, sort_keys=True) + + def __contains__(self, key): + return key in self._toPy + + # treat subscript gets as JSON representation + def __getitem__(self, key): + attr = self._toPy[key] + return getattr(self, attr) + + # treat subscript sets as JSON representation + def __setitem__(self, key, value): + attr = self._toPy[key] + setattr(self, attr, value) + + # legacy: generated definitions used to not correctly + # create typed objects and would use dict instead (from JSON) + # so we emulate some dict methods. + def get(self, key, default=None): + try: + attr = self._toPy[key] + except KeyError: + return default + return getattr(self, attr, default) + + +class Schema(dict): + def __init__(self, schema): + self.name = schema["Name"] + self.version = schema["Version"] + self.update(schema["Schema"]) + + self.registry = KindRegistry() + self.types = TypeRegistry(self) + + def reference_name(self, ref): + if ref.startswith("#/definitions/"): + ref = ref.rsplit("/", 1)[-1] + return ref + + def build_definitions(self): + # here we are building the types out + # anything in definitions is a type + # but these may contain references themselves + # so we dfs to the bottom and build upwards + # when a types is already in the registry + defs = self.get("definitions") + if not defs: + return + definitions = {} + for d, data in defs.items(): + if d in self.registry and d not in NAUGHTY_CLASSES: + continue + if data.get("type") != "object": + continue + definitions[d] = data + for d, definition in definitions.items(): + node = self.build_object(definition, d) + self.registry.register(d, self.version, node) + self.types.get_ref_type(d) + + def build_object(self, node, name=None): + # we don't need to build types recursively here + # they are all in definitions already + # we only want to include the type reference + # which we can derive from the name + struct = [] + add = struct.append + props = node.get("properties") + pprops = node.get("patternProperties") + if props: + # Sort these so the __init__ arg list for each Type remains + # consistently ordered across regens of client.py + for p in sorted(props): + prop = props[p] + if "$ref" in prop: + add((p, self.types.ref_type(prop))) + else: + kind = prop["type"] + if kind == "array": + add((p, self.build_array(prop))) + elif kind == "object": + struct.extend(self.build_object(prop, p)) + else: + add((p, self.types.obj_type(prop))) + if pprops: + if ".*" not in pprops: + raise ValueError( + "Cannot handle actual pattern in patternProperties %s" % pprops + ) + pprop = pprops[".*"] + if "$ref" in pprop: + add((name, Mapping[str, self.types.ref_type(pprop)])) + return struct + ppkind = pprop["type"] + if ppkind == "array": + add((name, Mapping[str, self.build_array(pprop)])) + else: + add((name, Mapping[str, SCHEMA_TO_PYTHON[ppkind]])) + + if not struct and node.get("additionalProperties", False): + add((name, SCHEMA_TO_PYTHON.get("object"))) + + return struct + + def build_array(self, obj): + # return a sequence from an array in the schema + if "$ref" in obj: + return Sequence[self.types.ref_type(obj)] + else: + kind = obj.get("type") + if kind and kind == "array": + items = obj["items"] + return self.build_array(items) + else: + return Sequence[self.types.obj_type(obj)] + + +def _getns(schema): + ns = {"Type": Type, "typing": typing, "ReturnMapping": ReturnMapping} + # Copy our types into the globals of the method + for facade in schema.registry: + ns[facade] = schema.registry.get_obj(facade) + return ns + + +def make_factory(name): + if name in factories: + del factories[name] + factories[name].write(f"class {name}(TypeFactory):\n pass\n\n") + + +def write_facades(captures, options): + """Write the Facades to the appropriate _client.py""" + for version in sorted(captures.keys()): + filename = f"{options.output_dir}/_client{version}.py" + with open(filename, "w") as f: + f.write(HEADER) + f.write("from juju.client.facade import Type, ReturnMapping\n") + f.write("from juju.client._definitions import *\n\n") + for key in sorted([k for k in captures[version] if "Facade" in k]): + print(captures[version][key], file=f) + + # Return the last (most recent) version for use in other routines. + return version + + +def write_definitions(captures, options): + """Write auxiliary (non versioned) classes to + _definitions.py The auxiliary classes currently get + written redudantly into each capture object, so we can look in + one of them -- we just use the last one from the loop above. + + """ + with open(f"{options.output_dir}/_definitions.py", "w") as f: + f.write(HEADER) + f.write("from juju.client.facade import Type, ReturnMapping\n\n") + for key in sorted([k for k in captures if "Facade" not in k]): + print(captures[key], file=f) + + +def write_client(captures, options): + """Write the TypeFactory classes to _client.py, along with some + imports and tables so that we can look up versioned Facades. + + """ + with open(f"{options.output_dir}/_client.py", "w") as f: + f.write(HEADER) + f.write("from juju.client._definitions import *\n\n") + clients = ", ".join(f"_client{v}" for v in captures) + + # from juju.client import _client2, _client1, _client3 ... + f.write("\nfrom juju.client import " + clients + "\n\n") + # CLIENTS = { .... + f.write( + CLIENT_TABLE.format( + clients=",\n ".join([f'"{v}": _client{v}' for v in captures]) + ) + ) + + f.write(LOOKUP_FACADE) + f.write(TYPE_FACTORY) + for key in sorted([k for k in factories if "Facade" in k]): + print(factories[key], file=f) + + +def generate_definitions(schemas): + # Build all of the auxiliary (unversioned) classes + # TODO: get rid of some of the excess trips through loops in the + # called functions. + definitions = codegen.Capture() + + for juju_version in sorted(schemas.keys()): + for schema in schemas[juju_version]: + schema.build_definitions() + + # ensure we write the latest ones first, so that earlier revisions + # get dropped. + for juju_version in sorted(schemas.keys(), reverse=True): + for schema in schemas[juju_version]: + build_types(schema, definitions) + + return definitions + + +def generate_facades( + schemas: dict[str, list[Schema]], +) -> dict[str, dict[int, codegen.Capture]]: + captures = defaultdict(codegen.Capture) + + # Build the Facade classes + for juju_version in sorted(schemas.keys(), key=packaging.version.parse): + for schema in schemas[juju_version]: + cls, source = build_facade(schema) + cls_name = f"{schema.name}Facade" + + captures[schema.version].clear(cls_name) + # Make the factory class for _client.py + make_factory(cls_name) + # Make the actual class + captures[schema.version][cls_name].write(source) + # Build the methods for each Facade class. + build_methods(cls, captures[schema.version]) + # Build the override RPC method if the Facade is a watcher. + build_watcher_methods(cls, captures[schema.version]) + # Mark this Facade class as being done for this version -- + # helps mitigate some excessive looping. + CLASSES[schema.name] = cls + + return captures + + +def load_schemas(options): + schemas = {} + for p in sorted(glob(options.schema)): + try: + juju_version = re.search(JUJU_VERSION, p).group() + except AttributeError: + print(f"Cannot extract a juju version from {p}") + print("Schemas must include a juju version in the filename") + raise SystemExit(1) + new_schemas = json.loads(Path(p).read_text("utf-8")) + schemas[juju_version] = [Schema(s) for s in new_schemas] + return schemas + + +def setup(): + parser = argparse.ArgumentParser() + parser.add_argument("-s", "--schema", default="juju/client/schemas-juju-*.json") + parser.add_argument("-o", "--output_dir", default="juju/client") + options = parser.parse_args() + return options + + +def main(): + options = setup() + + schemas = load_schemas(options) + + # Generate some text blobs + definitions = generate_definitions(schemas) + captures = generate_facades(schemas) + + # ... and write them out + write_definitions(definitions, options) + write_facades(captures, options) + write_client(captures, options) + + +if __name__ == "__main__": + main() diff --git a/build/lib/juju/client/facade_versions.py b/build/lib/juju/client/facade_versions.py new file mode 100644 index 00000000..a4f7cf29 --- /dev/null +++ b/build/lib/juju/client/facade_versions.py @@ -0,0 +1,139 @@ +# Copyright 2024 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +"""Constants for facade version negotiation.""" + +from typing import Dict, Sequence + +# Please keep in alphabetical order +# in future this will likely be generated automatically (perhaps at runtime) +client_facade_versions = { + "Action": (7,), + "Admin": (3,), + "AllModelWatcher": (4,), + "AllWatcher": (3,), + "Annotations": (2,), + "Application": (17, 19, 20), + "ApplicationOffers": (4, 5), + "Backups": (3,), + "Block": (2,), + "Bundle": (6,), + "Charms": (6,), + "Client": (6, 7, 8), + "Cloud": (7,), + "Controller": (11, 12), + "CredentialManager": (1,), + "FirewallRules": (1,), + "HighAvailability": (2,), + "ImageMetadataManager": (1,), + "KeyManager": (1,), + "MachineManager": (10,), + "MetricsDebug": (2,), + "ModelConfig": (3,), + "ModelGeneration": (4,), + "ModelManager": (9, 10), + "ModelUpgrader": (1,), + "Payloads": (1,), + "Pinger": (1,), + "Resources": (3,), + "SSHClient": (4,), + "SecretBackends": (1,), + "Secrets": (1, 2), + "Spaces": (6,), + "Storage": (6,), + "Subnets": (5,), + "UserManager": (3,), +} + +# Manual list of facades present in schemas + codegen which python-libjuju does not yet support +excluded_facade_versions: Dict[str, Sequence[int]] = {"Charms": (7,)} + + +# We don't generate code for these, as we can never use them. +# The controller happily lists them though, don't warn about these. +known_unsupported_facades = ( + "ActionPruner", + "Agent", + "AgentLifeFlag", + "AgentTools", + "ApplicationScaler", + "CAASAdmission", + "CAASAgent", + "CAASApplication", + "CAASApplicationProvisioner", + "CAASFirewaller", + "CAASFirewallerSidecar", + "CAASModelConfigManager", + "CAASModelOperator", + "CAASOperator", + "CAASOperatorProvisioner", + "CAASOperatorUpgrader", + "CAASUnitProvisioner", + "CharmDownloader", + "CharmRevisionUpdater", + "Cleaner", + "CredentialValidator", + "CrossController", + "CrossModelRelations", + "CrossModelSecrets", + "Deployer", + "DiskManager", + "EntityWatcher", + "EnvironUpgrader", + "ExternalControllerUpdater", + "FanConfigurer", + "FilesystemAttachmentsWatcher", + "Firewaller", + "HostKeyReporter", + "ImageMetadata", + "InstanceMutater", + "InstancePoller", + "KeyUpdater", + "LeadershipService", + "LifeFlag", + "LogForwarding", + "Logger", + "MachineActions", + "MachineUndertaker", + "Machiner", + "MeterStatus", + "MetricsAdder", + "MetricsManager", + "MigrationFlag", + "MigrationMaster", + "MigrationMinion", + "MigrationStatusWatcher", + "MigrationTarget", + "ModelSummaryWatcher", + "NotifyWatcher", + "OfferStatusWatcher", + "PayloadsHookContext", + "Provisioner", + "ProxyUpdater", + "Reboot", + "RelationStatusWatcher", + "RelationUnitsWatcher", + "RemoteRelationWatcher", + "RemoteRelations", + "ResourcesHookContext", + "RetryStrategy", + "SecretBackendsManager", + "SecretBackendsRotateWatcher", + "SecretsDrain", + "SecretsManager", + "SecretsRevisionWatcher", + "SecretsTriggerWatcher", + "Singular", + "StatusHistory", + "StorageProvisioner", + "StringsWatcher", + "Undertaker", + "UnitAssigner", + "Uniter", + "UpgradeSeries", + "UpgradeSteps", + "Upgrader", + "UserSecretsDrain", + "UserSecretsManager", + "VolumeAttachmentPlansWatcher", + "VolumeAttachmentsWatcher", +) diff --git a/build/lib/juju/client/flags.py b/build/lib/juju/client/flags.py new file mode 100644 index 00000000..fc678452 --- /dev/null +++ b/build/lib/juju/client/flags.py @@ -0,0 +1,17 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import os + +PYLIBJUJU_DEV_FEATURE_FLAG = "PYLIBJUJU_DEV_FEATURE_FLAGS" + + +DEFAULT_VALUES_FLAG = "default_values" + + +def feature_enabled(name): + flags = os.environ.get(PYLIBJUJU_DEV_FEATURE_FLAG) + if flags is not None: + parts = [s.strip() for s in flags.split(",")] + return name in parts + return False diff --git a/build/lib/juju/client/gocookies.py b/build/lib/juju/client/gocookies.py new file mode 100644 index 00000000..0c33f0be --- /dev/null +++ b/build/lib/juju/client/gocookies.py @@ -0,0 +1,108 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import datetime +import http.cookiejar as cookiejar +import json +import time + +import pyrfc3339 + + +class GoCookieJar(cookiejar.FileCookieJar): + """A CookieJar implementation that reads and writes cookies + to the cookiejar format as understood by the Go package + github.com/juju/persistent-cookiejar. + """ + + def _really_load(self, f, filename, ignore_discard, ignore_expires): + """Implement the _really_load method called by FileCookieJar + to implement the actual cookie loading + """ + data = json.load(f) or [] + now = time.time() + for cookie in map(go_to_py_cookie, data): + if not ignore_expires and cookie.is_expired(now): + continue + self.set_cookie(cookie) + + def save(self, filename=None, ignore_discard=False, ignore_expires=False): + """Implement the FileCookieJar abstract method.""" + if filename is None: + if self.filename is not None: + filename = self.filename + else: + raise ValueError(cookiejar.MISSING_FILENAME_TEXT) + + # TODO: obtain file lock, read contents of file, and merge with + # current content. + go_cookies = [] + now = time.time() + for cookie in self: + if not ignore_discard and cookie.discard: + continue + if not ignore_expires and cookie.is_expired(now): + continue + go_cookies.append(py_to_go_cookie(cookie)) + with open(filename, "w") as f: + f.write(json.dumps(go_cookies)) + + +def go_to_py_cookie(go_cookie): + """Convert a Go-style JSON-unmarshaled cookie into a Python cookie""" + expires = None + if go_cookie.get("Expires") is not None: + t = pyrfc3339.parse(go_cookie["Expires"]) + expires = t.timestamp() + return cookiejar.Cookie( + version=0, + name=go_cookie["Name"], + value=go_cookie["Value"], + port=None, + port_specified=False, + # Unfortunately Python cookies don't record the original + # host that the cookie came from, so we'll just use Domain + # for that purpose, and record that the domain was specified, + # even though it probably was not. This means that + # we won't correctly record the CanonicalHost entry + # when writing the cookie file after reading it. + domain=go_cookie["Domain"], + domain_specified=not go_cookie["HostOnly"], + domain_initial_dot=False, + path=go_cookie["Path"], + path_specified=True, + secure=go_cookie["Secure"], + expires=expires, + discard=False, + comment=None, + comment_url=None, + rest=None, + rfc2109=False, + ) + + +def py_to_go_cookie(py_cookie): + """Convert a python cookie to the JSON-marshalable Go-style cookie form.""" + # TODO (perhaps): + # HttpOnly + # Creation + # LastAccess + # Updated + # not done properly: CanonicalHost. + go_cookie = { + "Name": py_cookie.name, + "Value": py_cookie.value, + "Domain": py_cookie.domain, + "HostOnly": not py_cookie.domain_specified, + "Persistent": not py_cookie.discard, + "Secure": py_cookie.secure, + "CanonicalHost": py_cookie.domain, + } + if py_cookie.path_specified: + go_cookie["Path"] = py_cookie.path + if py_cookie.expires is not None: + unix_time = datetime.datetime.fromtimestamp(py_cookie.expires) + # Note: fromtimestamp bizarrely produces a time without + # a time zone, so we need to use accept_naive. + go_cookie["Expires"] = pyrfc3339.generate(unix_time, accept_naive=True) + return go_cookie diff --git a/build/lib/juju/client/jujudata.py b/build/lib/juju/client/jujudata.py new file mode 100644 index 00000000..5742985b --- /dev/null +++ b/build/lib/juju/client/jujudata.py @@ -0,0 +1,190 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import abc +import os +import pathlib + +import yaml + +from juju import tag +from juju.client import client as jujuclient +from juju.client.gocookies import GoCookieJar +from juju.errors import ( + JujuControllerNotFoundError, + JujuError, + PylibjujuProgrammingError, +) +from juju.utils import juju_config_dir + +API_ENDPOINTS_KEY = "api-endpoints" + + +class NoModelException(Exception): + pass + + +class JujuData: + __metaclass__ = abc.ABCMeta + + def parse_model(self, model): + """Split the given model_name into controller and model parts. + If the controller part is empty, the current controller will be used. + If the model part is empty, the current model will be used for + the controller. + The returned model name will always be qualified with a username. + :param model str: The model name to parse. + :return (str, str): The controller and model names. + """ + # TODO if model is empty, use $JUJU_MODEL environment variable. + if model and ":" in model: + # explicit controller given + controller_name, model_name = model.split(":") + else: + # use the current controller if one isn't explicitly given + controller_name = self.current_controller() + model_name = model + if not controller_name: + controller_name = self.current_controller() + if not model_name: + model_name = self.current_model(controller_name, model_only=True) + if not model_name: + raise NoModelException("no current model") + + if "/" not in model_name: + # model name doesn't include a user prefix, so add one + # by using the current user for the controller. + accounts = self.accounts().get(controller_name) + if accounts is None: + raise JujuError(f"No account found for controller {controller_name} ") + username = accounts.get("user") + if username is None: + raise JujuError(f"No username found for controller {controller_name}") + model_name = username + "/" + model_name + + return controller_name, model_name + + +class FileJujuData(JujuData): + """Provide access to the Juju client configuration files. + Any configuration file is read once and then cached. + """ + + def __init__(self): + self.path = juju_config_dir() + # _loaded keeps track of the loaded YAML from + # the Juju data files so we don't need to load the same + # file many times. + self._loaded = {} + + def refresh(self): + """Forget the cache of configuration file data""" + self._loaded = {} + + def current_controller(self): + """Return the current controller name""" + try: + return self._load_yaml("controllers.yaml", "current-controller") + except FileNotFoundError: + raise JujuControllerNotFoundError( + "No controllers.yaml file found. python-libjuju requires a bootstrapped Juju controller." + ) + + def current_model(self, controller_name=None, model_only=False): + """Return the current model, qualified by its controller name. + If controller_name is specified, the current model for + that controller will be returned. + + If model_only is true, only the model name, not qualified by + its controller name, will be returned. + """ + # TODO respect JUJU_MODEL environment variable. + if not controller_name: + controller_name = self.current_controller() + if not controller_name: + raise JujuError("No current controller") + models = self.models()[controller_name] + if "current-model" not in models: + return None + if model_only: + return models["current-model"] + return controller_name + ":" + models["current-model"] + + def load_credential(self, cloud, name=None): + """Load a local credential. + + :param str cloud: Name of cloud to load credentials from. + :param str name: Name of credential. If None, the default credential + will be used, if available. + :return: A CloudCredential instance, or None. + """ + try: + cloud = tag.untag("cloud-", cloud) + creds_data = self.credentials()[cloud] + if not name: + default_credential = creds_data.pop("default-credential", None) + default_region = creds_data.pop("default-region", None) # noqa + if default_credential: + name = creds_data["default-credential"] + elif len(creds_data) == 1: + name = next(iter(creds_data)) + else: + return None, None + cred_data = creds_data[name] + auth_type = cred_data.pop("auth-type") + return name, jujuclient.CloudCredential( + auth_type=auth_type, + attrs=cred_data, + ) + except (KeyError, FileNotFoundError): + return None, None + + def controller_name_by_endpoint(self, endpoint): + """Finds the controller that has the given endpoints, returns the name. + + :param str endpoint: The endpoint of the controller we're looking for + """ + for controller_name, controller in self.controllers().items(): + if isinstance(endpoint, str): + if endpoint in controller[API_ENDPOINTS_KEY]: + return controller_name + elif isinstance(endpoint, list): + for e in endpoint: + if e in controller[API_ENDPOINTS_KEY]: + return controller_name + else: + raise PylibjujuProgrammingError() + raise JujuError(f"Unable to find controller with endpoint {endpoint}") + + def controllers(self): + return self._load_yaml("controllers.yaml", "controllers") + + def models(self): + return self._load_yaml("models.yaml", "controllers") + + def accounts(self): + return self._load_yaml("accounts.yaml", "controllers") + + def credentials(self): + return self._load_yaml("credentials.yaml", "credentials") + + def _load_yaml(self, filename, key): + if filename in self._loaded: + # Data already exists in the cache. + return self._loaded[filename].get(key) + # TODO use the file lock like Juju does. + filepath = os.path.join(self.path, filename) + with open(filepath) as f: + data = yaml.safe_load(f) + self._loaded[filename] = data + return data.get(key) + + def cookies_for_controller(self, controller_name): + f = pathlib.Path(self.path) / "cookies" / (controller_name + ".json") + if not f.exists(): + f = pathlib.Path("~/.go-cookies").expanduser() + # TODO if neither cookie file exists, where should + # we create the cookies? + jar = GoCookieJar(str(f)) + jar.load() + return jar diff --git a/build/lib/juju/client/overrides.py b/build/lib/juju/client/overrides.py new file mode 100644 index 00000000..1b569255 --- /dev/null +++ b/build/lib/juju/client/overrides.py @@ -0,0 +1,418 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +import re +from typing import Any, NamedTuple + +from . import _client, _definitions +from .facade import ReturnMapping, Type, TypeEncoder + +__all__ = [ + "Binary", + "ConfigValue", + "Delta", + "Number", + "Resource", +] + +__patches__ = [ + "ResourcesFacade", + "AllWatcherFacade", + "ActionFacade", +] + + +class _Change(NamedTuple): + entity: str + type: str + data: dict[str, Any] + + +class Delta(Type): + """A single websocket delta. + + :ivar entity: The entity name, e.g. 'unit', 'application' + :vartype entity: str + + :ivar type: The delta type, e.g. 'add', 'change', 'remove' + :vartype type: str + + :ivar data: The raw delta data + :vartype data: dict + + NOTE: The 'data' variable above is being incorrectly cross-linked by a + Sphinx bug: https://github.com/sphinx-doc/sphinx/issues/2549 + + """ + + _toSchema = {"deltas": "deltas"} + _toPy = {"deltas": "deltas"} + + def __init__(self, deltas: tuple[str, str, dict[str, Any]]): + """:param deltas: [str, str, object]""" + self.deltas = deltas + + change = _Change(*self.deltas) + + self.entity = change.entity + self.type = change.type + self.data = change.data + + @classmethod + def from_json(cls, data): + return cls(deltas=data) + + +class ResourcesFacade(Type): + """Patch parts of ResourcesFacade to make it work.""" + + # FIXME: a facade method from codegen can be used instead + @ReturnMapping(_client.AddPendingResourcesResult) + async def AddPendingResources( # noqa: N802 + self, application_tag="", charm_url="", charm_origin=None, resources=None + ): + """Fix the calling signature of AddPendingResources. + + The ResourcesFacade doesn't conform to the standard facade pattern in + the Juju source, which leads to the schemagened code not matching up + properly with the actual calling convention in the API. There is work + planned to fix this in Juju, but we have to work around it for now. + + application_tag : str + charm_url : str + resources : typing.Sequence<+T_co>[~CharmResource]<~CharmResource> + Returns -> typing.Union[_ForwardRef('ErrorResult'), + typing.Sequence<+T_co>[str]] + """ + version = _client.ResourcesFacade.best_facade_version(self.connection) + # map input types to rpc msg + _params = dict() + msg = dict( + type="Resources", + request="AddPendingResources", + version=version, + params=_params, + ) + _params["tag"] = application_tag + _params["url"] = charm_url + _params["resources"] = resources + _params["charm-origin"] = charm_origin + reply = await self.rpc(msg) + return reply + + +class AllWatcherFacade(Type): + """Patch rpc method of allwatcher to add in 'id' stuff.""" + + async def rpc(self, msg): + if not hasattr(self, "Id"): + client = _client.ClientFacade.from_connection(self.connection) + + result = await client.WatchAll() + self.Id = result.watcher_id + + msg["Id"] = self.Id + result = await self.connection.rpc(msg, encoder=TypeEncoder) + return result + + +class ActionFacade(Type): + class _FindTagsResults(Type): + _toSchema = {"matches": "matches"} + _toPy = {"matches": "matches"} + + def __init__(self, matches=None, **unknown_fields): + """FindTagsResults wraps the mapping between the requested prefix and the + matching tags for each requested prefix. + + Matches map[string][]Entity `json:"matches"` + """ + self.matches = {} + matches = matches or {} + for prefix, tags in matches.items(): + self.matches[prefix] = [_definitions.Entity.from_json(r) for r in tags] + + # FIXME: This seems internal, can be renamed + @ReturnMapping(_FindTagsResults) + async def FindActionTagsByPrefix(self, prefixes): # noqa: N802 + """Prefixes : typing.Sequence[str] + Returns -> typing.Sequence[~Entity] + """ + # map input types to rpc msg + _params = dict() + msg = dict( + type="Action", request="FindActionTagsByPrefix", version=2, params=_params + ) + _params["prefixes"] = prefixes + reply = await self.rpc(msg) + return reply + + +class Number(_definitions.Number): + """Represent a semver string. + + Because it is not standard JSON, the typical from_json parsing fails and + the parsing must be handled specially. + + See https://github.com/juju/version for more info. + """ + + numberPat = re.compile( + r"^(\d{1,9})\.(\d{1,9})(?:\.|-([a-z]+))(\d{1,9})(\.\d{1,9})?$" + ) + + def __init__( + self, major=None, minor=None, patch=None, tag=None, build=None, **unknown_fields + ): + """Major : int + minor : int + patch : int + tag : str + build : int + """ + self.major = int(major or "0") + self.minor = int(minor or "0") + self.patch = int(patch or "0") + self.tag = tag or "" + self.build = int(build or "0") + + def __repr__(self): + return f"" + + def __str__(self): + return self.serialize() + + @property + def _cmp(self): + return (self.major, self.minor, self.tag, self.patch, self.build) + + def __eq__(self, other): + return isinstance(other, type(self)) and self._cmp == other._cmp + + def __lt__(self, other): + return self._cmp < other._cmp + + def __le__(self, other): + return self._cmp <= other._cmp + + def __gt__(self, other): + return self._cmp > other._cmp + + def __ge__(self, other): + return self._cmp >= other._cmp + + @classmethod + def from_json(cls, data): + parsed = None + if isinstance(data, cls): + return data + elif data is None: + return cls() + elif isinstance(data, dict): + parsed = data + elif isinstance(data, str): + match = cls.numberPat.match(data) + if match: + parsed = { + "major": match.group(1), + "minor": match.group(2), + "tag": match.group(3), + "patch": match.group(4), + "build": (match.group(5)[1:] if match.group(5) else 0), + } + if not parsed: + raise TypeError(f"Unable to parse Number version string: {data}") + d = {} + for k, v in parsed.items(): + d[cls._toPy.get(k, k)] = v + + return cls(**d) + + def serialize(self): + s = "" + if not self.tag: + s = f"{self.major}.{self.minor}.{self.patch}" + else: + s = f"{self.major}.{self.minor}-{self.tag}{self.patch}" + if self.build: + s = f"{s}.{self.build}" + return s + + def to_json(self): + return self.serialize() + + +class Binary(_definitions.Binary): + """Represent a semver string with additional series and arch info. + + Because it is not standard JSON, the typical from_json parsing fails and + the parsing must be handled specially. + + See https://github.com/juju/version for more info. + """ + + binaryPat = re.compile( + r"^(\d{1,9})\.(\d{1,9})(?:\.|-([a-z]+))(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$" + ) + + def __init__(self, number=None, series=None, arch=None, **unknown_fields): + """Number : Number + series : str + arch : str + """ + self.number = Number.from_json(number) + self.series = series + self.arch = arch + + def __repr__(self): + return f"" + + def __str__(self): + return self.serialize() + + def __eq__(self, other): + return ( + isinstance(other, type(self)) + and other.number == self.number + and other.series == self.series + and other.arch == self.arch + ) + + @classmethod + def from_json(cls, data): + parsed = None + if isinstance(data, cls): + return data + elif data is None: + return cls() + elif isinstance(data, dict): + parsed = data + elif isinstance(data, str): + match = cls.binaryPat.match(data) + if match: + parsed = { + "number": { + "major": match.group(1), + "minor": match.group(2), + "tag": match.group(3), + "patch": match.group(4), + "build": (match.group(5)[1:] if match.group(5) else 0), + }, + "series": match.group(6), + "arch": match.group(7), + } + if parsed is None: + raise TypeError(f"Unable to parse Binary version string: {data}") + d = {} + for k, v in parsed.items(): + d[cls._toPy.get(k, k)] = v + + return cls(**d) + + def serialize(self): + return f"{self.number.serialize()}-{self.series}-{self.arch}" + + def to_json(self): + return self.serialize() + + +class ConfigValue(_definitions.ConfigValue): + def __repr__(self): + return f"<{type(self).__name__} source={self.source!r} value={self.value!r}>" + + +class Resource(Type): + _toSchema = { + "application": "application", + "charmresource": "CharmResource", + "id_": "id", + "pending_id": "pending-id", + "timestamp": "timestamp", + "username": "username", + "name": "name", + "origin": "origin", + } + _toPy = { + "CharmResource": "charmresource", + "application": "application", + "id": "id_", + "pending-id": "pending_id", + "timestamp": "timestamp", + "username": "username", + "name": "name", + "origin": "origin", + } + + def __init__( + self, + charmresource=None, + application=None, + id_=None, + pending_id=None, + timestamp=None, + username=None, + name=None, + origin=None, + **unknown_fields, + ): + """Charmresource : CharmResource + application : str + id_ : str + pending_id : str + timestamp : str + username : str + name: str + origin : str + """ + if charmresource: + self.charmresource = _client.CharmResource.from_json(charmresource) + else: + self.charmresource = None + self.application = application + self.id_ = id_ + self.pending_id = pending_id + self.timestamp = timestamp + self.username = username + self.name = name + self.origin = origin + self.unknown_fields = unknown_fields + + +class Macaroon(Type): + _toSchema = { + "signature": "signature", + "caveats": "caveats", + "location": "location", + "identifier": "identifier", + } + _toPy = { + "signature": "signature", + "caveats": "caveats", + "location": "location", + "identifier": "identifier", + } + + def __init__( + self, signature="", caveats=None, location=None, identifier="", **unknown_fields + ): + """Signature : str + caveats : typing.Sequence<+T_co>[~RemoteSpace]<~RemoteSpace> + location : str + identifier : str + """ + self.signature = signature + self.caveats = caveats + self.location = location + self.identifier = identifier + self.unknown_fields = unknown_fields + + +class Caveat(Type): + _toSchema = {"cid": "cid"} + _toPy = {"cid": "cid"} + + def __init__(self, cid="", **unknown_fields): + """Cid : str""" + self.cid = cid + self.unknown_fields = unknown_fields diff --git a/build/lib/juju/client/proxy/__init__.py b/build/lib/juju/client/proxy/__init__.py new file mode 100644 index 00000000..f59d24d5 --- /dev/null +++ b/build/lib/juju/client/proxy/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2024 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +"""Juju client proxy.""" diff --git a/build/lib/juju/client/proxy/factory.py b/build/lib/juju/client/proxy/factory.py new file mode 100644 index 00000000..24f76081 --- /dev/null +++ b/build/lib/juju/client/proxy/factory.py @@ -0,0 +1,29 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +from juju.client.proxy.kubernetes.proxy import KubernetesProxy + + +def proxy_from_config(conf): + if conf is None: + return None + + if "type" not in conf: + return None + + proxy_type = conf["type"] + if proxy_type != "kubernetes-port-forward": + raise ValueError("unknown proxy type %s" % proxy_type) + + return _construct_kube_proxy(conf["config"]) + + +def _construct_kube_proxy(config): + return KubernetesProxy( + config.get("api-host", ""), + config.get("namespace", ""), + config.get("remote-port", ""), + config.get("service", ""), + config.get("service-account-token", ""), + config.get("ca-cert", None), + ) diff --git a/build/lib/juju/client/proxy/kubernetes/__init__.py b/build/lib/juju/client/proxy/kubernetes/__init__.py new file mode 100644 index 00000000..0b3faa2d --- /dev/null +++ b/build/lib/juju/client/proxy/kubernetes/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2024 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +"""Juju client proxy for k8s.""" diff --git a/build/lib/juju/client/proxy/kubernetes/proxy.py b/build/lib/juju/client/proxy/kubernetes/proxy.py new file mode 100644 index 00000000..3d4153b1 --- /dev/null +++ b/build/lib/juju/client/proxy/kubernetes/proxy.py @@ -0,0 +1,83 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +import logging +import tempfile + +from kubernetes import client +from kubernetes.stream import portforward + +from juju.client.proxy.proxy import Proxy, ProxyNotConnectedError + +log = logging.getLogger("juju.client.connection") + + +class KubernetesProxy(Proxy): + def __init__( + self, + api_host, + namespace, + remote_port, + service, + service_account_token, + ca_cert=None, + ): + config = client.Configuration() + config.host = api_host + config.ssl_ca_cert = ca_cert + config.api_key = {"authorization": "Bearer " + service_account_token} + + self.namespace = namespace + self.remote_port = remote_port + self.service = service + + try: + self.remote_port = int(remote_port) + except ValueError: + raise ValueError(f"Invalid port number: {remote_port}") + + self.port_forwarder = None + + if ca_cert: + self.temp_ca_file = tempfile.NamedTemporaryFile() # noqa: SIM115 + self.temp_ca_file.write(bytes(ca_cert, "utf-8")) + self.temp_ca_file.flush() + config.ssl_ca_cert = self.temp_ca_file.name + else: + self.temp_ca_file = None + + self.api_client = client.ApiClient(config) + + def connect(self): + corev1 = client.CoreV1Api(self.api_client) + service = corev1.read_namespaced_service(self.service, self.namespace) + + label_selector = ",".join(k + "=" + v for k, v in service.spec.selector.items()) + + pods = corev1.list_namespaced_pod( + namespace=self.namespace, + label_selector=label_selector, + ) + + self.port_forwarder = portforward( + corev1.connect_get_namespaced_pod_portforward, + pods.items[0].metadata.name, + self.namespace, + ports=str(self.remote_port), + ) + + def __del__(self): + self.close() + + def close(self): + try: + if self.port_forwarder: + self.port_forwarder.close() + if self.temp_ca_file: + self.temp_ca_file.close() + except AttributeError: + pass + + def socket(self): + if self.port_forwarder is not None: + return self.port_forwarder.socket(self.remote_port)._socket + raise ProxyNotConnectedError() diff --git a/build/lib/juju/client/proxy/proxy.py b/build/lib/juju/client/proxy/proxy.py new file mode 100644 index 00000000..3a521e10 --- /dev/null +++ b/build/lib/juju/client/proxy/proxy.py @@ -0,0 +1,26 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +from abc import abstractmethod + +from juju.errors import AbstractMethodError + + +class ProxyNotConnectedError(Exception): + pass + + +class Proxy: + """Abstract class to represent a generic controller connection proxy""" + + @abstractmethod + def connect(self): + raise AbstractMethodError() + + @abstractmethod + def close(self): + raise AbstractMethodError() + + @abstractmethod + def socket(self): + raise AbstractMethodError() diff --git a/build/lib/juju/client/runner.py b/build/lib/juju/client/runner.py new file mode 100644 index 00000000..87518b2c --- /dev/null +++ b/build/lib/juju/client/runner.py @@ -0,0 +1,25 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + + +class AsyncRunner: + async def __call__(self, facade_method, *args, **kwargs): + await self.connection.rpc(facade_method(*args, **kwargs)) + + +class ThreadedRunner: + pass + + +# Methods are descriptors?? +# get is called with params +# set gets called with the result? +# This could let us fake the protocol we want +# while decoupling the protocol from the RPC and the IO/Process context + +# The problem is leaking the runtime impl details to the top levels of the API +# with async def By handling the Marshal/Unmarshal side of RPC as a protocol we +# can leave the RPC running to a specific delegate without altering the method +# signatures. This still isn't quite right though as async is co-op +# multitasking and the methods still need to know not to block or they will +# pause other execution diff --git a/build/lib/juju/constraints.py b/build/lib/juju/constraints.py new file mode 100644 index 00000000..246bd4c7 --- /dev/null +++ b/build/lib/juju/constraints.py @@ -0,0 +1,254 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +# +# Module that parses constraints +# +# The current version of juju core expects the client to take +# constraints given in the form "mem=10G foo=bar" and parse them into +# json that looks like {"mem": 10240, "foo": "bar"}. This module helps us +# accomplish that task. +# +# We do not attempt to duplicate the checking done in +# client/_client.py:Value here. That class will verify that the +# constraints keys are valid, and that we can successfully dump the +# constraints dict to json. +# +# Once https://bugs.launchpad.net/juju/+bug/1645402 is addressed, this +# module should be deprecated. +# + +import re +from typing import Dict, List, Mapping, Optional, TypedDict, Union + +from typing_extensions import NotRequired, Required + +# Matches on a string specifying memory size +MEM = re.compile("^[1-9][0-9]*[MGTP]$") + +# Multiplication factors to get Megabytes +# https://github.com/juju/juju/blob/master/constraints/constraints.go#L666 +FACTORS = { + "M": 1024**0, + "G": 1024**1, + "T": 1024**2, + "P": 1024**3, + "E": 1024**4, + "Z": 1024**5, + "Y": 1024**6, +} + +# List of supported constraint keys, see +# http://github.com/cderici/juju/blob/2.9/core/constraints/constraints.go#L20-L39 +SUPPORTED_KEYS = [ + "arch", + "container", + "cpu_cores", + "cores", + "cpu_power", + "mem", + "root_disk", + "root_disk_source", + "tags", + "instance_role", + "instance_type", + "spaces", + "virt_type", + "zones", + "allocate_public_ip", +] + +LIST_KEYS = {"tags", "spaces", "zones"} + +SNAKE1 = re.compile(r"(.)([A-Z][a-z]+)") +SNAKE2 = re.compile("([a-z0-9])([A-Z])") + + +ParsedValue = Union[int, bool, str] + + +class ConstraintsDict(TypedDict, total=False): + allocate_public_ip: ParsedValue + arch: ParsedValue + container: ParsedValue + cores: ParsedValue + cpu_cores: ParsedValue + cpu_power: ParsedValue + instance_role: ParsedValue + instance_type: ParsedValue + mem: ParsedValue + root_disk: ParsedValue + root_dist_source: ParsedValue + spaces: List[ParsedValue] + tags: List[ParsedValue] + virt_type: ParsedValue + zones: List[ParsedValue] + + +def parse(constraints: Union[str, ConstraintsDict]) -> Optional[ConstraintsDict]: + """Constraints must be expressed as a string containing only spaces + and key value pairs joined by an '='. + + """ + if not constraints: + return None + + if isinstance(constraints, dict): + # Forwards compatibility: already parsed + return constraints + + normalized_constraints: ConstraintsDict = {} + for s in constraints.split(" "): + if "=" not in s: + raise ValueError("malformed constraint %s" % s) + + k, v = s.split("=") + normalized_constraints[normalize_key(k)] = ( + normalize_list_value(v) if k in LIST_KEYS else normalize_value(v) + ) + + return normalized_constraints + + +def normalize_key(orig_key: str) -> str: + key = orig_key.strip() + + key = key.replace("-", "_") # Our _client lib wants "_" in place of "-" + + # Convert camelCase to snake_case + key = SNAKE1.sub(r"\1_\2", key) + key = SNAKE2.sub(r"\1_\2", key).lower() + + if key not in SUPPORTED_KEYS: + raise ValueError("unknown constraint in %s" % orig_key) + return key + + +def normalize_value(value: str) -> Union[int, bool, str]: + value = value.strip() + + if MEM.match(value): + # Translate aliases to Megabytes. e.g. 1G = 10240 + return int(value[:-1]) * FACTORS[value[-1:]] + + if value.isdigit(): + return int(value) + + if value.lower() == "true": + return True + if value.lower() == "false": + return False + + return value + + +def normalize_list_value(value: str) -> List[ParsedValue]: + values = value.strip().split(",") + return [normalize_value(value) for value in values] + + +STORAGE = re.compile( + # original regex: + # '(?:(?:^|(?<=,))(?:|(?P[a-zA-Z]+[-?a-zA-Z0-9]*)|(?P-?[0-9]+)|(?:(?P-?[0-9]+(?:\\.[0-9]+)?)(?P[MGTPEZY])(?:i?B)?))(?:$|,))' + # with formatting and explanation -- note that this regex is used with re.finditer: + "(?:" + "(?:^|(?<=,))" # start of string or previous match ends with ',' + "(?:" # match one of the following: + "|(?P[a-zA-Z]+[-?a-zA-Z0-9]*)" # * pool: a sequence starting with a letter, ending with a letter or number, + # ------- and including letters, numbers and hyphens (no more than one in a row) + "|(?P-?[0-9]+)" # * count: an optional minus sign followed by one or more digits + "|(?:" # * size (number) and size_exp (units): + "(?P-?[0-9]+(?:\\.[0-9]+)?)" # -- * an optional minus sign followed by one or more digits, optionally with decimal point and more digits + "(?P[MGTPEZY])(?:i?B)?)" # -- * one of MGTPEZY, optionally followed by iB or B, for example 1M or 2.0MB or -3.3MiB + ")" + "(?:$|,)" # end of string or ',' + ")" +) + + +class StorageConstraintDict(TypedDict): + count: Required[int] # >= 1 + pool: NotRequired[str] + size: NotRequired[int] + + +def parse_storage_constraint(constraint: str) -> StorageConstraintDict: + storage: StorageConstraintDict = {"count": 1} + for m in STORAGE.finditer(constraint): + pool = m.group("pool") + if pool: + if "pool" in storage: + raise ValueError("pool already specified") + storage["pool"] = pool + count = m.group("count") + if count: + count = int(count) + storage["count"] = count if count > 0 else 1 + size = m.group("size") + if size: + storage["size"] = int(float(size) * FACTORS[m.group("size_exp")]) + return storage + + +def parse_storage_constraints( + constraints: Optional[Mapping[str, Union[str, StorageConstraintDict]]] = None, +) -> Dict[str, StorageConstraintDict]: + if constraints is None: + return {} + parsed: dict[str, StorageConstraintDict] = {} + for label, storage_constraint in constraints.items(): + if isinstance(storage_constraint, str): + parsed[label] = parse_storage_constraint(storage_constraint) + elif isinstance(storage_constraint, dict): # pyright: ignore[reportUnnecessaryIsInstance] + parsed[label] = storage_constraint + else: + raise ValueError( + f"Unexpected constraint {storage_constraint!r}" + f" for label {label!r} in {constraints}" + ) + return parsed + + +DEVICE = re.compile( + # original regex: + # '^(?P[0-9]+)?(?:^|,)(?P[^,]+)(?:$|,(?!$))(?P(?:[^=]+=[^;]+)+)*$' + # with formatting and explanation -- note this regex is used with re.match: + "^" # start of string + "(?P[0-9]+)?" # count is 1+ digits, and is optional + "(?:^|,)" # match start of string or a comma + # -- so type can be at the start or comma separated from count + "(?P[^,]+)" # type is 1+ anything not a comma (including digits), and is required + "(?:$|,(?!$))" # match end of string | or a non-trailing comma + # -- so type can be at the end or followed by attrs + "(?P(?:[^=]+=[^;]+)+)*" # attrs is any number of semicolon separated key=value items + # -- value can have spare '=' inside, possible not intended + # -- attrs will be matched with ATTR.finditer afterwards in parse_device_constraint + "$" # end of string +) +ATTR = re.compile(";?(?P[^=]+)=(?P[^;]+)") + + +class DeviceConstraintDict(TypedDict): + count: Required[int] + type: Required[str] + attributes: NotRequired[Dict[str, str]] + + +def parse_device_constraint(constraint: str) -> DeviceConstraintDict: + m = DEVICE.match(constraint) + if m is None: + raise ValueError("device constraint does not match") + device: DeviceConstraintDict = {} + count = m.group("count") + if count: + count = int(count) + device["count"] = count if count > 0 else 1 + else: + device["count"] = 1 + device["type"] = m.group("type") + attrs = m.group("attrs") + if attrs: + device["attributes"] = { + match.group("key"): match.group("value") for match in ATTR.finditer(attrs) + } + return device diff --git a/build/lib/juju/controller.py b/build/lib/juju/controller.py new file mode 100644 index 00000000..c5b077da --- /dev/null +++ b/build/lib/juju/controller.py @@ -0,0 +1,1009 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import json +import logging +from concurrent.futures import CancelledError +from pathlib import Path + +import websockets + +from . import errors, jasyncio, tag, utils +from .client import client, connector +from .errors import JujuAPIError +from .offerendpoints import ParseError as OfferParseError +from .offerendpoints import parse_offer_endpoint, parse_offer_url +from .user import User + +log = logging.getLogger(__name__) + + +class RemoveError(Exception): + def __init__(self, message): + self.message = message + + +class Controller: + def __init__( + self, + max_frame_size=None, + bakery_client=None, + jujudata=None, + ): + """Instantiate a new Controller. + + One of the connect_* methods will need to be called before this + object can be used for anything interesting. + + If jujudata is None, jujudata.FileJujuData will be used. + + :param max_frame_size: See + `juju.client.connection.Connection.MAX_FRAME_SIZE` + :param bakery_client httpbakery.Client: The bakery client to use + for macaroon authorization. + :param jujudata JujuData: The source for current controller + information. + """ + self._connector = connector.Connector( + max_frame_size=max_frame_size, + bakery_client=bakery_client, + jujudata=jujudata, + ) + self._controller_name = None + + async def __aenter__(self): + await self.connect() + return self + + async def __aexit__(self, exc_type, exc, tb): + await self.disconnect() + + async def connect(self, *args, **kwargs): + """Connect to a Juju controller. + + This supports two calling conventions: + + The controller and (optionally) authentication information can be + taken from the data files created by the Juju CLI. This convention + will be used if a ``controller_name`` is specified, or if the + ``endpoint`` is not. + + Otherwise, both the ``endpoint`` and authentication information + (``username`` and ``password``, or ``bakery_client`` and/or + ``macaroons``) are required. + + If a single positional argument is given, it will be assumed to be + the ``controller_name``. Otherwise, the first positional argument, + if any, must be the ``endpoint``. + + Available parameters are: + + :param str controller_name: Name of controller registered with the + Juju CLI. + :param str endpoint: The hostname:port of the controller to connect to. + :param str username: The username for controller-local users (or None + to use macaroon-based login.) + :param str password: The password for controller-local users. + :param str cacert: The CA certificate of the controller + (PEM formatted). + :param httpbakery.Client bakery_client: The macaroon bakery client to + to use when performing macaroon-based login. Macaroon tokens + acquired when logging will be saved to bakery_client.cookies. + If this is None, a default bakery_client will be used. + :param list macaroons: List of macaroons to load into the + ``bakery_client``. + :param int max_frame_size: The maximum websocket frame size to allow. + :param specified_facades: (deprecated) overwrite the facades with a series of + specified facades. + """ + await self.disconnect() + if "endpoint" not in kwargs and len(args) < 2: + if args and "model_name" in kwargs: + raise TypeError("connect() got multiple values for controller_name") + elif args: + controller_name = args[0] + else: + controller_name = kwargs.pop("controller_name", None) + await self._connector.connect_controller(controller_name, **kwargs) + else: + if "controller_name" in kwargs: + raise TypeError( + "connect() got values for both controller_name and endpoint" + ) + if args and "endpoint" in kwargs: + raise TypeError("connect() got multiple values for endpoint") + has_userpass = len(args) >= 3 or {"username", "password"}.issubset(kwargs) + has_macaroons = len(args) >= 5 or not { + "bakery_client", + "macaroons", + }.isdisjoint(kwargs) + if not (has_userpass or has_macaroons): + raise TypeError("connect() missing auth params") + arg_names = [ + "endpoint", + "username", + "password", + "cacert", + "bakery_client", + "macaroons", + "max_frame_size", + ] + for i, arg in enumerate(args): + kwargs[arg_names[i]] = arg + if "endpoint" not in kwargs: + raise ValueError("endpoint is required if controller_name not given") + if not ( + {"username", "password"}.issubset(kwargs) + or {"bakery_client", "macaroons"}.intersection(kwargs) + ): + raise ValueError( + "Authentication parameters are required " + "if controller_name not given" + ) + await self._connector.connect(**kwargs) + await self.update_endpoints() + + async def update_endpoints(self): + try: + info = await self.info() + self._connector._connection.endpoints = [ + (e, info.results[0].cacert) for e in info.results[0].addresses + ] + except errors.JujuPermissionError: + log.warning( + "This user doesn't have at least read access to the controller model, so endpoints are not updated after connection." + ) + pass + + async def connect_current(self): + """.. deprecated:: 0.7.3 + Use :meth:`.connect()` instead. + """ + return await self.connect() + + async def connect_controller(self, controller_name): + """.. deprecated:: 0.7.3 + Use :meth:`.connect(controller_name)` instead. + """ + return await self.connect(controller_name) + + async def _connect_direct(self, **kwargs): + await self.disconnect() + await self._connector.connect(**kwargs) + + def is_connected(self): + """Reports whether the Controller is currently connected.""" + return self._connector.is_connected() + + def connection(self): + """Return the current Connection object. It raises an exception + if the Controller is disconnected + """ + return self._connector.connection() + + @property + def controller_name(self): + if not self._controller_name: + try: + self._controller_name = ( + self._connector.jujudata.controller_name_by_endpoint( + self._connector.connection().endpoint + ) + ) + except FileNotFoundError: + raise errors.PylibjujuError( + "Unable to determine controller name. controllers.yaml not found." + ) + return self._controller_name + + @property + def controller_uuid(self): + return self._connector.controller_uuid + + @property + async def api_endpoints(self): + """Get API endpoints + + :return list string: List of API Endpoints + """ + info = await self.info() + return info.results[0].addresses + + async def disconnect(self): + """Shut down the watcher task and close websockets.""" + await self._connector.disconnect(entity="controller") + + async def add_credential( + self, name=None, credential=None, cloud=None, owner=None, force=False + ): + """Add or update a credential to the controller. + + :param str name: Name of new credential. If None, the default + local credential is used. Name must be provided if a credential + is given. + :param CloudCredential credential: Credential to add. If not given, + it will attempt to read from local data, if available. + :param str cloud: Name of cloud to associate the credential with. + Defaults to the same cloud as the controller. + :param str owner: Username that will own the credential. Defaults to + the current user. + :param bool force: Force indicates whether the update should be forced. + It's only supported for facade v3 or later. + Defaults to false. + :returns: Name of credential that was uploaded. + """ + if not cloud: + cloud = await self.get_cloud() + + if not owner: + owner = self.connection().info["user-info"]["identity"] + + if credential and not name: + raise errors.JujuError("Name must be provided for credential") + + if not credential: + name, credential = self._connector.jujudata.load_credential(cloud, name) + if credential is None: + raise errors.JujuError(f"Unable to find credential: {name}") + + if credential.auth_type == "jsonfile" and "file" in credential.attrs: + # file creds have to be loaded before being sent to the controller + try: + # it might already be JSON + json.loads(credential.attrs["file"]) + except json.JSONDecodeError: + # not valid JSON, so maybe it's a file + cred_path = Path(credential.attrs["file"]) + if cred_path.exists(): + # make a copy + cred_json = credential.to_json() + credential = client.CloudCredential.from_json(cred_json) + # inline the cred + credential.attrs["file"] = cred_path.read_text() + + log.debug("Uploading credential %s", name) + cloud_facade = client.CloudFacade.from_connection(self.connection()) + tagged_credentials = [ + client.TaggedCredential( + tag=tag.credential(cloud, tag.untag("user-", owner), name), + credential=credential, + ) + ] + if cloud_facade.version >= 3: + # UpdateCredentials was renamed to UpdateCredentialsCheckModels + # in facade version 3. + await cloud_facade.UpdateCredentialsCheckModels( + credentials=tagged_credentials, + force=force, + ) + else: + await cloud_facade.UpdateCredentials(credentials=tagged_credentials) + return name + + async def add_cloud(self, name, cloud): + """Add a cloud to this controller. + + :param str name: Name to give the new cloud. + :param Cloud cloud: Cloud configuration. + :return Cloud: Cloud that was created. + """ + log.debug("Adding cloud %s", name) + cloud_facade = client.CloudFacade.from_connection(self.connection()) + await cloud_facade.AddCloud(cloud=cloud, name=name) + result = await self.cloud(name=name) + return result.cloud + + async def info(self): + """Show Controller Info from connection + + :return ControllerAPIInfoResult + """ + log.debug("Getting information") + uuids = await self.model_uuids() + if "controller" not in uuids: + raise errors.JujuPermissionError("Requires access to controller model.") + controller_facade = client.ControllerFacade.from_connection(self.connection()) + params = [client.Entity(tag.model(uuids["controller"]))] + return await controller_facade.ControllerAPIInfoForModels(entities=params) + + async def remove_cloud(self, name): + """Remove a cloud from this controller. + + :param str name: Name of the cloud to remove. + """ + log.debug("Removing cloud %s", name) + cloud_facade = client.CloudFacade.from_connection(self.connection()) + await cloud_facade.RemoveClouds(entities=[client.Entity(tag.cloud(name))]) + + async def add_model( + self, + model_name, + cloud_name=None, + credential_name=None, + owner=None, + config=None, + region=None, + ): + """Add a model to this controller. + + :param str model_name: Name to give the new model. + :param str cloud_name: Name of the cloud in which to create the + model, e.g. 'aws'. Defaults to same cloud as controller. + :param str credential_name: Name of the credential to use when + creating the model. If not given, it will attempt to find a + default credential. + :param str owner: Username that will own the model. Defaults to + the current user. + :param dict config: Model configuration. + :param str region: Region in which to create the model. + :return Model: A connection to the newly created model. + """ + model_facade = client.ModelManagerFacade.from_connection(self.connection()) + + owner = owner or self.connection().info["user-info"]["identity"] + cloud_name = cloud_name or await self.get_cloud() + + try: + # attempt to add/update the credential from local data if available + credential_name = await self.add_credential( + name=credential_name, cloud=cloud_name, owner=owner + ) + except errors.JujuError: + # if it's not available locally, assume it's on the controller + pass + + if credential_name: + credential = tag.credential( + cloud_name, tag.untag("user-", owner), credential_name + ) + else: + credential = None + + log.debug("Creating model %s", model_name) + + if not config or "authorized-keys" not in config: + config = config or {} + config["authorized-keys"] = await utils.read_ssh_key() + + model_info = await model_facade.CreateModel( + cloud_tag=tag.cloud(cloud_name), + config=config, + credential=credential, + name=model_name, + owner_tag=owner, + region=region, + ) + from juju.model import Model + + model = Model(jujudata=self._connector.jujudata) + kwargs = self.connection().connect_params() + kwargs["uuid"] = model_info.uuid + model._info = model_info + await model._connect_direct(**kwargs) + + return model + + async def destroy_models( + self, *models, destroy_storage=False, force=False, max_wait=None + ): + """Destroy one or more models. + + :param str *models: Names or UUIDs of models to destroy + :param bool destroy_storage: Whether or not to destroy storage when + destroying the models. Defaults to false. + :param bool force: Whether or not to force when destroying the models. + Defaults to false. + :param int max_wait : Max time in seconds to wait when destroying the models. + + """ + uuids = await self.model_uuids() + models = [uuids.get(model) or model for model in models] + + model_facade = client.ModelManagerFacade.from_connection(self.connection()) + + log.debug( + "Destroying model%s %s", "" if len(models) == 1 else "s", ", ".join(models) + ) + + if model_facade.version >= 5: + params = [ + client.DestroyModelParams( + model_tag=tag.model(model), + destroy_storage=destroy_storage, + force=force, + max_wait=max_wait, + ) + for model in models + ] + await model_facade.DestroyModels(models=params) + else: + params = [client.Entity(tag.model(model)) for model in models] + + await model_facade.DestroyModels(entities=params) + + destroy_model = destroy_models + + async def add_user(self, username, password=None, display_name=None): + """Add a user to this controller. + + :param str username: Username + :param str password: Password + :param str display_name: Display name + :returns: A :class:`~juju.user.User` instance + """ + if not display_name: + display_name = username + user_facade = client.UserManagerFacade.from_connection(self.connection()) + users = [ + client.AddUser( + display_name=display_name, username=username, password=password + ) + ] + results = await user_facade.AddUser(users=users) + secret_key = results.results[0].secret_key + return await self.get_user(username, secret_key=secret_key) + + async def remove_user(self, username): + """Remove a user from this controller.""" + client_facade = client.UserManagerFacade.from_connection(self.connection()) + user = tag.user(username) + await client_facade.RemoveUser(entities=[client.Entity(user)]) + + async def change_user_password(self, username, password): + """Change the password for a user in this controller. + + :param str username: Username + :param str password: New password + + """ + user_facade = client.UserManagerFacade.from_connection(self.connection()) + entity = client.EntityPassword(password=password, tag=tag.user(username)) + return await user_facade.SetPassword(changes=[entity]) + + async def reset_user_password(self, username): + """Reset user password. + + :param str username: Username + :returns: A :class:`~juju.user.User` instance + """ + user_facade = client.UserManagerFacade.from_connection(self.connection()) + entity = client.Entity(tag.user(username)) + results = await user_facade.ResetPassword(entities=[entity]) + secret_key = results.results[0].secret_key + return await self.get_user(username, secret_key=secret_key) + + async def destroy(self, destroy_all_models=False, destroy_storage=False): + """Destroy this controller. + + :param bool destroy_all_models: Destroy all hosted models in the + controller. + :param bool destroy_storage: Destroy all hosted storage in the + controller. + """ + controller_facade = client.ControllerFacade.from_connection(self.connection()) + return await controller_facade.DestroyController( + destroy_models=destroy_all_models, destroy_storage=destroy_storage + ) + + async def disable_user(self, username): + """Disable a user. + + :param str username: Username + + """ + user_facade = client.UserManagerFacade.from_connection(self.connection()) + entity = client.Entity(tag.user(username)) + return await user_facade.DisableUser(entities=[entity]) + + async def enable_user(self, username): + """Re-enable a previously disabled user.""" + user_facade = client.UserManagerFacade.from_connection(self.connection()) + entity = client.Entity(tag.user(username)) + return await user_facade.EnableUser(entities=[entity]) + + async def get_model_info(self, model_name=None, model_uuid=None): + """Return a client.ModelInfo object for a given Model. + + Retrieves latest info for this Model from the api server. The + return value is cached on the Model.info attribute so that the + valued may be accessed again without another api call, if + desired. + + This method is called automatically when the Model is connected, + resulting in Model.info being initialized without requiring an + explicit call to this method. + + """ + if model_uuid is None and model_name is None: + raise errors.JujuError( + "get_model_info requires either a name or a uuid for a model" + ) + + facade = client.ModelManagerFacade.from_connection(self.connection()) + if model_uuid is None: + uuids = await self.model_uuids() + try: + model_uuid = uuids[model_name] + except KeyError: + raise errors.JujuError( + f"{model_name} is not among the models in the controller : {uuids}" + ) + entity = client.Entity(tag.model(model_uuid)) + _model_info_results = await facade.ModelInfo(entities=[entity]) + return _model_info_results.results[0].result + + async def cloud(self, name=None): + """Get Cloud + + :param str name: Cloud name. If not specified, the cloud where + the controller lives on is returned. + :returns: -> ~CloudResult + """ + if name is None: + name = await self.get_cloud() + entity = client.Entity(tag.cloud(name)) + cloud_facade = client.CloudFacade.from_connection(self.connection()) + cloud = await cloud_facade.Cloud(entities=[entity]) + if len(cloud.results) == 0: + log.error("No clouds found.") + raise + elif len(cloud.results) > 1: + log.error("More than one cloud found.") + raise + return cloud.results[0] + + async def clouds(self): + """Get all the clouds in the controller + + :returns: -> ~CloudsResult + """ + cloud_facade = client.CloudFacade.from_connection(self.connection()) + return await cloud_facade.Clouds() + + async def get_cloud(self): + """Get the name of the cloud that this controller lives on.""" + cloud_facade = client.CloudFacade.from_connection(self.connection()) + + result = await cloud_facade.Clouds() + cloud = next(iter(result.clouds)) + return tag.untag("cloud-", cloud) + + async def get_models(self, all=False, username=None): # noqa: A002 + """.. deprecated:: 0.7.0 + Use :meth:`.list_models` instead. + """ + return await self.list_models(username, all) + + async def model_uuids(self, username=None, all=False): # noqa: A002 + """Return a mapping of model names to UUIDs the given user can access. + + :param str username: Optional username argument, defaults to + current connected user. + + :param bool all: Flag to list all models, regardless of + user accessibility (administrative users only) + + :returns: {str name : str UUID} + """ + model_manager_facade = client.ModelManagerFacade.from_connection( + self.connection() + ) + u_name = username if username else self.get_current_username() + user = tag.user(u_name) + + user_model_list = await model_manager_facade.ListModelSummaries( + user_tag=user, all_=all + ) + model_summaries = [msr.result for msr in user_model_list.results] + return { + model_summary.name: model_summary.uuid for model_summary in model_summaries + } + + async def list_models(self, username=None, all=False): # noqa: A002 + """Return list of names of the available models on this controller. + + Equivalent to ``sorted((await self.model_uuids()).keys())`` + """ + uuids = await self.model_uuids(username, all) + return sorted(uuids.keys()) + + async def get_current_user(self, secret_key=None): + """Returns the user object associated with the current connection. + :param str secret_key: Issued by juju when add or reset user + password + + :returns: A :class:`~juju.user.User` instance + """ + return await self.get_user(self.connection().username) + + def get_current_username(self): + """Returns the username associated with the current connection. + + :returns: :str: username of the connected user + """ + return self.connection().username + + async def get_model(self, model): + """Get a model by name or UUID. + + :param str model: Model name or UUID + :returns Model: Connected Model instance. + """ + uuids = await self.model_uuids() + uuid = uuids.get(model) or model + + from juju.model import Model + + model = Model() + kwargs = self.connection().connect_params() + kwargs["uuid"] = uuid + await model._connect_direct(**kwargs) + return model + + async def get_user(self, username, secret_key=None): + """Get a user by name. + + :param str username: Username + :param str secret_key: Issued by juju when add or reset user + password + :returns: A :class:`~juju.user.User` instance + """ + client_facade = client.UserManagerFacade.from_connection(self.connection()) + user = tag.user(username) + args = [client.Entity(user)] + try: + response = await client_facade.UserInfo( + entities=args, include_disabled=True + ) + except errors.JujuError as e: + if "permission denied" in e.errors: + # apparently, trying to get info for a nonexistent user returns + # a "permission denied" error rather than an empty result set + return None + raise + if response.results and response.results[0].result: + return User(self, response.results[0].result, secret_key=secret_key) + return None + + async def get_users(self, include_disabled=False): + """Return list of users that can connect to this controller. + + :param bool include_disabled: Include disabled users + :returns: A list of :class:`~juju.user.User` instances + """ + client_facade = client.UserManagerFacade.from_connection(self.connection()) + response = await client_facade.UserInfo( + entities=None, include_disabled=include_disabled + ) + return [User(self, r.result) for r in response.results] + + async def grant(self, username, acl="login"): + """Grant access level of the given user on the controller. + Note that if the user already has higher permissions than the + provided ACL, this will do nothing (see revoke for a way to + remove permissions). + :param str username: Username + :param str acl: Access control ('login', 'add-model' or 'superuser') + :returns: True if new access was granted, False if user already had + requested access or greater. Raises JujuError if failed. + """ + controller_facade = client.ControllerFacade.from_connection(self.connection()) + user = tag.user(username) + changes = client.ModifyControllerAccess(acl, "grant", user) + try: + await controller_facade.ModifyControllerAccess(changes=[changes]) + return True + except errors.JujuError as e: + if "user already has" in str(e): + return False + else: + raise + + async def revoke(self, username, acl="login"): + """Removes some or all access of a user to from a controller + If 'login' access is revoked, the user will no longer have any + permissions on the controller. Revoking a higher privilege from + a user without that privilege will have no effect. + + :param str username: username + :param str acl: Access to remove ('login', 'add-model' or 'superuser') + """ + controller_facade = client.ControllerFacade.from_connection(self.connection()) + user = tag.user(username) + changes = client.ModifyControllerAccess(acl, "revoke", user) + return await controller_facade.ModifyControllerAccess(changes=[changes]) + + async def grant_model(self, username, model_uuid, acl="read"): + """Grant a user access to a model. Note that if the user + already has higher permissions than the provided ACL, + this will do nothing (see revoke_model for a way to remove + permissions). + + :param str username: Username + :param str model_uuid: The UUID of the model to change. + :param str acl: Access control ('read, 'write' or 'admin') + """ + model_facade = client.ModelManagerFacade.from_connection(self.connection()) + user = tag.user(username) + model = tag.model(model_uuid) + changes = client.ModifyModelAccess(acl, "grant", model, user) + return await model_facade.ModifyModelAccess(changes=[changes]) + + async def revoke_model(self, username, model_uuid, acl="read"): + """Revoke some or all of a user's access to a model. + If 'read' access is revoked, the user will no longer have any + permissions on the model. Revoking a higher privilege from + a user without that privilege will have no effect. + + :param str username: Username to revoke + :param str model_uuid: The UUID of the model to change. + :param str acl: Access control ('read, 'write' or 'admin') + """ + model_facade = client.ModelManagerFacade.from_connection(self.connection()) + user = tag.user(username) + model = tag.model(model_uuid) + changes = client.ModifyModelAccess(acl, "revoke", model, user) + return await model_facade.ModifyModelAccess(changes=[changes]) + + async def create_offer( + self, model_uuid, endpoint, offer_name=None, application_name=None + ): + """Offer a deployed application using a series of endpoints for use by + consumers. + + @param endpoint: holds the application and endpoint you want to offer + @param offer_name: override the offer name to help the consumer + @param application_name: overrides the application name in the endpoint + """ + # If we have both the offer_name and the application_name + # then we're coming from bundle/overlays, so no need to parse the endpoint + # Also we accept endpoints without a colon (:) in the overlays + if offer_name and application_name: + o_name = offer_name + a_name = application_name + eps = {endpoint: endpoint} + else: + try: + offer = parse_offer_endpoint(endpoint) + except OfferParseError as e: + log.error(e.message) + raise + + o_name = offer_name if offer_name else offer.application + a_name = application_name if application_name else offer.application + eps = {name: name for name in offer.endpoints} + + params = client.AddApplicationOffer() + params.application_name = a_name + params.endpoints = eps + params.offer_name = o_name + params.model_tag = tag.model(model_uuid) + + facade = client.ApplicationOffersFacade.from_connection(self.connection()) + return await facade.Offer(offers=[params]) + + async def list_offers(self, model_name): + """Offers list information about applications' endpoints that have been + shared and who is connected. + """ + params = client.OfferFilter() + params.model_name = model_name + + facade = client.ApplicationOffersFacade.from_connection(self.connection()) + return await facade.ListApplicationOffers(filters=[params]) + + async def remove_offer(self, model_uuid, offer, force=False): + """Remove offer for an application. + + Offers will also remove relations to those offers, use force to do + so, without an error. + """ + url = None + try: + url = parse_offer_url(offer) + except OfferParseError as e: + log.error(e.message) + raise + if url is None: + raise Exception + + offer_source = url.source + if offer_source == "": + offer_source = self.controller_name + if not force: + raise RemoveError( + "removing offer will also remove relations, use force and try again." + ) + + facade = client.ApplicationOffersFacade.from_connection(self.connection()) + return await facade.DestroyOffers(force=force, offer_urls=[url.string()]) + + async def get_consume_details(self, endpoint): + """get_consume_details returns the details necessary to pass to another + model to consume the specified offers represented by the urls. + """ + facade = client.ApplicationOffersFacade.from_connection(self.connection()) + offers = await facade.GetConsumeDetails( + offer_urls=client.OfferURLs(offer_urls=[endpoint]) + ) + if len(offers.results) != 1: + raise JujuAPIError("expected to find one result") + result = offers.results[0] + if result.error is not None: + raise JujuAPIError(result.error) + + return result + + async def watch_model_summaries(self, callback, as_admin=False): + """Watch the controller for model summary updates. + + If as_admin is true, a call will be made as the admin to watch + all models in the controller. If the user isn't a superuser they + will get a permission error. + """ + stop_event = jasyncio.Event() + + async def _watcher(stop_event): + try: + facade = client.ControllerFacade.from_connection(self.connection()) + watcher = client.ModelSummaryWatcherFacade.from_connection( + self.connection() + ) + if as_admin: + result = await facade.WatchAllModelSummaries() + watcher.Id = result.watcher_id + else: + result = await facade.WatchModelSummaries() + log.debug(f"watcher id: {result.watcher_id}") + watcher.Id = result.watcher_id + + while True: + try: + results = await utils.run_with_interrupt( + watcher.Next(), stop_event, log=log + ) + except JujuAPIError as e: + if "watcher was stopped" not in str(e): + raise + except websockets.ConnectionClosed: + break + if stop_event.is_set(): + try: + await watcher.Stop() + except websockets.ConnectionClosed: + pass # can't stop on a closed conn + break + for summary in results.models: + callback(summary) + except CancelledError: + pass + except Exception: + log.exception("Error in watcher") + raise + + log.debug("Starting watcher task for model summaries") + jasyncio.ensure_future(_watcher(stop_event)) + return stop_event + + async def add_secret_backends(self, id_, name, backend_type, config): + """Add a new secret backend. + + Parameters + ---------- + id : string + id for the backend + name : string + name of the backend + backend-type : string + config : dict + dictionary with the backend configuration values + + Returns + ------- + list + a list of errors if any + + """ + facade = client.SecretBackendsFacade.from_connection(self.connection()) + return await facade.AddSecretBackends([ + { + "id": id_, + "backend-type": backend_type, + "config": config, + "name": name, + "token-rotate-interval": config.get("token-rotate-interval", None), + } + ]) + + async def list_secret_backends(self, reveal=False): + """Return the list of secret backends + + Parameters + ---------- + reveal : boolean + include sensitive backend config content if true + + Returns + ------- + list + a list of available secret backends + + """ + facade = client.SecretBackendsFacade.from_connection(self.connection()) + return await facade.ListSecretBackends(None, reveal) + + async def remove_secret_backends(self, name, force=False): + """Remove a secrets backend. + + Parameters + ---------- + name : name of the backend + force : true if the operation is foced + + Returns + ------- + error if any + + """ + facade = client.SecretBackendsFacade.from_connection(self.connection()) + return await facade.RemoveSecretBackends([{"name": name, "force": force}]) + + async def update_secret_backends( + self, + name, + config=None, + force=False, + name_change=None, + token_rotate_interval=None, + ): + """Update a backend. + + Parameters + ---------- + name : string + the backend name + config : dict + key value dict with configuration parameters + force : boolean + true to force the update process + name_change : string + new name for the backend + token_rotate_interval : int + token rotation interval + + """ + facade = client.SecretBackendsFacade.from_connection(self.connection()) + return await facade.UpdateSecretBackends([ + { + "name": name, + "config": config, + "force": force, + "token-rotate-interval": token_rotate_interval, + "name-change": name_change, + } + ]) + + +class ConnectedController(Controller): + def __init__( + self, + connection, + max_frame_size=None, + bakery_client=None, + jujudata=None, + ): + super().__init__( + max_frame_size=max_frame_size, + bakery_client=bakery_client, + jujudata=jujudata, + ) + self._conn = connection + + async def __aenter__(self): + kwargs = self._conn.connect_params() + kwargs.pop("uuid") + await self._connect_direct(**kwargs) + return self + + async def __aexit__(self, exc_type, exc, tb): + await self.disconnect() diff --git a/build/lib/juju/delta.py b/build/lib/juju/delta.py new file mode 100644 index 00000000..c82a20bd --- /dev/null +++ b/build/lib/juju/delta.py @@ -0,0 +1,140 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +from . import model +from .client import client, overrides + + +def get_entity_delta(d: overrides.Delta): + return _delta_types[d.entity](d.deltas) + + +def get_entity_class(entity_type): + return _delta_types[entity_type].get_entity_class() + + +class EntityDelta(client.Delta): + data: dict[str, str] + + def get_id(self) -> str: + return self.data["id"] + + @classmethod + def get_entity_class(cls) -> type[model.ModelEntity]: + raise NotImplementedError() + + +class ActionDelta(EntityDelta): + @classmethod + def get_entity_class(cls): + from .action import Action + + return Action + + +class ApplicationDelta(EntityDelta): + def get_id(self): + return self.data["name"] + + @classmethod + def get_entity_class(cls): + from .application import Application + + return Application + + +class AnnotationDelta(EntityDelta): + def get_id(self): + return self.data["tag"] + + @classmethod + def get_entity_class(cls): + from .annotation import Annotation + + return Annotation + + +class ModelDelta(EntityDelta): + def get_id(self): + return self.data["model-uuid"] + + @classmethod + def get_entity_class(cls): + from .model import ModelInfo + + return ModelInfo + + +class MachineDelta(EntityDelta): + @classmethod + def get_entity_class(cls): + from .machine import Machine + + return Machine + + +class UnitDelta(EntityDelta): + def get_id(self): + return self.data["name"] + + @classmethod + def get_entity_class(cls): + from .unit import Unit + + return Unit + + +class RelationDelta(EntityDelta): + @classmethod + def get_entity_class(cls): + from .relation import Relation + + return Relation + + +class RemoteApplicationDelta(EntityDelta): + def get_id(self): + return self.data["name"] + + @classmethod + def get_entity_class(cls): + from .remoteapplication import RemoteApplication + + return RemoteApplication + + +class CharmDelta(EntityDelta): + def get_id(self): + return self.data["charm-url"] + + @classmethod + def get_entity_class(cls): + from .charm import Charm + + return Charm + + +class ApplicationOfferDelta(EntityDelta): + def get_id(self): + return self.data["application-name"] + + @classmethod + def get_entity_class(cls): + from .remoteapplication import ApplicationOffer + + return ApplicationOffer + + +_delta_types = { + "action": ActionDelta, + "annotation": AnnotationDelta, + "application": ApplicationDelta, + "applicationOffer": ApplicationOfferDelta, + "charm": CharmDelta, + "machine": MachineDelta, + "model": ModelDelta, + "relation": RelationDelta, + "remoteApplication": RemoteApplicationDelta, + "unit": UnitDelta, +} diff --git a/build/lib/juju/errors.py b/build/lib/juju/errors.py new file mode 100644 index 00000000..92418703 --- /dev/null +++ b/build/lib/juju/errors.py @@ -0,0 +1,156 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + + +class JujuError(Exception): + def __init__(self, *args, **kwargs): + self.message = "" + self.errors = [] + if args: + self.message = str(args[0]) + if isinstance(args[0], (list, tuple)): + self.errors = args[0] + elif len(args) > 1: + self.errors = list(args) + else: + self.errors = [self.message] + super().__init__(*args, **kwargs) + + +class JujuAPIError(JujuError): + def __init__(self, result): + self.result = result + self.message = result["error"] + self.error_code = result.get("error-code") + self.response = result["response"] + self.request_id = result["request-id"] + self.error_info = result.get("error-info") + super().__init__(self.message) + + +class JujuConnectionError(ConnectionError, JujuError): + pass + + +class JujuAuthError(JujuConnectionError): + pass + + +class JujuRedirectException(Exception): + """Exception indicating that a redirection was requested""" + + def __init__(self, redirect_info, follow_redirect=True): + self.redirect_info = redirect_info + self.follow_redirect = follow_redirect + + @property + def ca_cert(self): + return self.redirect_info["ca-cert"] + + @property + def endpoints(self): + return [ + ("{value}:{port}".format(**s), self.ca_cert) + for servers in self.redirect_info["servers"] + for s in servers + if s["scope"] == "public" or not self.follow_redirect + ] + + +class JujuEntityNotFoundError(JujuError): + """Exception indicating that an entity was not found in the state. It was + expected that the entity was found in state and this is a terminal + condition. + To fix this condition, you should disconnect and reconnect to ensure that + any missing entities are correctly picked up. + """ + + def __init__(self, entity_name, entity_types=None): + self.entity_name = entity_name + self.entity_types = entity_types + super().__init__(f"Entity not found: {entity_name}") + + +class JujuModelError(JujuError): + pass + + +class JujuMachineError(JujuError): + pass + + +class JujuAgentError(JujuError): + pass + + +class JujuAppError(JujuError): + pass + + +class JujuUnitError(JujuError): + pass + + +class JujuPermissionError(JujuError): + pass + + +class JujuBackupError(JujuError): + pass + + +class PylibjujuProgrammingError(Exception): + pass + + +class JujuNotSupportedError(JujuError): + pass + + +class JujuNotValid(JujuError): + def __init__(self, entity_type, entity_name): + self.entity_type = entity_type + self.entity_name = entity_name + super().__init__(f"Invalid {entity_type} : {entity_name}") + + +class JujuConfigError(JujuError): + """Exception raised during processing a configuration key-value pair + in a config set for an application. + """ + + def __init__(self, config, config_pair, message=None): + self.config = config + self.config_pair = config_pair + if message is None: + self.message = ( + "Couldn't process the value of a config pair : %s, value of type %s" + % (self.config_pair, type(self.config_pair[1])) + ) + else: + self.message = message + super().__init__(self.message) + + +class JujuApplicationConfigError(JujuConfigError): + pass + + +class JujuModelConfigError(JujuConfigError): + pass + + +class JujuControllerNotFoundError(JujuError): + pass + + +class AbstractMethodError(Exception): + pass + + +class PylibjujuError(JujuError): + pass + + +class JujuUnknownVersion(PylibjujuError): + pass diff --git a/build/lib/juju/exceptions.py b/build/lib/juju/exceptions.py new file mode 100644 index 00000000..a8544993 --- /dev/null +++ b/build/lib/juju/exceptions.py @@ -0,0 +1,6 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + + +class DeadEntityException(Exception): + pass diff --git a/build/lib/juju/jasyncio.py b/build/lib/juju/jasyncio.py new file mode 100644 index 00000000..49d49914 --- /dev/null +++ b/build/lib/juju/jasyncio.py @@ -0,0 +1,174 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +# A compatibility layer on asyncio that ensures we have all the right +# bindings for the functions we need from asyncio. Reason for this +# layer is the frequent functional changes, additions and deprecations +# in asyncio across the different Python versions. + +# Any module that needs to use the asyncio should get the binding from +# this layer. + +import asyncio +import functools +import logging +import signal +from asyncio import ( + ALL_COMPLETED as ALL_COMPLETED, +) +from asyncio import ( + FIRST_COMPLETED as FIRST_COMPLETED, +) +from asyncio import ( + CancelledError, + Task, + create_task, + wait, +) + +# FIXME: integration tests don't use these, but some are used in this repo +# Use primitives from asyncio within this repo and remove these re-exports +from asyncio import ( + Event as Event, +) +from asyncio import ( + Lock as Lock, +) +from asyncio import ( + Queue as Queue, +) +from asyncio import ( + TimeoutError as TimeoutError, # noqa: A004 +) +from asyncio import ( + all_tasks as all_tasks, +) +from asyncio import ( + as_completed as as_completed, +) +from asyncio import ( + create_subprocess_exec as create_subprocess_exec, +) +from asyncio import ( + current_task as current_task, +) +from asyncio import ( + ensure_future as ensure_future, +) +from asyncio import ( + gather as gather, +) +from asyncio import ( + get_event_loop_policy as get_event_loop_policy, +) +from asyncio import ( + get_running_loop as get_running_loop, +) +from asyncio import ( + new_event_loop as new_event_loop, +) +from asyncio import ( + shield as shield, +) +from asyncio import ( + sleep as sleep, +) +from asyncio import ( + subprocess as subprocess, +) +from asyncio import ( + wait_for as wait_for, +) + +import websockets + +ROOT_LOGGER = logging.getLogger() + + +def create_task_with_handler(coro, task_name, logger=ROOT_LOGGER) -> Task: + """Wrapper around "asyncio.create_task" to make sure the task + exceptions are handled properly. + + asyncio loop event_handler is only called on task exceptions when + the Task object is cleared from memory. But the GC doesn't clear + the Task if we keep a reference for it (e.g. _pinger_task in + connection.py) until the very end. + + This makes sure the exceptions are retrieved and properly + handled/logged whenever the Task is destroyed. + """ + + def _task_result_exp_handler(task, task_name=task_name, logger=logger): + try: + task.result() + except CancelledError: + pass + except websockets.exceptions.ConnectionClosed: + return + except Exception as e: + # This really is an arbitrary exception we need to catch + # + # No need to re-raise, though, because after this point + # the only thing that can catch this is asyncio loop base + # event_handler, which won't do anything but yell 'Task + # exception was never retrieved' anyways. + logger.exception("Task %s raised an exception: %s" % (task_name, e)) + + task = create_task(coro) + task.add_done_callback( + functools.partial(_task_result_exp_handler, task_name=task_name, logger=logger) + ) + return task + + +class SingletonEventLoop: + """Single instance containing an event loop to be reused.""" + + loop = None + + def __new__(cls): + if not hasattr(cls, "instance"): + cls.instance = super().__new__(cls) + cls.instance.loop = asyncio.new_event_loop() + + return cls.instance + + +def run(*steps): + """Helper to run one or more async functions synchronously, with graceful + handling of SIGINT / Ctrl-C. + + Returns the return value of the last function. + """ + if not steps: + return + + task = None + run._sigint = False # function attr to allow setting from closure + # Use a singleton class to force a single event loop instance + loop = SingletonEventLoop().loop + + def abort(): + task.cancel() + run._sigint = True + + added = False + try: + loop.add_signal_handler(signal.SIGINT, abort) + added = True + except (ValueError, OSError, RuntimeError) as e: + # add_signal_handler doesn't work in a thread + if "main thread" not in str(e): + raise + try: + for step in steps: + task = loop.create_task(step) + loop.run_until_complete(wait([task])) + if run._sigint: + raise KeyboardInterrupt() + if task.exception(): + raise task.exception() + return task.result() + finally: + if added: + loop.remove_signal_handler(signal.SIGINT) diff --git a/build/lib/juju/juju.py b/build/lib/juju/juju.py new file mode 100644 index 00000000..f77ab47a --- /dev/null +++ b/build/lib/juju/juju.py @@ -0,0 +1,37 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +from juju.client.jujudata import FileJujuData +from juju.controller import Controller +from juju.errors import JujuError + + +class Juju: + def __init__(self, jujudata=None): + self.jujudata = jujudata or FileJujuData() + + def get_controllers(self): + """Return list of all available controllers.""" + return self.jujudata.controllers() + + async def get_controller(self, name, include_passwords=False): + """Get a controller by name. + + :param str name: Name of controller + :param bool include_passwords: Include passwords for accounts + + The returned controller will try and connect to be ready to use. + """ + # check if name is in the controllers.yaml + controllers = self.jujudata.controllers() + assert isinstance(controllers, dict) + if name not in controllers: + raise JujuError( + "%s is not among the controllers: %s" % (name, controllers.keys()) + ) + + # make a new Controller object that's connected to the + # controller with the given name + controller = Controller() + await controller.connect(name) + return controller diff --git a/build/lib/juju/loop.py b/build/lib/juju/loop.py new file mode 100644 index 00000000..d9d2e8f8 --- /dev/null +++ b/build/lib/juju/loop.py @@ -0,0 +1,12 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +from .jasyncio import * # noqa + +import warnings + +warnings.warn( + "juju.loop module is being deprecated by 3.0, use juju.jasyncio instead", + DeprecationWarning, + stacklevel=2, +) diff --git a/build/lib/juju/machine.py b/build/lib/juju/machine.py new file mode 100644 index 00000000..70b7327b --- /dev/null +++ b/build/lib/juju/machine.py @@ -0,0 +1,310 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import ipaddress +import logging +import typing + +import pyrfc3339 + +from juju.utils import block_until, juju_ssh_key_paths + +from . import jasyncio, model, tag +from .annotationhelper import _get_annotations, _set_annotations +from .client import client +from .errors import JujuError + +log = logging.getLogger(__name__) + + +class Machine(model.ModelEntity): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + async def destroy(self, force=False): + """Remove this machine from the model. + + Blocks until the machine is actually removed. + + """ + if self.connection.is_using_old_client: + # Then we'll use the DestroyMachines from client.ClientFacade + facade = client.ClientFacade.from_connection(self.connection) + await facade.DestroyMachines(force=force, machine_names=[self.id]) + else: + facade = client.MachineManagerFacade.from_connection(self.connection) + await facade.DestroyMachineWithParams( + force=force, machine_tags=[tag.machine(self.id)] + ) + + log.debug("Destroying machine %s", self.id) + return await self.model._wait("machine", self.id, "remove") + + remove = destroy + + async def get_annotations(self): + """Get annotations on this machine. + + :return dict: The annotations for this application + """ + return await _get_annotations(self.tag, self.connection) + + async def set_annotations(self, annotations): + """Set annotations on this machine. + + :param annotations map[string]string: the annotations as key/value + pairs. + + """ + return await _set_annotations(self.tag, annotations, self.connection) + + def _format_addr(self, addr): + """Validate and format IP address. + + :param addr: IPv6 or IPv4 address + :type addr: str + :returns: Address string, optionally encapsulated in brackets ([]) + :rtype: str + :raises: ValueError + """ + ipaddr = ipaddress.ip_address(addr) + if isinstance(ipaddr, ipaddress.IPv6Address): + fmt = "[{}]" + else: + fmt = "{}" + return fmt.format(ipaddr) + + async def scp_to( + self, + source, + destination, + user="ubuntu", + proxy=False, + scp_opts="", + wait_for_active=False, + timeout=None, + ): + """Transfer files to this machine. + + :param str source: Local path of file(s) to transfer + :param str destination: Remote destination of transferred files + :param str user: Remote username + :param bool proxy: Proxy through the Juju API server + :param scp_opts: Additional options to the `scp` command + :type scp_opts: str or list + :param bool wait_for_active: Wait until the machine is ready to take in ssh commands. + :param int timeout: Time in seconds to wait until the machine becomes ready. + """ + if proxy: + raise NotImplementedError("proxy option is not implemented") + if wait_for_active: + await block_until(lambda: self.addresses, timeout=timeout) + try: + # if dns_name is an IP address format it appropriately + address = self._format_addr(self.dns_name) + except ValueError: + # otherwise we assume it to be a DNS resolvable string + address = self.dns_name + destination = f"{user}@{address}:{destination}" + await self._scp(source, destination, scp_opts) + + async def scp_from( + self, + source, + destination, + user="ubuntu", + proxy=False, + scp_opts="", + wait_for_active=False, + timeout=None, + ): + """Transfer files from this machine. + + :param str source: Remote path of file(s) to transfer + :param str destination: Local destination of transferred files + :param str user: Remote username + :param bool proxy: Proxy through the Juju API server + :param scp_opts: Additional options to the `scp` command + :type scp_opts: str or list + :param bool wait_for_active: Wait until the machine is ready to take in ssh commands. + :param int timeout: Time in seconds to wait until the machine becomes ready. + """ + if proxy: + raise NotImplementedError("proxy option is not implemented") + if wait_for_active: + await block_until(lambda: self.addresses, timeout=timeout) + try: + # if dns_name is an IP address format it appropriately + address = self._format_addr(self.dns_name) + except ValueError: + # otherwise we assume it to be a DNS resolvable string + address = self.dns_name + source = f"{user}@{address}:{source}" + await self._scp(source, destination, scp_opts) + + async def _scp(self, source, destination, scp_opts): + """Execute an scp command. Requires a fully qualified source and + destination. + """ + _, id_path = juju_ssh_key_paths() + cmd = ["scp", "-i", id_path, "-o", "StrictHostKeyChecking=no", "-q", "-B"] + cmd.extend(scp_opts.split() if isinstance(scp_opts, str) else scp_opts) + cmd.extend([source, destination]) + # There's a bit of a gap between the time that the machine is assigned an IP and the ssh + # service is up and listening, which creates a race for the ssh command. So we retry a + # couple of times until either we run out of attempts, or the ssh command succeeds to + # mitigate that effect. + # TODO (cderici): refactor the ssh and scp subcommand processing into a single method. + retry_backoff = 2 + retries = 10 + for _ in range(retries): + process = await jasyncio.create_subprocess_exec(*cmd) + await process.wait() + if process.returncode == 0: + break + await jasyncio.sleep(retry_backoff) + if process.returncode != 0: + raise JujuError(f"command failed after {retries} attempts: {cmd}") + + async def ssh( + self, + command, + user="ubuntu", + proxy=False, + ssh_opts=None, + wait_for_active=False, + timeout=None, + ): + """Execute a command over SSH on this machine. + + :param str command: Command to execute + :param str user: Remote username + :param bool proxy: Proxy through the Juju API server + :param str ssh_opts: Additional options to the `ssh` command + :param bool wait_for_active: Wait until the machine is ready to take in ssh commands. + :param int timeout: Time in seconds to wait until the machine becomes ready. + """ + if proxy: + raise NotImplementedError("proxy option is not implemented") + if wait_for_active: + await block_until(lambda: self.addresses, timeout=timeout) + address = self.dns_name + destination = f"{user}@{address}" + _, id_path = juju_ssh_key_paths() + cmd = [ + "ssh", + "-i", + id_path, + "-o", + "StrictHostKeyChecking=no", + "-q", + destination, + ] + if ssh_opts: + cmd.extend(ssh_opts.split() if isinstance(ssh_opts, str) else ssh_opts) + cmd.extend([command]) + + # There's a bit of a gap between the time that the machine is assigned an IP and the ssh + # service is up and listening, which creates a race for the ssh command. So we retry a + # couple of times until either we run out of attempts, or the ssh command succeeds to + # mitigate that effect. + retry_backoff = 2 + retries = 10 + for _ in range(retries): + process = await jasyncio.create_subprocess_exec( + *cmd, stdout=jasyncio.subprocess.PIPE, stderr=jasyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() + if process.returncode == 0: + break + await jasyncio.sleep(retry_backoff) + if process.returncode != 0: + raise JujuError( + f"command failed: {cmd} after {retries} attempts, with {stderr.decode()}" + ) + # stdout is a bytes-like object, returning a string might be more useful + return stdout.decode() + + @property + def addresses(self) -> typing.List[str]: + """Returns the machine addresses.""" + return self.safe_data["addresses"] or [] + + @property + def agent_status(self): + """Returns the current Juju agent status string.""" + return self.safe_data["agent-status"]["current"] + + @property + def agent_status_since(self): + """Get the time when the `agent_status` was last updated.""" + return pyrfc3339.parse(self.safe_data["agent-status"]["since"]) + + @property + def agent_version(self): + """Get the version of the Juju machine agent. + + May return None if the agent is not yet available. + """ + version = self.safe_data["agent-status"]["version"] + if version: + return client.Number.from_json(version) + else: + return None + + @property + def status(self): + """Returns the current machine provisioning status string.""" + return self.safe_data["instance-status"]["current"] + + @property + def status_message(self): + """Returns the current machine provisioning status message.""" + return self.safe_data["instance-status"]["message"] + + @property + def status_since(self): + """Get the time when the `status` was last updated.""" + return pyrfc3339.parse(self.safe_data["instance-status"]["since"]) + + @property + def dns_name(self): + """Get the DNS name for this machine. This is a best guess based on the + addresses available in current data. + + May return None if no suitable address is found. + """ + ordered_scopes = ["public", "local-cloud", "local-fan"] + ordered_addresses = [ + address + for scope in ordered_scopes + for address in self.addresses + if scope == address["scope"] + ] + + for address in ordered_addresses: + scope = address["scope"] + for check_scope in ordered_scopes: + if scope == check_scope: + return address["value"] + return None + + @property + def hostname(self): + """Get the hostname for this machine as reported by the machine agent + running on it. This is only supported on 2.8.10+ controllers. + + May return None if no hostname information is available. + """ + if "hostname" in self.safe_data and self.safe_data["hostname"] != "": + return self.safe_data["hostname"] + return None + + @property + def series(self): + """Returns the series of the current machine""" + return self.safe_data["series"] + + @property + def tag(self): + return tag.machine(self.id) diff --git a/build/lib/juju/model.py b/build/lib/juju/model.py new file mode 100644 index 00000000..c0f0758e --- /dev/null +++ b/build/lib/juju/model.py @@ -0,0 +1,3359 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +import base64 +import collections +import hashlib +import json +import logging +import os +import re +import stat +import sys +import tempfile +import warnings +import weakref +import zipfile +from concurrent.futures import CancelledError +from datetime import datetime, timedelta +from functools import partial +from pathlib import Path +from typing import TYPE_CHECKING, Any, Literal, Mapping, overload + +import websockets +import yaml +from typing_extensions import deprecated + +from . import jasyncio, provisioner, tag, utils +from .annotationhelper import _get_annotations, _set_annotations +from .bundle import BundleHandler, get_charm_series, is_local_charm +from .charmhub import CharmHub +from .client import client, connection, connector +from .client.overrides import Caveat, Macaroon +from .constraints import parse as parse_constraints +from .constraints import parse_storage_constraints +from .controller import ConnectedController, Controller +from .delta import get_entity_class, get_entity_delta +from .errors import ( + JujuAgentError, + JujuAPIError, + JujuAppError, + JujuBackupError, + JujuError, + JujuMachineError, + JujuModelConfigError, + JujuModelError, + JujuNotSupportedError, + JujuUnitError, + PylibjujuError, +) +from .exceptions import DeadEntityException +from .names import is_valid_application +from .offerendpoints import ParseError as OfferParseError +from .offerendpoints import parse_local_endpoint, parse_offer_url +from .origin import Channel, Source +from .placement import parse as parse_placement +from .secrets import create_secret_data, read_secret_data +from .tag import application as application_tag +from .url import URL, Schema +from .version import DEFAULT_ARCHITECTURE + +if TYPE_CHECKING: + from .application import Application + from .client._definitions import FullStatus + from .constraints import StorageConstraintDict + from .machine import Machine + from .relation import Relation + from .remoteapplication import ApplicationOffer, RemoteApplication + from .unit import Unit + +log = logging.getLogger(__name__) + + +class _Observer: + """Wrapper around an observer callable. + + This wrapper allows filter criteria to be associated with the + callable so that it's only called for changes that meet the criteria. + + """ + + def __init__(self, callable_, entity_type, action, entity_id, predicate): + self.callable_ = callable_ + self.entity_type = entity_type + self.action = action + self.entity_id = entity_id + self.predicate = predicate + if self.entity_id: + self.entity_id = str(self.entity_id) + if not self.entity_id.startswith("^"): + self.entity_id = "^" + self.entity_id + if not self.entity_id.endswith("$"): + self.entity_id += "$" + + async def __call__(self, delta, old, new, model): + await self.callable_(delta, old, new, model) + + def cares_about(self, delta): + """Return True if this observer "cares about" (i.e. wants to be + called) for a this delta. + + """ + return not ( + ( + self.entity_id + and delta.get_id() + and not re.match(self.entity_id, str(delta.get_id())) + ) + or (self.entity_type and self.entity_type != delta.entity) + or (self.action and self.action != delta.type) + or (self.predicate and not self.predicate(delta)) + ) + + +class ModelObserver: + """Base class for creating observers that react to changes in a model.""" + + async def __call__(self, delta, old, new, model): + handler_name = f"on_{delta.entity}_{delta.type}" + method = getattr(self, handler_name, self.on_change) + await method(delta, old, new, model) + + async def on_change(self, delta, old, new, model): + """Generic model-change handler. + + This should be overridden in a subclass. + + :param delta: :class:`juju.client.overrides.Delta` + :param old: :class:`juju.model.ModelEntity` + :param new: :class:`juju.model.ModelEntity` + :param model: :class:`juju.model.Model` + + """ + pass + + +class ModelState: + """Holds the state of the model, including the delta history of all + entities in the model. + + """ + + def __init__(self, model): + self.model = model + self.state = dict() + + @overload + def _live_entity_map( + self, entity_type: Literal["application"] + ) -> dict[str, Application]: ... + + @overload + def _live_entity_map( + self, entity_type: Literal["applicationOffer"] + ) -> dict[str, ApplicationOffer]: ... + + @overload + def _live_entity_map( + self, entity_type: Literal["machine"] + ) -> dict[str, Machine]: ... + + @overload + def _live_entity_map( + self, entity_type: Literal["relation"] + ) -> dict[str, Relation]: ... + + @overload + def _live_entity_map( + self, entity_type: Literal["remoteApplication"] + ) -> dict[str, RemoteApplication]: ... + + @overload + def _live_entity_map(self, entity_type: Literal["unit"]) -> dict[str, Unit]: ... + + def _live_entity_map(self, entity_type: str) -> Mapping[str, ModelEntity]: + """Return an id:Entity map of all the living entities of + type ``entity_type``. + + """ + return { + entity_id: self.get_entity(entity_type, entity_id) + for entity_id, history in self.state.get(entity_type, {}).items() + if history[-1] is not None + } + + @property + def applications(self) -> dict[str, Application]: + """Return a map of application-name:Application for all applications + currently in the model. + + """ + return self._live_entity_map("application") + + @property + def remote_applications(self) -> dict[str, RemoteApplication]: + """Return a map of application-name:Application for all remote + applications currently in the model. + + """ + return self._live_entity_map("remoteApplication") + + @property + def application_offers(self) -> dict[str, ApplicationOffer]: + """Return a map of application-name:Application for all applications + offers currently in the model. + """ + return self._live_entity_map("applicationOffer") + + @property + def machines(self) -> dict[str, Machine]: + """Return a map of machine-id:Machine for all machines currently in + the model. + + """ + return self._live_entity_map("machine") + + @property + def units(self) -> dict[str, Unit]: + """Return a map of unit-id:Unit for all units currently in + the model. + + """ + return self._live_entity_map("unit") + + @property + def subordinate_units(self) -> dict[str, Unit]: + """Return a map of unit-id:Unit for all subordinate units""" + return {u_name: u for u_name, u in self.units.items() if u.is_subordinate} + + @property + def relations(self) -> dict[str, Relation]: + """Return a map of relation-id:Relation for all relations currently in + the model. + + """ + return self._live_entity_map("relation") + + def entity_history(self, entity_type, entity_id): + """Return the history deque for an entity.""" + return self.state[entity_type][entity_id] + + def entity_data(self, entity_type, entity_id, history_index): + """Return the data dict for an entity at a specific index of its + history. + + """ + return self.entity_history(entity_type, entity_id)[history_index] + + def apply_delta(self, delta): + """Apply delta to our state and return a copy of the + affected object as it was before and after the update, e.g.: + + old_obj, new_obj = self.apply_delta(delta) + + old_obj may be None if the delta is for the creation of a new object, + e.g. a new application or unit is deployed. + + new_obj will never be None, but may be dead (new_obj.dead == True) + if the object was deleted as a result of the delta being applied. + + """ + history = self.state.setdefault(delta.entity, {}).setdefault( + delta.get_id(), collections.deque() + ) + + history.append(delta.data) + if delta.type == "remove": + history.append(None) + + entity = self.get_entity(delta.entity, delta.get_id()) + return entity.previous(), entity + + def get_entity( + self, entity_type, entity_id, history_index=-1, connected=True + ) -> ModelEntity | None: + """Return an object instance for the given entity_type and id. + + By default the object state matches the most recent state from + Juju. To get an instance of the object in an older state, pass + history_index, an index into the history deque for the entity. + + """ + if history_index < 0 and history_index != -1: + history_index += len(self.entity_history(entity_type, entity_id)) + if history_index < 0: + return None + + try: + self.entity_data(entity_type, entity_id, history_index) + except IndexError: + return None + + entity_class = get_entity_class(entity_type) + return entity_class( + entity_id, self.model, history_index=history_index, connected=connected + ) + + +class ModelEntity: + """An object in the Model tree""" + + entity_id: str + model: Model + _history_index: int + connected: bool + connection: connection.Connection + _status: str + + def __init__( + self, + entity_id: str, + model: Model, + history_index: int = -1, + connected: bool = True, + ): + """Initialize a new entity + + :param entity_id str: The unique id of the object in the model + :param model: The model instance in whose object tree this + entity resides + :history_index int: The index of this object's state in the model's + history deque for this entity + :connected bool: Flag indicating whether this object gets live updates + from the model. + + """ + self.entity_id = entity_id + self.model = model + self._history_index = history_index + self.connected = connected + self.connection = model.connection() + self._status = "unknown" + + def __repr__(self): + return f'<{type(self).__name__} entity_id="{self.entity_id}">' + + def __getattr__(self, name: str) -> Any: + """Fetch object attributes from the underlying data dict held in the + model. + + """ + try: + return self.safe_data[name] + except KeyError: + name = name.replace("_", "-") + if name in self.safe_data: + return self.safe_data[name] + else: + raise + + def __bool__(self): + return bool(self.data) + + def on_change(self, callable_): + """Add a change observer to this entity.""" + self.model.add_observer(callable_, self.entity_type, "change", self.entity_id) + + def on_remove(self, callable_): + """Add a remove observer to this entity.""" + self.model.add_observer(callable_, self.entity_type, "remove", self.entity_id) + + @property + def entity_type(self): + """A string identifying the entity type of this object, e.g. + 'application' or 'unit', etc. + + """ + # Allow the overriding of entity names from the type instead of from + # the class name. Useful because Model and ModelInfo clash and we really + # want ModelInfo to be called Model. + if hasattr(self.__class__, "type_name_override") and callable( + self.__class__.type_name_override + ): + return self.__class__.type_name_override() + + def first_lower(s): + if len(s) == 0: + return s + else: + return s[0].lower() + s[1:] + + return first_lower(self.__class__.__name__) + + @property + def current(self): + """Return True if this object represents the current state of the + entity in the underlying model. + + This will be True except when the object represents an entity at a + non-latest state in history, e.g. if the object was obtained by calling + .previous() on another object. + + """ + return self._history_index == -1 + + @property + def dead(self): + """Returns True if this entity no longer exists in the underlying + model. + + """ + return ( + self.data is None + or self.model.state.entity_data(self.entity_type, self.entity_id, -1) + is None + ) + + @property + def alive(self): + """Returns True if this entity still exists in the underlying + model. + + """ + return not self.dead + + @property + def data(self): + """The data dictionary for this entity.""" + return self.model.state.entity_data( + self.entity_type, self.entity_id, self._history_index + ) + + @property + def safe_data(self): + """The data dictionary for this entity. + + If this `ModelEntity` points to the dead state, it will + raise `DeadEntityException`. + + """ + if self.data is None: + raise DeadEntityException( + f"Entity {self.entity_type}:{self.entity_id} is dead - its attributes can no longer be " + "accessed. Use the .previous() method on this object to get " + "a copy of the object at its previous state." + ) + return self.data + + def previous(self): + """Return a copy of this object as was at its previous state in + history. + + Returns None if this object is new (and therefore has no history). + + The returned object is always "disconnected", i.e. does not receive + live updates. + + """ + return self.model.state.get_entity( + self.entity_type, self.entity_id, self._history_index - 1, connected=False + ) + + def next(self): + """Return a copy of this object at its next state in + history. + + Returns None if this object is already the latest. + + The returned object is "disconnected", i.e. does not receive + live updates, unless it is current (latest). + + """ + if self._history_index == -1: + return None + + new_index = self._history_index + 1 + connected = ( + new_index + == len(self.model.state.entity_history(self.entity_type, self.entity_id)) + - 1 + ) + return self.model.state.get_entity( + self.entity_type, + self.entity_id, + self._history_index - 1, + connected=connected, + ) + + def latest(self): + """Return a copy of this object at its current state in the model. + + Returns self if this object is already the latest. + + The returned object is always "connected", i.e. receives + live updates from the model. + + """ + if self._history_index == -1: + return self + + return self.model.state.get_entity(self.entity_type, self.entity_id) + + +class DeployTypeResult: + """DeployTypeResult represents the result of a deployment type after a + resolution. + """ + + def __init__(self, identifier, origin, app_name, is_local=False, is_bundle=False): + self.identifier = identifier + self.origin = origin + self.app_name = app_name + self.is_local = is_local + self.is_bundle = is_bundle + + +class LocalDeployType: + """LocalDeployType deals with local only deployments.""" + + async def resolve( + self, + charm_path, + architecture, + app_name=None, + channel=None, + series=None, + revision=None, + entity_url=None, + force=False, + model_conf=None, + ): + """Resolve attempts to resolve a local charm or bundle using the url + and architecture. If information is missing, it will attempt to backfill + that information, before sending the result back. + + -- revision flag is ignored for local charms + """ + entity_path = Path(charm_path) + if entity_path.suffix == ".yaml": + bundle_path = entity_path + else: + bundle_path = entity_path / "bundle.yaml" + + origin = client.CharmOrigin(source="local", architecture=architecture) + if not (entity_path.is_dir() or entity_path.is_file()): + raise JujuError(f"{entity_url} path not found") + + is_bundle = bundle_path.exists() + + if app_name is None: + if is_bundle: + bundle_with_overlays = [ + b for b in yaml.safe_load_all(bundle_path.read_text()) + ] + app_name = bundle_with_overlays[0].get("name", "") + else: + app_name = utils.get_local_charm_metadata(entity_path)["name"] + + return DeployTypeResult( + identifier=charm_path, + origin=origin, + app_name=app_name, + is_local=True, + is_bundle=is_bundle, + ) + + +class CharmhubDeployType: + """CharmhubDeployType defines a class for resolving and deploying charmhub + charms and bundles. + """ + + def __init__(self, charm_resolver): + self.charm_resolver = charm_resolver + + async def resolve( + self, + url, + architecture, + app_name=None, + channel=None, + series=None, + revision=None, + entity_url=None, + force=False, + model_conf=None, + ): + """Resolve attempts to resolve charmhub charms or bundles. A request to + the charmhub API is required to correctly determine the charm url and + underlying origin. + """ + if revision and not channel: + raise JujuError( + "specifying a revision requires a channel for future upgrades. Please use --channel" + ) + + ch = Channel("latest", "stable") + if channel is not None: + ch = Channel.parse(channel).normalize() + + base = client.Base() + if series: + base.channel = ch.normalize().compute_base_channel(series=series) + base.name = "ubuntu" + + origin = client.CharmOrigin( + source=Source.CHARM_HUB.value, + architecture=architecture, + risk=ch.risk, + track=ch.track, + base=base, + revision=revision, + ) + + charm_url, origin = await self.charm_resolver( + url, origin, force, series, model_conf + ) + + is_bundle = origin.type_ == "bundle" + if is_bundle and revision and channel: + raise JujuError( + "revision and channel are mutually exclusive when deploying a bundle. Please choose one." + ) + + if app_name is None: + app_name = charm_url.name + + return DeployTypeResult( + identifier=str(charm_url), + app_name=app_name, + origin=origin, + is_bundle=is_bundle, + ) + + +class Model: + """The main API for interacting with a Juju model.""" + + connector: connector.Connector + state: ModelState + + def __init__( + self, + max_frame_size=None, + bakery_client=None, + jujudata=None, + ): + """Instantiate a new Model. + + The connect method will need to be called before this + object can be used for anything interesting. + + If jujudata is None, jujudata.FileJujuData will be used. + + :param max_frame_size: See + `juju.client.connection.Connection.MAX_FRAME_SIZE` + :param bakery_client httpbakery.Client: The bakery client to use + for macaroon authorization. + :param jujudata JujuData: The source for current controller information + """ + self._connector = connector.Connector( + max_frame_size=max_frame_size, + bakery_client=bakery_client, + jujudata=jujudata, + ) + self._observers = weakref.WeakValueDictionary() + self.state = ModelState(self) + self._info = None + self._mode = None + self._watch_stopping = jasyncio.Event() + self._watch_stopped = jasyncio.Event() + self._watch_received = jasyncio.Event() + self._watch_stopped.set() + + self._charmhub = CharmHub(self) + + self.deploy_types = { + Schema.LOCAL: LocalDeployType(), + Schema.CHARM_HUB: CharmhubDeployType(self._resolve_charm), + } + + def is_connected(self): + """Reports whether the Model is currently connected.""" + return self._connector.is_connected() + + def connection(self) -> connection.Connection: + """Return the current Connection object. It raises an exception + if the Model is disconnected + """ + return self._connector.connection() + + async def get_controller(self): + """Return a Controller instance for the currently connected model. + :return Controller: + """ + from juju.controller import Controller + + controller = Controller(jujudata=self._connector.jujudata) + kwargs = self.connection().connect_params() + kwargs.pop("uuid") + await controller._connect_direct(**kwargs) + return controller + + async def __aenter__(self): + await self.connect() + return self + + async def __aexit__(self, exc_type, exc, tb): + await self.disconnect() + + async def connect(self, *args, **kwargs): + """Connect to a juju model. + + This supports two calling conventions: + + The model and (optionally) authentication information can be taken + from the data files created by the Juju CLI. This convention will + be used if a ``model_name`` is specified, or if the ``endpoint`` + and ``uuid`` are not. + + Otherwise, all of the ``endpoint``, ``uuid``, and authentication + information (``username`` and ``password``, or ``bakery_client`` and/or + ``macaroons``) are required. + + If a single positional argument is given, it will be assumed to be + the ``model_name``. Otherwise, the first positional argument, if any, + must be the ``endpoint``. + + Available parameters are: + + :param model_name: Format [controller:][user/]model + :param str endpoint: The hostname:port of the controller to connect to. + :param str uuid: The model UUID to connect to. + :param str username: The username for controller-local users (or None + to use macaroon-based login.) + :param str password: The password for controller-local users. + :param str cacert: The CA certificate of the controller + (PEM formatted). + :param httpbakery.Client bakery_client: The macaroon bakery client to + to use when performing macaroon-based login. Macaroon tokens + acquired when logging will be saved to bakery_client.cookies. + If this is None, a default bakery_client will be used. + :param list macaroons: List of macaroons to load into the + ``bakery_client``. + :param int max_frame_size: The maximum websocket frame size to allow. + :param specified_facades: (deprecated) overwrite the facades with a series of + specified facades. + """ + is_debug_log_conn = "debug_log_conn" in kwargs + if not is_debug_log_conn: + await self.disconnect() + model_name = model_uuid = None + if "endpoint" not in kwargs and len(args) < 2: + # Then we're using the model_name to pick the model + if args and "model_name" in kwargs: + raise TypeError("connect() got multiple values for model_name") + elif args: + model_name = args[0] + else: + model_name = kwargs.pop("model_name", None) + model_uuid = await self._connector.connect_model(model_name, **kwargs) + else: + # Then we're using the endpoint to pick the model + if "model_name" in kwargs: + raise TypeError("connect() got values for both model_name and endpoint") + if args and "endpoint" in kwargs: + raise TypeError("connect() got multiple values for endpoint") + if len(args) < 2 and "uuid" not in kwargs: + raise TypeError("connect() missing value for uuid") + has_userpass = len(args) >= 4 or {"username", "password"}.issubset(kwargs) + has_macaroons = len(args) >= 6 or not { + "bakery_client", + "macaroons", + }.isdisjoint(kwargs) + if not (has_userpass or has_macaroons): + raise TypeError("connect() missing auth params") + arg_names = [ + "endpoint", + "uuid", + "username", + "password", + "cacert", + "bakery_client", + "macaroons", + "max_frame_size", + ] + for i, arg in enumerate(args): + kwargs[arg_names[i]] = arg + model_uuid = kwargs["uuid"] + if not {"endpoint", "uuid"}.issubset(kwargs): + raise ValueError( + "endpoint and uuid are required if model_name not given" + ) + if not ( + {"username", "password"}.issubset(kwargs) + or {"bakery_client", "macaroons"}.intersection(kwargs) + ): + raise ValueError( + "Authentication parameters are required if model_name not given" + ) + await self._connector.connect(**kwargs) + if not is_debug_log_conn: + await self._after_connect(model_name, model_uuid) + + async def connect_model(self, model_name, **kwargs): + """.. deprecated:: 0.6.2 + Use ``connect(model_name=model_name)`` instead. + """ + return await self.connect(model_name=model_name, **kwargs) + + async def connect_current(self): + """.. deprecated:: 0.6.2 + Use ``connect()`` instead. + """ + return await self.connect() + + @deprecated("Model.connect_to() is deprecated and will be removed soon") + async def connect_to(self, connection): + conn_params = connection.connect_params() + await self._connect_direct(**conn_params) + + async def _connect_direct(self, **kwargs): + if self._info: + uuid = self._info.uuid + elif "uuid" in kwargs: + uuid = kwargs["uuid"] + else: + raise PylibjujuError("Unable to find uuid for the model") + await self.disconnect() + await self._connector.connect(**kwargs) + await self._after_connect(model_uuid=uuid) + + async def _after_connect(self, model_name=None, model_uuid=None): + self._watch() + + # Wait for the first packet of data from the AllWatcher, + # which contains all information on the model. + # TODO this means that we can't do anything until + # we've received all the model data, which might be + # a whole load of unneeded data if all the client wants + # to do is make one RPC call. + async def watch_received_waiter(): + await self._watch_received.wait() + + waiter = jasyncio.create_task(watch_received_waiter()) + + # If we just wait for the _watch_received event and the _all_watcher task + # fails (e.g. because API fails like migration is in progress), then + # we'll hang because the _watch_received will never be set + # Instead, we watch for two things, 1) _watch_received, 2) _all_watcher done + # If _all_watcher is done before the _watch_received, then we should see + # (and raise) an exception coming from the _all_watcher + # Otherwise (i.e. _watch_received is set), then we're good to go + done, _pending = await jasyncio.wait( + [waiter, self._watcher_task], return_when=jasyncio.FIRST_COMPLETED + ) + if self._watcher_task in done: + # Cancel the _watch_received.wait + waiter.cancel() + # If there's no exception, then why did the _all_watcher broke its loop? + if not self._watcher_task.exception(): + raise JujuError( + "AllWatcher task is finished abruptly without an exception." + ) + raise self._watcher_task.exception() + + if self._info is None: + # TODO (cderici): See if this can be optimized away, or at least + # be done lazily (i.e. not every time after_connect, but whenever + # self.info is needed -- which here can be bypassed if model_uuid + # is known) + async with ConnectedController(self.connection()) as contr: + self._info = await contr.get_model_info(model_name, model_uuid) + log.debug("Got ModelInfo: %s", vars(self.info)) + + self.uuid = self.info.uuid + + async def disconnect(self): + """Shut down the watcher task and close websockets.""" + if not self._watch_stopped.is_set(): + log.debug("Stopping watcher task") + self._watch_stopping.set() + # If the _all_watcher task is finished, + # check to see an exception, if yes, raise, + # otherwise we should see the _watch_stopped + # flag is set + if self._watcher_task.done() and self._watcher_task.exception(): + raise self._watcher_task.exception() + await self._watch_stopped.wait() + self._watch_stopping.clear() + + if self.is_connected(): + await self._connector.disconnect(entity="model") + self._info = None + + async def add_local_charm_dir(self, charm_dir, series): + """Upload a local charm to the model. + + This will automatically generate an archive from + the charm dir. + + :param charm_dir: Path to the charm directory + :param series: Charm series + + """ + charm_dir = Path(charm_dir) + if charm_dir.suffix == ".charm": + fn = charm_dir + else: + # FIXME this is probably a bug, the file is never removed + fn = tempfile.NamedTemporaryFile().name # noqa: SIM115 + CharmArchiveGenerator(str(charm_dir)).make_archive(fn) + with open(str(fn), "rb") as fh: + func = partial(self.add_local_charm, fh, series, os.stat(str(fn)).st_size) + loop = jasyncio.get_running_loop() + charm_url = await loop.run_in_executor(None, func) + + log.debug("Uploaded local charm: %s -> %s", charm_dir, charm_url) + return charm_url + + def add_local_charm(self, charm_file, series="", size=None): + """Upload a local charm archive to the model. + + Returns the 'local:...' url that should be used to deploy the charm. + + :param charm_file: Path to charm zip archive + :param series: Charm series + :param size: Size of the archive, in bytes + :return str: 'local:...' url for deploying the charm + :raises: :class:`JujuError` if the upload fails + + Uses an https endpoint at the same host:port as the wss. + Supports large file uploads. + + .. warning:: + + This method will block. Consider using :meth:`add_local_charm_dir` + instead. + + """ + conn, headers, path_prefix = self.connection().https_connection() + path = "%s/charms?series=%s" % (path_prefix, series) + headers["Content-Type"] = "application/zip" + if size: + headers["Content-Length"] = size + conn.request("POST", path, charm_file, headers) + response = conn.getresponse() + result = response.read().decode() + if not response.status == 200: + raise JujuError(result) + result = json.loads(result) + return result["charm-url"] + + def all_units_idle(self): + """Return True if all units are idle.""" + for unit in self.units.values(): + unit_status = unit.data["agent-status"]["current"] + if unit_status != "idle": + return False + return True + + async def reset(self, force=False): + """Reset the model to a clean state. + + :param bool force: Force-terminate machines. + + This returns only after the model has reached a clean state. "Clean" + means no applications or machines exist in the model. + + """ + log.debug("Resetting model") + for app in self.applications.values(): + await app.destroy() + await self.block_until(lambda: len(self.applications) == 0) + for machine in self.machines.values(): + await machine.destroy(force=force) + await self.block_until(lambda: len(self.machines) == 0) + + async def create_storage_pool(self, name, provider_type, attributes=""): + """Create or define a storage pool. + + :param str name: a pool name + :param str provider_type: provider type (defaults to "kubernetes" for + Kubernetes models) + :param str attributes: attributes for configuration as space-separated pairs, + e.g. tags, size, path, etc. + :return: + """ + _attrs = [splt.split("=") for splt in attributes.split()] + + storage_facade = client.StorageFacade.from_connection(self.connection()) + return await storage_facade.CreatePool( + pools=[ + client.StoragePool( + name=name, provider=provider_type, attrs=dict(_attrs) + ) + ] + ) + + async def remove_storage_pool(self, name): + """Remove an existing storage pool. + + :param str name: + :return: + """ + storage_facade = client.StorageFacade.from_connection(self.connection()) + return await storage_facade.RemovePool( + pools=[client.StoragePoolDeleteArg(name)] + ) + + async def update_storage_pool(self, name, attributes=""): + """Update storage pool attributes. + + :param name: + :param attributes: "key=value key=value ..." + :return: + """ + _attrs = dict([splt.split("=") for splt in attributes.split()]) + if len(_attrs) == 0: + raise JujuError("Expected at least one attribute to update") + + storage_facade = client.StorageFacade.from_connection(self.connection()) + return await storage_facade.UpdatePool( + pools=[ + client.StoragePool( + name=name, + attrs=_attrs, + ) + ] + ) + + async def list_storage(self, filesystem=False, volume=False): + """Lists storage details. + + :param bool filesystem: List filesystem storage + :param bool volume: List volume storage + :return: + """ + storage_facade = client.StorageFacade.from_connection(self.connection()) + + if filesystem and volume: + raise JujuError("--filesystem and --volume can not be used together") + if filesystem: + _res = await storage_facade.ListFilesystems( + filters=[client.FilesystemFilter()] + ) + elif volume: + _res = await storage_facade.ListVolumes(filters=[client.VolumeFilter()]) + else: + _res = await storage_facade.ListStorageDetails( + filters=[client.StorageFilter()] + ) + + err = _res.results[0].error + res = _res.results[0].result + + if err is not None: + raise JujuError(err.message) + + return [details.serialize() for details in res] + + async def show_storage_details(self, *storage_ids): + """Shows storage instance information. + + :param []str storage_ids: + :return: + """ + if not storage_ids: + raise JujuError("Expected at least one storage ID") + + storage_facade = client.StorageFacade.from_connection(self.connection()) + res = await storage_facade.StorageDetails( + entities=[client.Entity(tag.storage(s)) for s in storage_ids] + ) + return [s.result.serialize() for s in res.results] + + async def list_storage_pools(self): + """List storage pools. + + :return: + """ + # TODO (cderici): Filter on pool type, name. + storage_facade = client.StorageFacade.from_connection(self.connection()) + res = await storage_facade.ListPools(filters=[client.StoragePoolFilter()]) + err = res.results[0].error + if err: + raise JujuError(err.message) + return [p.serialize() for p in res.results[0].storage_pools] + + async def remove_storage(self, *storage_ids, force=False, destroy_storage=False): + """Removes storage from the model. + + :param bool force: Remove storage even if it is currently attached + :param bool destroy_storage: Remove the storage and destroy it + :param []str storage_ids: + :return: + """ + if not storage_ids: + raise JujuError("Expected at least one storage ID") + + storage_facade = client.StorageFacade.from_connection(self.connection()) + ret = await storage_facade.Remove( + storage=[ + client.RemoveStorageInstance( + destroy_storage=destroy_storage, + force=force, + tag=s, + ) + for s in storage_ids + ] + ) + if ret.results[0].error: + raise JujuError(ret.results[0].error.message) + + async def remove_application( + self, + app_name, + block_until_done=False, + force=False, + destroy_storage=False, + no_wait=False, + timeout=None, + ): + """Removes the given application from the model. + + :param str app_name: Name of the application + :param bool force: Completely remove an application and all its dependencies. (=false) + :param bool destroy_storage: Destroy storage attached to application unit. (=false) + :param bool no_wait: Rush through application removal without waiting for each individual step to complete (=false) + :param bool block_until_done: Ensure the app is removed from the + model when returned + :param int timeout: Raise asyncio.exceptions.TimeoutError if the application is not removed + within the timeout period. + """ + if app_name not in self.applications: + raise JujuError( + "Given application doesn't seem to appear in the\ + model: %s\nCurrent applications are: %s" + % (app_name, self.applications) + ) + await self.applications[app_name].remove( + destroy_storage=destroy_storage, + force=force, + no_wait=no_wait, + ) + if block_until_done: + await self.block_until( + lambda: app_name not in self.applications, timeout=timeout + ) + + async def block_until(self, *conditions, timeout=None, wait_period=0.5): + """Return only after all conditions are true. + + Raises `websockets.ConnectionClosed` if disconnected. + """ + + def _disconnected(): + return not (self.is_connected() and self.connection().is_open) + + def done(): + return _disconnected() or all(c() for c in conditions) + + await utils.block_until(done, timeout=timeout, wait_period=wait_period) + if _disconnected(): + raise websockets.ConnectionClosed(1006, "no reason") + + @property + def tag(self): + return tag.model(self.uuid) + + @property + def applications(self) -> dict[str, Application]: + """Return a map of application-name:Application for all applications + currently in the model. + + """ + return self.state.applications + + @property + def remote_applications(self) -> dict[str, RemoteApplication]: + """Return a map of application-name:Application for all remote + applications currently in the model. + + """ + return self.state.remote_applications + + @property + def application_offers(self) -> dict[str, ApplicationOffer]: + """Return a map of application-name:Application for all applications + offers currently in the model. + """ + return self.state.application_offers + + @property + def machines(self) -> dict[str, Machine]: + """Return a map of machine-id:Machine for all machines currently in + the model. + + """ + return self.state.machines + + @property + def units(self) -> dict[str, Unit]: + """Return a map of unit-id:Unit for all units currently in + the model. + + """ + return self.state.units + + @property + def subordinate_units(self) -> dict[str, Unit]: + """Return a map of unit-id:Unit for all subordiante units currently in + the model. + + """ + return self.state.subordinate_units + + @property + def relations(self) -> list[Relation]: + """Return a list of all Relations currently in the model.""" + return list(self.state.relations.values()) + + @property + def charmhub(self): + """Return a charmhub repository for requesting charm information using + the charm-hub-url model config. + + """ + return self._charmhub + + @property + def name(self): + """Return the name of this model""" + if self._info is None: + raise JujuModelError("Model is not connected") + return self._info.name + + @property + def info(self) -> ModelInfo: + """Return the cached client.ModelInfo object for this Model. + + If Model.get_info() has not been called, this will raise an error. + """ + if not self.is_connected(): + raise JujuModelError("Model is not connected") + + assert self._info is not None + return self._info + + @property + def strict_mode(self): + return self._mode is not None and "strict" in self._mode + + def add_observer( + self, callable_, entity_type=None, action=None, entity_id=None, predicate=None + ): + """Register an "on-model-change" callback + + Once the model is connected, ``callable_`` + will be called each time the model changes. ``callable_`` should + be Awaitable and accept the following positional arguments: + + delta - An instance of :class:`juju.delta.EntityDelta` + containing the raw delta data recv'd from the Juju + websocket. + + old_obj - If the delta modifies an existing object in the model, + old_obj will be a copy of that object, as it was before the + delta was applied. Will be None if the delta creates a new + entity in the model. + + new_obj - A copy of the new or updated object, after the delta + is applied. Will be None if the delta removes an entity + from the model. + + model - The :class:`Model` itself. + + Events for which ``callable_`` is called can be specified by passing + entity_type, action, and/or entitiy_id filter criteria, e.g.:: + + add_observer( + myfunc, + entity_type='application', action='add', entity_id='ubuntu') + + For more complex filtering conditions, pass a predicate function. It + will be called with a delta as its only argument. If the predicate + function returns True, the ``callable_`` will be called. + + """ + observer = _Observer(callable_, entity_type, action, entity_id, predicate) + self._observers[observer] = callable_ + + def _watch(self): + """Start an asynchronous watch against this model. + + See :meth:`add_observer` to register an onchange callback. + + """ + + def _post_step(obj): + # Once we get the model, ensure we're running in the correct state + # as a post step. + if isinstance(obj, ModelInfo) and obj.safe_data is not None: + model_config = obj.safe_data["config"] + if "mode" in model_config: + self._mode = model_config["mode"] + + async def _all_watcher(): + # First attempt to get the model config so we know what mode the + # library should be running in. + model_config = await self.get_config() + if "mode" in model_config: + self._mode = model_config["mode"]["value"] + + try: + allwatcher = client.AllWatcherFacade.from_connection(self.connection()) + while not self._watch_stopping.is_set(): + try: + results = await utils.run_with_interrupt( + allwatcher.Next(), self._watch_stopping, log=log + ) + except JujuAPIError as e: + if "watcher was stopped" not in str(e): + raise + if self._watch_stopping.is_set(): + # this shouldn't ever actually happen, because + # the event should trigger before the controller + # has a chance to tell us the watcher is stopped + # but handle it gracefully, just in case + break + # controller stopped our watcher for some reason + # but we're not actually stopping, so just restart it + log.warning("Watcher: watcher stopped, restarting") + del allwatcher.Id + continue + except websockets.ConnectionClosed: + if self.connection().monitor.status == connection.Monitor.ERROR: + # closed unexpectedly, try to reopen + log.warning("Watcher: connection closed, reopening") + await self.connection().reconnect() + if ( + self.connection().monitor.status + != connection.Monitor.CONNECTED + ): + # reconnect failed; abort and shutdown + log.error( + "Watcher: automatic reconnect " + "failed; stopping watcher" + ) + break + del allwatcher.Id + continue + else: + # closed on request, go ahead and shutdown + break + if self._watch_stopping.is_set(): + try: + await allwatcher.Stop() + except websockets.ConnectionClosed: + pass # can't stop on a closed conn + break + for delta in results.deltas: + entity = None + try: + entity = get_entity_delta(delta) + except KeyError: + if self.strict_mode: + raise JujuError(f"unknown delta type '{delta.entity}'") + + if not self.strict_mode and entity is None: + continue + old_obj, new_obj = self.state.apply_delta(entity) + await self._notify_observers(entity, old_obj, new_obj) + # Post step ensure that we can handle any settings + # that need to be correctly set as a post step. + _post_step(new_obj) + self._watch_received.set() + except CancelledError: + pass + except Exception: + log.exception("Error in watcher") + raise + finally: + self._watch_stopped.set() + + log.debug("Starting watcher task") + self._watch_received.clear() + self._watch_stopping.clear() + self._watch_stopped.clear() + self._watcher_task = jasyncio.create_task(_all_watcher()) + + async def _notify_observers(self, delta, old_obj, new_obj): + """Call observing callbacks, notifying them of a change in model state + + :param delta: The raw change from the watcher + (:class:`juju.client.overrides.Delta`) + :param old_obj: The object in the model that this delta updates. + May be None. + :param new_obj: The object in the model that is created or updated + by applying this delta. + + """ + if new_obj and not old_obj: + delta.type = "add" + + log.debug("Model changed: %s %s %s", delta.entity, delta.type, delta.get_id()) + + for o in self._observers: + if o.cares_about(delta): + jasyncio.ensure_future(o(delta, old_obj, new_obj, self)) + + async def _wait(self, entity_type, entity_id, action, predicate=None): + """Block the calling routine until a given action has happened to the + given entity + + :param entity_type: The entity's type. + :param entity_id: The entity's id. + :param action: the type of action (e.g., 'add', 'change', or 'remove') + :param predicate: optional callable that must take as an + argument a delta, and must return a boolean, indicating + whether the delta contains the specific action we're looking + for. For example, you might check to see whether a 'change' + has a 'completed' status. See the _Observer class for details. + + """ + q = jasyncio.Queue() + + async def callback(delta, old, new, model): + await q.put(delta.get_id()) + + self.add_observer(callback, entity_type, action, entity_id, predicate) + entity_id = await q.get() + # object might not be in the entity_map if we were waiting for a + # 'remove' action + return self.state._live_entity_map(entity_type).get(entity_id) + + async def _wait_for_new(self, entity_type, entity_id): + """Wait for a new object to appear in the Model and return it. + + Waits for an object of type ``entity_type`` with id ``entity_id`` + to appear in the model. This is similar to watching for the + object using ``block_until``, but uses the watcher rather than + polling. + + """ + # if the entity is already in the model, just return it + if entity_id in self.state._live_entity_map(entity_type): + return self.state._live_entity_map(entity_type)[entity_id] + return await self._wait(entity_type, entity_id, None) + + async def wait_for_action(self, action_id): + """Given an action, wait for it to complete.""" + if action_id.startswith("action-"): + # if we've been passed action.tag, transform it into the + # id that the api deltas will use. + action_id = action_id[7:] + + def predicate(delta): + return delta.data["status"] in ("completed", "failed") + + return await self._wait("action", action_id, None, predicate) + + async def get_annotations(self): + """Get annotations on this model. + + :return dict: The annotations for this model + """ + return await _get_annotations(self.tag, self.connection()) + + async def set_annotations(self, annotations): + """Set annotations on this model. + + :param annotations map[string]string: the annotations as key/value + pairs. + + """ + return await _set_annotations(self.tag, annotations, self.connection()) + + async def add_machine( + self, spec: str | None = None, constraints=None, disks=None, series=None + ): + """Start a new, empty machine and optionally a container, or add a + container to a machine. + + :param str spec: Machine specification + Examples:: + + (None) - starts a new machine + 'lxd' - starts a new machine with one lxd container + 'lxd:4' - starts a new lxd container on machine 4 + 'ssh:user@10.10.0.3:/path/to/private/key' - manually provision + a machine with ssh and the private key used for authentication + 'zone=us-east-1a' - starts a machine in zone us-east-1s on AWS + 'maas2.name' - acquire machine maas2.name on MAAS + + :param dict constraints: Machine constraints, which can contain the + the following keys:: + + arch : str + container : str + cores : int + cpu_power : int + instance_type : str + mem : int + root_disk : int + spaces : list(str) + tags : list(str) + virt_type : str + + Example:: + + constraints={ + 'mem': 256 * MB, + 'tags': ['virtual'], + } + + :param list disks: List of disk constraint dictionaries, which can + contain the following keys:: + + count : int + pool : str + size : int + + Example:: + + disks=[{ + 'pool': 'rootfs', + 'size': 10 * GB, + 'count': 1, + }] + + :param str series: Series, e.g. 'xenial' + + Supported container types are: lxd, kvm + + When deploying a container to an existing machine, constraints cannot + be used. + + """ + params = client.AddMachineParams() + + if spec: + if spec.startswith("ssh:"): + placement, target, private_key_path = spec.split(":") + user, host = target.split("@") + + prov = provisioner.SSHProvisioner( + host=host, + user=user, + private_key_path=private_key_path, + ) + + params = prov.provision_machine() + else: + placement = parse_placement(spec) + if placement: + params.placement = placement[0] + + params.jobs = ["JobHostUnits"] + + if constraints: + params.constraints = client.Value.from_json(constraints) + + if disks: + params.disks = [client.Constraints.from_json(o) for o in disks] + + if series: + params.series = series + + # Submit the request. + client_facade = client.MachineManagerFacade.from_connection(self.connection()) + results = await client_facade.AddMachines(params=[params]) + error = results.machines[0].error + if error: + raise ValueError("Error adding machine: %s" % error.message) + machine_id = results.machines[0].machine + + if spec and spec.startswith("ssh:"): + # Need to run this after AddMachines has been called, + # as we need the machine_id + await prov.install_agent( + self.connection(), + params.nonce, + machine_id, + ) + + log.debug("Added new machine %s", machine_id) + return await self._wait_for_new("machine", machine_id) + + async def add_relation(self, relation1, relation2): + """.. deprecated:: 2.9.9 + Use ``relate()`` instead + """ + return await self.relate(relation1, relation2) + + async def integrate(self, relation1, relation2): + """Add a relation between two applications. + + :param str relation1: '[:]' + :param str relation2: '[:]' + + """ + # attempt to validate any url that are passed in. + endpoints = [] + remote_endpoint = None + for ep in [relation1, relation2]: + try: + url = parse_offer_url(ep) + except OfferParseError: + pass + else: + if remote_endpoint is not None: + raise JujuError("move than one remote endpoints not supported") + remote_endpoint = url + endpoints.append(url.application) + continue + + try: + parse_local_endpoint(ep) + except OfferParseError: + raise + else: + endpoints.append(ep) + if len(endpoints) != 2: + raise JujuError("error validating one of the endpoints") + + facade_cls = client.ApplicationFacade + if remote_endpoint is not None: + if facade_cls.best_facade_version(self.connection()) < 5: + # old clients don't support cross model capability + raise JujuError( + f"cannot add relation to {remote_endpoint.string()}: remote endpoints not supported" + ) + + if remote_endpoint.has_empty_source(): + async with ConnectedController(self.connection()) as current: + remote_endpoint.source = current.controller_name + # consume the remote endpoint + await self.consume( + remote_endpoint.string(), application_alias=remote_endpoint.application + ) + + log.debug("Adding relation %s <-> %s", endpoints[0], endpoints[1]) + + def _find_relation(*specs): + for rel in self.relations: + if rel.matches(*specs): + return rel + return None + + app_facade = facade_cls.from_connection(self.connection()) + try: + result = await app_facade.AddRelation(endpoints=endpoints, via_cidrs=None) + except JujuAPIError as e: + if "relation already exists" not in e.message: + raise + rel = _find_relation(endpoints[0], endpoints[1]) + if rel: + return rel + raise JujuError( + f"Relation {endpoints[0]} {endpoints[1]} exists but not in model" + ) + + specs = [ + "{}:{}".format(app, data["name"]) for app, data in result.endpoints.items() + ] + + await self.block_until(lambda: _find_relation(*specs) is not None) + return _find_relation(*specs) + + async def relate(self, relation1, relation2): + """The relate function is deprecated in favor of integrate. + + The logic is the same. + """ + log.warning("relate is deprecated and will be removed. Use integrate instead.") + return await self.integrate(relation1, relation2) + + async def add_space(self, name, cidrs=None, public=True): + """Add a new network space. + + Adds a new space with the given name and associates the given + (optional) list of existing subnet CIDRs with it. + + :param str name: Name of the space + :param List[str] cidrs: Optional list of existing subnet CIDRs + """ + space_facade = client.SpacesFacade.from_connection(self.connection()) + spaces = [{"cidrs": cidrs, "space-tag": tag.space(name), "public": public}] + return await space_facade.CreateSpaces(spaces=spaces) + + async def add_ssh_key(self, user, key): + """Add a public SSH key to this model. + + :param str user: The username of the user + :param str key: The public ssh key + + """ + key_facade = client.KeyManagerFacade.from_connection(self.connection()) + return await key_facade.AddKeys(ssh_keys=[key], user=user) + + add_ssh_keys = add_ssh_key + + async def get_backups(self): + """Retrieve metadata for backups in this model. + + :return [dict]: List of metadata for the stored backups + """ + backups_facade = client.BackupsFacade.from_connection(self.connection()) + _backups_metadata = await backups_facade.List() + backups_metadata = _backups_metadata.serialize() + if "list" not in backups_metadata: + raise JujuAPIError("Unexpected response metadata : %s" % backups_metadata) + return backups_metadata["list"] + + async def create_backup(self, notes=None): + """Create a backup of this model. + + :param str note: A note to store with the backup + :param bool keep_copy: Keep a copy of the archive on the controller + :param bool no_download: Do not download the backup archive + :return str, dict: Filename for the downloaded archive file, Extra metadata for the created backup + + """ + backups_facade = client.BackupsFacade.from_connection(self.connection()) + results = await backups_facade.Create(notes=notes) + + if results is None: + raise JujuAPIError("unable to create a backup") + + backup_metadata = results.serialize() + + if "error" in backup_metadata: + raise JujuBackupError( + "unable to create a backup, got %s from Juju API" % backup_metadata + ) + + backup_id = backup_metadata["filename"] + + file_name = self.download_backup(backup_id) + + return file_name, backup_metadata + + async def debug_log( + self, + target=sys.stdout, + no_tail=False, + exclude_module=[], + include_module=[], + include=[], + level="", + limit=sys.maxsize, + lines=10, + exclude=[], + ): + """Get log messages for this model. + + :param bool no_tail: Stop after returning existing log messages + :param list exclude_module: Do not show log messages for these logging + modules + :param list include_module: Only show log messages for these logging + modules + :param list include: Only show log messages for these entities + :param str level: Log level to show, valid options are 'TRACE', + 'DEBUG', 'INFO', 'WARNING', 'ERROR, + :param int limit: Return this many of the most recent (possibly + filtered) lines are shown + :param int lines: Yield this many of the most recent lines, and keep + yielding + :param list exclude: Do not show log messages for these entities + + """ + if not self.is_connected(): + await self.connect() + + params = { + "no_tail": no_tail, + "exclude_module": exclude_module, + "include_module": include_module, + "include": include, + "level": level, + "limit": limit, + "lines": lines, + "exclude": exclude, + } + await self.connect(debug_log_conn=target, debug_log_params=params) + + async def deploy( + self, + entity_url, + application_name=None, + bind=None, + channel=None, + config=None, + constraints=None, + force=False, + num_units=1, + overlays=[], + base=None, + resources=None, + series=None, + revision=None, + storage: Mapping[str, str | StorageConstraintDict] | None = None, + to=None, + devices=None, + trust=False, + attach_storage=[], + ): + """Deploy a new service or bundle. + + :param str entity_url: Charm or bundle to deploy. Charm url or file path + :param str application_name: Name to give the service + :param dict bind: : pairs + :param str channel: Charm store channel from which to retrieve + the charm or bundle, e.g. 'edge' + :param dict config: Charm configuration dictionary + :param constraints: Service constraints + :type constraints: :class:`juju.Constraints` + :param bool force: Allow charm to be deployed to a machine running + an unsupported series + :param int num_units: Number of units to deploy + :param [] overlays: Bundles to overlay on the primary bundle, applied in order + :param str base: The base on which to deploy + :param dict resources: : pairs + :param str series: Series on which to deploy DEPRECATED: use --base (with Juju 3.1) + :param int revision: specifying a revision requires a channel for future upgrades for charms. + For bundles, revision and channel are mutually exclusive. + :param dict storage: optional storage constraints, in the form of `{label: constraint}`. + The label is a string specified by the charm, while the constraint is + a constraints.StorageConstraintsDict, or a string following + `the juju storage constraint directive format `_, + specifying the storage pool, number of volumes, and size of each volume. + :param to: Placement directive as a string. For example: + + '23' - place on machine 23 + 'lxd:7' - place in new lxd container on machine 7 + '24/lxd/3' - place in container 3 on machine 24 + + If None, a new machine is provisioned. + :param devices: charm device constraints + :param bool trust: Trust signifies that the charm should be deployed + with access to trusted credentials. Hooks run by the charm can access + cloud credentials and other trusted access credentials. + + :param str[] attach_storage: Existing storage to attach to the deployed unit + (not available on k8s models) + """ + if trust and (self.info.agent_version < client.Number.from_json("2.4.0")): + raise NotImplementedError( + f"trusted is not supported on model version {self.info.agent_version}" + ) + + if not all([isinstance(st, str) for st in attach_storage]): + raise JujuError( + f"Expected attach_storage to be a list of strings, given {attach_storage}" + ) + + # Ensure what we pass in, is a string. + entity = str(entity_url) + if is_local_charm(entity): + if entity.startswith("local:"): + entity = entity[6:] + architecture = await self._resolve_architecture() + schema = Schema.LOCAL + + else: + if client.CharmsFacade.best_facade_version(self.connection()) < 3: + url = URL.parse(entity, default_store=Schema.CHARM_STORE) + else: + url = URL.parse(entity) + entity = str(url) + + architecture = await self._resolve_architecture(url) + schema = url.schema + name = url.name + + if schema not in self.deploy_types: + raise JujuError(f"unknown deploy type {schema}, expected charmhub or local") + + model_conf = await self.get_config() + res = await self.deploy_types[schema].resolve( + entity, + architecture, + application_name, + channel, + series, + revision, + entity_url, + force, + model_conf, + ) + + if res.identifier is None: + raise JujuError(f"unknown charm or bundle {entity_url}") + identifier = res.identifier + + charm_series = series + charm_origin = res.origin + if base: + charm_origin.base = utils.parse_base_arg(base) + + server_side_deploy = False + + if res.is_bundle: + handler = BundleHandler(self, trusted=trust, forced=force) + await handler.fetch_plan(entity, charm_origin, overlays=overlays) + await handler.execute_plan() + extant_apps = {app for app in self.applications} + pending_apps = handler.applications - extant_apps + if pending_apps: + # new apps will usually be in the model by now, but if some + # haven't made it yet we'll need to wait on them to be added + await jasyncio.gather(*[ + jasyncio.ensure_future(self._wait_for_new("application", app_name)) + for app_name in pending_apps + ]) + return [ + app + for name, app in self.applications.items() + if name in handler.applications + ] + else: + if overlays: + raise JujuError( + "options provided but not supported when deploying a charm: overlays=%s" + % overlays + ) + # XXX: we're dropping local resources here, but we don't + # actually support them yet anyway + if not res.is_local: + add_charm_res = await self._add_charm(identifier, charm_origin) + if isinstance(add_charm_res, dict): + # This is for backwards compatibility for older + # versions where AddCharm returns a dictionary + charm_origin = add_charm_res.get("charm_origin", charm_origin) + else: + charm_origin = add_charm_res.charm_origin + if Schema.CHARM_HUB.matches(schema): + if ( + client.ApplicationFacade.best_facade_version(self.connection()) + >= 19 + ): + server_side_deploy = True + else: + # TODO (cderici): this is an awkward workaround for basically not calling + # the AddPendingResources in case this is a server side deploy. + # If that's the case, then the store resources (and revisioned local + # resources) are handled at the server side if this is a server side deploy + # (local uploads are handled right after we get the pendingIDs returned + # from the facade call). + resources = await self._add_charmhub_resources( + res.app_name, identifier, add_charm_res.charm_origin + ) + + is_sub = await self.charmhub.is_subordinate(name) + if is_sub: + if num_units > 1: + raise JujuError( + "cannot use num_units with subordinate application" + ) + num_units = 0 + + else: + # We have a local charm dir that needs to be uploaded + charm_dir = os.path.abspath(os.path.expanduser(identifier)) + metadata = utils.get_local_charm_metadata(charm_dir) + charm_series = charm_series or await get_charm_series(metadata, self) + + base = utils.get_local_charm_base(charm_series, charm_dir, client.Base) + charm_origin.base = base + + if not application_name: + application_name = metadata["name"] + if not application_name: + application_name = metadata["name"] + if base is None and charm_series is None: + raise JujuError( + "Either series or base is needed to deploy the " + f"charm at {charm_dir}. " + ) + + identifier = await self.add_local_charm_dir(charm_dir, charm_series) + resources = await self.add_local_resources( + application_name, identifier, metadata, resources=resources + ) + + if config is None: + config = {} + if trust: + config["trust"] = True + + return await self._deploy( + charm_url=identifier, + application=res.app_name, + series=charm_series, + config=config, + constraints=constraints, + endpoint_bindings=bind, + resources=resources, + storage=storage, + channel=channel, + num_units=num_units, + placement=parse_placement(to), + devices=devices, + charm_origin=charm_origin, + attach_storage=attach_storage, + force=force, + server_side_deploy=server_side_deploy, + ) + + async def _add_charm(self, charm_url, origin): + """_add_charm sends the given origin and the url to the Juju API too add the charm to the + state. Either calls the CharmsFacade.AddCharm for (> version 2), or the + ClientFacade.AddCharm (for older versions). + + :param str charm_url: the url of the charm to be added + :param client.CharmOrigin origin: the origin for the charm to be added + + :returns client.CharmOriginResult + """ + # client facade is deprecated with in Juju, and smaller, more focused + # facades have been created and we'll use that if it's available. + charms_cls = client.CharmsFacade + if charms_cls.best_facade_version(self.connection()) > 2: + charms_facade = charms_cls.from_connection(self.connection()) + return await charms_facade.AddCharm( + charm_origin=origin, url=charm_url, force=False + ) + + client_facade = client.ClientFacade.from_connection(self.connection()) + return await client_facade.AddCharm( + channel=str(origin.risk), url=charm_url, force=False + ) + + async def _resolve_charm( + self, url, origin, force=False, series=None, model_config=None + ): + """Calls Charms.ResolveCharms to resolve all the fields of the + charm_origin and also the url and the supported_series + + :param str url: The url of the charm + :param client.CharmOrigin origin: The manually constructed origin + based on what we know about the charm and the deployment so far + + Returns the confirmed origin returned by the Juju API to be used in + calls like ApplicationFacade.Deploy + + :returns url.URL, client.CharmOrigin, [str] + """ + charms_cls = client.CharmsFacade + if charms_cls.best_facade_version(self.connection()) < 3: + raise JujuError("resolve charm") + + charms_facade = charms_cls.from_connection(self.connection()) + + # TODO (cderici): following part can be refactored out, since the + # origin should be set (including the base) before calling this, + # though all tests need to run (in earlier versions too) before + # committing to make sure there's no regression + source = Source.CHARM_HUB.value + + resolve_origin = { + "source": source, + "architecture": origin.architecture, + "track": origin.track, + "risk": origin.risk, + "base": origin.base, + "revision": origin.revision, + } + + resp = await charms_facade.ResolveCharms( + resolve=[{"reference": str(url), "charm-origin": resolve_origin}] + ) + if len(resp.results) != 1: + raise JujuError(f"expected one result, received {resp.results}") + + result = resp.results[0] + if result.error: + raise JujuError(f"resolving {url} : {result.error.message}") + + # TODO (cderici) : supported_bases + supported_series = result.get( + "supported_series", result.unknown_fields["supported-series"] + ) + resolved_origin = result.charm_origin + charm_url = URL.parse(result.url) + + # run the series selector to get a series for the base + selected_series = utils.series_selector( + series, charm_url, model_config, supported_series, force + ) + result.charm_origin.base = utils.get_base_from_origin_or_channel( + resolved_origin, selected_series + ) + charm_url.series = selected_series + + return charm_url, resolved_origin + + async def _resolve_architecture(self, url=None): + """_resolve_architecture returns the architecture for a given charm url. + If the charm url is absent, or doesn't specific an arch, we return the + default architecture from the model. + + :param str url: the client.URL to determine the arch for + + :returns str architecture for the given url + """ + if url is not None and url.architecture: + return url.architecture + + constraints = await self.get_constraints() + if "arch" in constraints: + return constraints["arch"] + + return DEFAULT_ARCHITECTURE + + async def _add_charmhub_resources( + self, application, entity_url, origin, overrides=None + ): + """_add_charmhub_resources is called by the deploy to add pending resources requested by + the charm being deployed. It calls the ResourcesFacade.AddPendingResources. + + :param str application: the name of the application + :param client.CharmURL entity_url: url for the charm that we add resources for + :param client.CharmOrigin origin: origin for the charm that we add resources for + + :returns [string]string resource_map that is a map of resources to their assigned + pendingIDs. + """ + charm_facade = client.CharmsFacade.from_connection(self.connection()) + res = await charm_facade.CharmInfo(entity_url) + + resources = [ + { + "description": resource.description, + "name": resource.name, + "path": resource.path, + "type_": resource.type_, + "origin": "store", + "revision": -1, + } + for resource in res.meta.resources.values() + ] + + if overrides: + names = {r["name"] for r in resources} + unknown = overrides.keys() - names + if unknown: + raise JujuError( + "Unrecognized resource{}: {}".format( + "s" if len(unknown) > 1 else "", ", ".join(unknown) + ) + ) + for resource in resources: + if resource["name"] in overrides: + resource["revision"] = overrides[resource["name"]] + + resources_facade = client.ResourcesFacade.from_connection(self.connection()) + response = await resources_facade.AddPendingResources( + application_tag=tag.application(application), + charm_url=entity_url, + charm_origin=origin, + resources=[client.CharmResource(**resource) for resource in resources], + ) + + # response.pending_ids always exists but it can itself be None + # see juju/client/_definitions.py for AddPendingResourcesResult + resource_map = { + resource["name"]: pid + for resource, pid in zip(resources, response.pending_ids or {}) + } + + return resource_map + + async def add_local_resources(self, application, entity_url, metadata, resources): + """_add_local_resources is called by the deploy to add pending local resources requested by + the charm being deployed. It calls the ResourcesFacade.AddPendingResources. After getting + the pending IDs from the controller it sends an HTTP PUT request to actually upload local + resources. + + :param str application: the name of the application + :param client.CharmURL entity_url: url for the charm that we add resources for + :param [string]string metadata: metadata for the charm that we add resources for + :param dict[str, str] resources: the paths for the local files (or oci-images) to + be added as local resources + + :returns [string]string resource_map that is a map of resources to their assigned + pendingIDs. + """ + if not resources: + return None + + resource_map = dict() + + for name, path in resources.items(): + resource_type = metadata["resources"][name]["type"] + if resource_type not in {"oci-image", "file"}: + log.info(f"Resource {name} of type {resource_type} is not supported") + continue + + charmresource = { + "description": "", + "fingerprint": "", + "name": name, + "path": Path(path).name, + "revision": 0, + "size": 0, + "type_": resource_type, + "origin": "upload", + } + + resources_facade = client.ResourcesFacade.from_connection(self.connection()) + response = await resources_facade.AddPendingResources( + application_tag=tag.application(application), + charm_url=entity_url, + resources=[client.CharmResource(**charmresource)], + ) + pending_id = response.pending_ids[0] + resource_map[name] = pending_id + + if resource_type == "oci-image": + # TODO Docker Image validation and support for local images. + docker_image_details = { + "registrypath": path, + "username": "", + "password": "", + } + data = yaml.dump(docker_image_details).encode("utf-8") + else: + p = Path(path) + data = p.read_bytes() if p.exists() else b"" + + self._upload(data, path, application, name, resource_type, pending_id) + + return resource_map + + def _upload( + self, + data: bytes, + path: str | Path, + app_name: str, + res_name: str, + res_type: str, + pending_id: str, + ) -> None: + conn, headers, path_prefix = self.connection().https_connection() + + query = f"?pendingid={pending_id}" + url = f"{path_prefix}/applications/{app_name}/resources/{res_name}{query}" + if res_type == "oci-image": + disp = f'multipart/form-data; filename="{path}"' + else: + disp = f'form-data; filename="{path}"' + + headers["Content-Type"] = "application/octet-stream" + headers["Content-Length"] = str(len(data)) + headers["Content-Sha384"] = hashlib.sha384(data).hexdigest() + headers["Content-Disposition"] = disp + + conn.request("PUT", url, data, headers) + + response = conn.getresponse() + result = response.read().decode() + if not response.status == 200: + raise JujuError(result) + + async def _deploy( + self, + charm_url, + application, + series, + config, + constraints, + endpoint_bindings, + resources, + storage: Mapping[str, str | StorageConstraintDict] | None, + channel=None, + num_units=None, + placement=None, + devices=None, + charm_origin=None, + attach_storage=[], + force=False, + server_side_deploy=False, + ): + """Logic shared between `Model.deploy` and `BundleHandler.deploy`. + + :param dict storage: optional storage constraints, in the form of `{label: constraint}`. + The label is a string specified by the charm, while the constraint is + either a constraints.StorageConstraintDict, or a string following + `the juju storage constraint directive format `_, + specifying the storage pool, number of volumes, and size of each volume. + """ + log.info("Deploying %s", charm_url) + + storage = parse_storage_constraints(storage) + + trust = config.get("trust", False) + # stringify all config values for API, and convert to YAML + config = {k: str(v) for k, v in config.items()} + config = yaml.dump({application: config}, default_flow_style=False) + + app_facade = client.ApplicationFacade.from_connection(self.connection()) + + if server_side_deploy: + # Call DeployFromRepository + app = client.DeployFromRepositoryArg( + applicationname=application, + attachstorage=attach_storage, + charmname=charm_url, + configyaml=config, + cons=parse_constraints(constraints), + devices=devices, + dryrun=False, + placement=placement, + storage=storage, + trust=trust, + base=charm_origin.base, + channel=channel, + endpoint_bindings=endpoint_bindings, + force=force, + num_units=num_units, + resources=resources, + revision=charm_origin.revision, + ) + result = await app_facade.DeployFromRepository([app]) + # Collect the errors + errors = [] + for r in result.results: + if r.errors: + errors.extend([e.message for e in r.errors]) + # Upload pending local resources if any + for _result in result.results: + for pending_upload_resource in getattr( + _result, "pendingresourceuploads", [] + ): + _path = pending_upload_resource.filename + p = Path(_path) + data = p.read_text() if p.exists() else "" + self._upload( + data, + _path, + application, + pending_upload_resource.name, + "file", + "", + ) + else: + app = client.ApplicationDeploy( + charm_url=charm_url, + application=application, + series=series, + channel=channel, + charm_origin=charm_origin, + config_yaml=config, + constraints=parse_constraints(constraints), + endpoint_bindings=endpoint_bindings, + num_units=num_units, + resources=resources, + storage=storage, + placement=placement, + devices=devices, + attach_storage=attach_storage, + force=force, + ) + result = await app_facade.Deploy(applications=[app]) + errors = [r.error.message for r in result.results if r.error] + if errors: + raise JujuError("\n".join(errors)) + + return await self._wait_for_new("application", application) + + async def destroy_unit( + self, unit_id, destroy_storage=False, dry_run=False, force=False, max_wait=None + ): + """Destroy units by name.""" + return await self.destroy_units( + unit_id, + destroy_storage=destroy_storage, + dry_run=dry_run, + force=force, + max_wait=max_wait, + ) + + async def destroy_units( + self, + *unit_names, + destroy_storage=False, + dry_run=False, + force=False, + max_wait=None, + ): + """Destroy several units at once.""" + connection = self.connection() + app_facade = client.ApplicationFacade.from_connection(connection) + + units_to_destroy = [] + for unit_id in unit_names: + unit_tag = tag.unit(unit_id) + if unit_tag is None: + log.error("Error converting %s to a valid unit tag", unit_id) + raise JujuUnitError("Error converting %s to a valid unit tag", unit_id) + units_to_destroy.append({ + "unit-tag": unit_tag, + "destroy-storage": destroy_storage, + "force": force, + "max-wait": max_wait, + "dry-run": dry_run, + }) + log.debug("Destroying units %s", unit_names) + return await app_facade.DestroyUnit(units=units_to_destroy) + + def download_backup(self, archive_id, target_filename=None): + """Download a backup archive file. + + :param str archive_id: The id of the archive to download + :param str (optional) target_filename: A custom name for the target file + :return str: Path to the archive file + + """ + conn, headers, path_prefix = self.connection().https_connection() + path = "%s/backups" % path_prefix + headers["Content-Type"] = "application/json" + args = {"id": archive_id} + conn.request("GET", path, json.dumps(args, indent=2), headers=headers) + response = conn.getresponse() + result = response.read() + if not response.status == 200: + raise JujuBackupError( + "unable to download the backup ID : %s -- got : %s from the JujuAPI with a HTTP response code : %s" + % (archive_id, result, response.status) + ) + + if target_filename: + file_name = target_filename + else: + # check if archive_id is a filename + if re.match(r".*\.tar\.gz", archive_id): + # if so, use the same ID generated & sent by the Juju API + archive_id = re.compile("[0-9]+").findall(archive_id)[0] + + file_name = "juju-backup-%s.tar.gz" % archive_id + + with open(str(file_name), "wb") as f: + try: + f.write(result) + except OSError as e: + raise JujuBackupError( + "backup ID : %s was fetched, but : %s" % (archive_id, e) + ) + + log.info("Backup archive downloaded in : %s" % file_name) + return file_name + + async def get_config(self): + """Return the configuration settings for this model. + + :returns: A ``dict`` mapping keys to `ConfigValue` instances, + which have `source` and `value` attributes. + """ + config_facade = client.ModelConfigFacade.from_connection(self.connection()) + result = await config_facade.ModelGet() + config = result.config + for key, value in config.items(): + config[key] = client.ConfigValue.from_json(value) + return config + + async def get_constraints(self): + """Return the machine constraints for this model. + + :returns: A ``dict`` of constraints. + """ + constraints = {} + facade_cls = client.ModelConfigFacade + + facade = facade_cls.from_connection(self.connection()) + result = await facade.GetModelConstraints() + + # GetModelConstraints returns GetConstraintsResults which has a + # 'constraints' attribute. If no constraints have been set + # GetConstraintsResults.constraints is None. Otherwise + # GetConstraintsResults.constraints has an attribute for each possible + # constraint, each of these in turn will be None if they have not been + # set. + if result.constraints: + constraint_types = [ + a for a in dir(result.constraints) if a in client.Value._toSchema + ] + for constraint in constraint_types: + value = getattr(result.constraints, constraint) + if value is not None: + constraints[constraint] = getattr(result.constraints, constraint) + return constraints + + async def get_machines(self): + """Return list of machines in this model.""" + return list(self.state.machines.keys()) + + async def get_spaces(self): + """Return list of all known spaces, including associated subnets. + + Returns a List of :class:`~juju._definitions.Space` instances. + """ + space_facade = client.SpacesFacade.from_connection(self.connection()) + response = await space_facade.ListSpaces() + return response.results + + async def get_ssh_key(self, raw_ssh=False): + """Return known SSH keys for this model. + :param bool raw_ssh: if True, returns the raw ssh key, + else it's fingerprint + + """ + key_facade = client.KeyManagerFacade.from_connection(self.connection()) + entity = {"tag": tag.model(self.info.uuid)} + entities = client.Entities([entity]) + return await key_facade.ListKeys(entities=entities, mode=raw_ssh) + + get_ssh_keys = get_ssh_key + + async def remove_backup(self, backup_id): + """Delete a backup. + + :param str backup_id: The id of the backup to remove + + """ + backups_facade = client.BackupsFacade.from_connection(self.connection()) + return await backups_facade.Remove([backup_id]) + + async def remove_backups(self, backup_ids): + """Delete the given backups. + + :param [str] backup_ids: The list of ids of the backups to remove + + """ + backups_facade = client.BackupsFacade.from_connection(self.connection()) + return await backups_facade.Remove(backup_ids) + + async def remove_ssh_key(self, user, key): + """Remove a public SSH key(s) from this model. + + :param str key: Full ssh key + :param str user: Juju user to which the key is registered + + """ + key_facade = client.KeyManagerFacade.from_connection(self.connection()) + key = base64.b64decode(bytes(key.strip().split()[1].encode("ascii"))) + key = hashlib.md5(key).hexdigest() # noqa: S324 + key = ":".join(a + b for a, b in zip(key[::2], key[1::2])) + await key_facade.DeleteKeys(ssh_keys=[key], user=user) + + remove_ssh_keys = remove_ssh_key + + def restore_backup( + self, + backup_id, + bootstrap=False, + constraints=None, + archive=None, + upload_tools=False, + ): + """Restore a backup archive to a new controller. + + :param str backup_id: Id of backup to restore + :param bool bootstrap: Bootstrap a new state machine + :param constraints: Model constraints + :type constraints: :class:`juju.Constraints` + :param str archive: Path to backup archive to restore + :param bool upload_tools: Upload tools if bootstrapping a new machine + + """ + raise DeprecationWarning( + "juju restore-backup is deprecated in favor of the stand-alone 'juju-restore' tool: https://github.com/juju/juju-restore" + ) + + async def set_config(self, config): + """Set configuration keys on this model. + + :param dict config: Mapping of config keys to either string values or + `ConfigValue` instances, as returned by `get_config`. + """ + config_facade = client.ModelConfigFacade.from_connection(self.connection()) + + new_conf = {} + for key, value in config.items(): + if isinstance(value, client.ConfigValue): + new_conf[key] = value.value + elif isinstance(value, str): + new_conf[key] = value + else: + raise JujuModelConfigError( + "Expected either a string or a ConfigValue as a config value, found : %s of type %s" + % (value, type(value)) + ) + await config_facade.ModelSet(config=new_conf) + + async def set_constraints(self, constraints): + """Set machine constraints on this model. + + :param dict config: Mapping of model constraints + """ + facade_cls = client.ModelConfigFacade + + facade = facade_cls.from_connection(self.connection()) + + await facade.SetModelConstraints(application="", constraints=constraints) + + async def get_action_output(self, action_uuid, wait=None): + """Get the results of an action by ID. + + :param str action_uuid: Id of the action + :param int wait: Time in seconds to wait for action to complete. + :return dict: Output from action + :raises: :class:`JujuError` if invalid action_uuid + """ + action = await self._get_completed_action(action_uuid, wait=wait) + # ActionResult.output is None if the action produced no output + return {} if action.output is None else action.output + + async def _get_completed_action(self, action_uuid, wait=None): + """Get the completed internal _definitions.Action object. + + :param str action_uuid: Id of the action + :param int wait: Time in seconds to wait for action to complete. + :return dict: Output from action + :raises: :class:`JujuError` if invalid action_uuid + """ + action_facade = client.ActionFacade.from_connection(self.connection()) + entity = [{"tag": tag.action(action_uuid)}] + # Cannot use self.wait_for_action as the action event has probably + # already happened and self.wait_for_action works by processing + # model deltas and checking if they match our type. If the action + # has already occurred then the delta has gone. + + async def _wait_for_action_status(): + while True: + action_output = await action_facade.Actions(entities=entity) + if action_output.results[0].status in ("completed", "failed"): + return + else: + await jasyncio.sleep(1) + + await jasyncio.wait_for(_wait_for_action_status(), timeout=wait) + action_results = await action_facade.Actions(entities=entity) + return action_results.results[0] + + # FIXME: this function seems dead, the facade methods don't seem to exist + async def get_action_status(self, uuid_or_prefix=None, name=None): + """Get the status of all actions, filtered by ID, ID prefix, or name. + + :param str uuid_or_prefix: Filter by action uuid or prefix + :param str name: Filter by action name + + """ + results = {} + action_results = [] + action_facade = client.ActionFacade.from_connection(self.connection()) + if name: + name_results = await action_facade.FindActionsByNames(names=[name]) + action_results.extend(name_results.actions[0].actions) + if uuid_or_prefix: + # Collect list of actions matching uuid or prefix + matching_actions = await action_facade.FindActionTagsByPrefix( + prefixes=[uuid_or_prefix] + ) + entities = [] + for actions in matching_actions.matches.values(): + entities = [{"tag": a.tag} for a in actions] + # Get action results matching action tags + uuid_results = await action_facade.Actions(entities=entities) + action_results.extend(uuid_results.results) + for a in action_results: + results[tag.untag("action-", a.action.tag)] = a.status + return results + + async def get_status(self, filters=None, utc=False) -> FullStatus: + """Return the status of the model. + + :param str filters: Optional list of applications, units, or machines + to include, which can use wildcards ('*'). + :param bool utc: Display time as UTC in RFC3339 format + + """ + client_facade = client.ClientFacade.from_connection(self.connection()) + return await client_facade.FullStatus(patterns=filters) + + async def get_metrics(self, *tags): + """Retrieve metrics. + + :param str *tags: Tags of entities from which to retrieve metrics. + No tags retrieves the metrics of all units in the model. + :return: Dictionary of unit_name:metrics + + """ + log.debug("Retrieving metrics for %s", ", ".join(tags) if tags else "all units") + + metrics_facade = client.MetricsDebugFacade.from_connection(self.connection()) + + entities = [client.Entity(tag) for tag in tags] + metrics_result = await metrics_facade.GetMetrics(entities=entities) + + metrics = collections.defaultdict(list) + + for entity_metrics in metrics_result.results: + error = entity_metrics.error + if error: + if "is not a valid tag" in error: + raise ValueError(error.message) + else: + raise Exception(error.message) + + for metric in entity_metrics.metrics: + metrics[metric.unit].append(vars(metric)) + + return metrics + + async def create_offer(self, endpoint, offer_name=None, application_name=None): + """Offer a deployed application using a series of endpoints for use by + consumers. + + @param endpoint: holds the application and endpoint you want to offer + @param offer_name: over ride the offer name to help the consumer + """ + async with ConnectedController(self.connection()) as controller: + return await controller.create_offer( + self.info.uuid, + endpoint, + offer_name=offer_name, + application_name=application_name, + ) + + async def list_offers(self): + """Offers list information about applications' endpoints that have been + shared and who is connected. + """ + async with ConnectedController(self.connection()) as controller: + return await controller.list_offers(self.name) + + async def remove_offer(self, endpoint, force=False): + """Remove offer for an application. + + Offers will also remove relations to those offers, use force to do + so, without an error. + """ + async with ConnectedController(self.connection()) as controller: + return await controller.remove_offer(self.info.uuid, endpoint, force) + + async def consume( + self, endpoint, application_alias="", controller_name=None, controller=None + ): + """Adds a remote offer to the model. Relations can be created later using + "juju relate". + + If consuming a relation from a model on different controller the + controller name must be included in the endpoint. The controller_name + argument is being deprecated. + """ + if controller and controller_name: + raise JujuError("cannot set both controller_name and controller") + try: + offer = parse_offer_url(endpoint) + except OfferParseError as e: + log.error(e.message) + raise + if offer.has_endpoint(): + raise JujuError(f"remote offer {endpoint} should not include an endpoint") + if offer.user == "": + offer.user = self.info.username + endpoint = offer.string() + + if controller_name: + log.warning( + "controller_name argument will soon be deprecated, controller " + "should be specified in endpoint url" + ) + if offer.source == "": + offer.source = controller_name + + if offer.source: + source = await self._get_source_api(offer) + else: + if controller: + source = controller + else: + source = Controller() + kwargs = self.connection().connect_params() + kwargs["uuid"] = None + await source._connect_direct(**kwargs) + + consume_details = await source.get_consume_details(offer.as_local().string()) + + # Only disconnect when the controller object has been created within + # with function We don't want to disconnect the object passed by the + # user in the controller argument + if not controller: + await source.disconnect() + if consume_details is None or consume_details.offer is None: + raise JujuAPIError(f"missing consuming offer url for {offer.string()}") + + offer_url = parse_offer_url(consume_details.offer.offer_url) + offer_url.source = offer.source + + consume_details.offer.offer_url = offer_url.string() + consume_details.offer.application_alias = application_alias + + arg = _create_consume_args( + consume_details.offer, + consume_details.macaroon, + consume_details.external_controller, + ) + + facade = client.ApplicationFacade.from_connection(self.connection()) + results = await facade.Consume(args=[arg]) + if len(results.results) != 1: + raise JujuAPIError(f"expected 1 result, received {len(results.results)}") + if results.results[0].error is not None: + raise JujuAPIError(results.results[0].error) + local_name = offer_url.application + if application_alias != "": + local_name = application_alias + return local_name + + async def remove_saas(self, name): + """Removing a consumed (SAAS) application will terminate any relations that + application has, potentially leaving any related local applications + in a non-functional state. + """ + if not is_valid_application(name): + raise JujuError(f"invalid SAAS application name {name}") + + arg = client.DestroyConsumedApplicationParams() + arg.application_tag = application_tag(name) + + facade = client.ApplicationFacade.from_connection(self.connection()) + return await facade.DestroyConsumedApplications(applications=[arg]) + + async def export_bundle(self, filename=None): + """Exports the current model configuration as a reusable bundle.""" + facade = client.BundleFacade.from_connection(self.connection()) + result = await facade.ExportBundle() + if result.error is not None: + raise JujuAPIError(result.error) + + if filename is None: + return result.result + + try: + with open(str(filename), "w") as file: + file.write(result.result) + except OSError: + raise + + async def add_secret(self, name, data_args, file="", info=""): + """Adds a secret with a list of key values + + Equivalent to the cli command: + juju add-secret [options] [key[#base64|#file]=value...] + + :param name str: The name of the secret to be added. + :param data_args []str: The key value pairs to be added into the secret. + :param file str: A path to a yaml file containing secret key values. + :param info str: The secret description. + """ + data = create_secret_data(data_args) + + if file: + data_from_file = read_secret_data(file) + for k, v in data_from_file.items(): + # Caution: key/value pairs in files overwrite the ones in the args. + data[k] = v + + if client.SecretsFacade.best_facade_version(self.connection()) < 2: + raise JujuNotSupportedError("user secrets") + + secrets_facade = client.SecretsFacade.from_connection(self.connection()) + results = await secrets_facade.CreateSecrets([ + { + "content": {"data": data}, + "description": info, + "label": name, + } + ]) + if len(results.results) != 1: + raise JujuAPIError(f"expected 1 result, got {len(results.results)}") + result = results.results[0] + if result.error is not None: + raise JujuAPIError(result.error.message) + return result.result + + async def update_secret(self, name, data_args=[], new_name="", file="", info=""): + """Update a secret with a list of key values, or info. + + Equivalent to the cli command: + juju add-secret [options] [key[#base64|#file]=value...] + + :param name str: The name of the secret to be added. + :param data_args []str: The key value pairs to be added into the secret. + :param file str: A path to a yaml file containing secret key values. + :param info str: The secret description. + """ + data = create_secret_data(data_args) + if file: + data_from_file = read_secret_data(file) + for k, v in data_from_file.items(): + # Caution: key/value pairs in files overwrite the ones in the args. + data[k] = v + + if client.SecretsFacade.best_facade_version(self.connection()) < 2: + raise JujuNotSupportedError("user secrets") + secrets_facade = client.SecretsFacade.from_connection(self.connection()) + results = await secrets_facade.UpdateSecrets([ + { + "content": {"data": data}, + "description": info, + "existing-label": name, + "label": new_name, + } + ]) + if len(results.results) != 1: + raise JujuAPIError(f"expected 1 result, got {len(results.results)}") + result_error = results.results[0] + if result_error.error is not None: + raise JujuAPIError(result_error.error) + + async def list_secrets(self, filter=None, show_secrets=False): # noqa: A002 + """Returns the list of available secrets.""" + facade = client.SecretsFacade.from_connection(self.connection()) + results = await facade.ListSecrets( + filter_=filter, + show_secrets=show_secrets, + ) + return results.results + + async def remove_secret(self, secret_name, revision=-1): + """Remove an existing secret. + + :param secret_name str: ID|name of the secret to remove. + :param revision int: remove the specified revision. + """ + if client.SecretsFacade.best_facade_version(self.connection()) < 2: + raise JujuNotSupportedError("user secrets") + remove_secret_arg = { + "label": secret_name, + } + if revision >= 0: + remove_secret_arg["revisions"] = [revision] + + secrets_facade = client.SecretsFacade.from_connection(self.connection()) + results = await secrets_facade.RemoveSecrets([remove_secret_arg]) + if len(results.results) != 1: + raise JujuAPIError(f"expected 1 result, got {len(results.results)}") + result_error = results.results[0] + if result_error.error is not None: + raise JujuAPIError(result_error.error) + + async def grant_secret(self, secret_name, application, *applications): + """Grants access to a secret to the specified applications. + + :param secret_name str: ID|name of the secret. + :param application str: name of an application for which the access is granted + :param applications []str: names of more applications to associate the secret with + """ + if client.SecretsFacade.best_facade_version(self.connection()) < 2: + raise JujuNotSupportedError("user secrets") + secrets_facade = client.SecretsFacade.from_connection(self.connection()) + results = await secrets_facade.GrantSecret( + applications=[application, *applications], label=secret_name + ) + if len(results.results) != 1: + raise JujuAPIError(f"expected 1 result, got {len(results.results)}") + result_error = results.results[0] + if result_error.error is not None: + raise JujuAPIError(result_error.error) + + async def revoke_secret(self, secret_name, application, *applications): + """Revoke access to a secret. + + Revoke applications' access to view the value of a specified secret. + + :param secret_name str: ID|name of the secret. + :param application str: name of an application for which the access to the secret is revoked + :param applications []str: names of more applications to disassociate the secret with + """ + if client.SecretsFacade.best_facade_version(self.connection()) < 2: + raise JujuNotSupportedError("user secrets") + secrets_facade = client.SecretsFacade.from_connection(self.connection()) + results = await secrets_facade.RevokeSecret( + applications=[application, *applications], label=secret_name + ) + if len(results.results) != 1: + raise JujuAPIError(f"expected 1 result, got {len(results.results)}") + result_error = results.results[0] + if result_error.error is not None: + raise JujuAPIError(result_error.error) + + async def _get_source_api(self, url): + controller = Controller() + if url.has_empty_source(): + async with ConnectedController(self.connection()) as current: + if current.controller_name is not None: + controller_name = current.controller_name + else: + controller_name = url.source + + await controller.connect(controller_name=controller_name) + return controller + + async def wait_for_idle( + self, + apps: list[str] | None = None, + raise_on_error: bool = True, + raise_on_blocked: bool = False, + wait_for_active: bool = False, + timeout: float | None = 10 * 60, + idle_period: float = 15, + check_freq: float = 0.5, + status: str | None = None, + wait_for_at_least_units: int | None = None, + wait_for_exact_units: int | None = None, + ) -> None: + """Wait for applications in the model to settle into an idle state. + + :param List[str] apps: Optional list of specific app names to wait on. + If given, all apps must be present in the model and idle, while other + apps in the model can still be busy. If not given, all apps currently + in the model must be idle. + + :param bool raise_on_error: If True, then any unit or app going into + "error" status immediately raises either a JujuAppError or a JujuUnitError. + Note that machine or agent failures will always raise an exception (either + JujuMachineError or JujuAgentError), regardless of this param. The default + is True. + + :param bool raise_on_blocked: If True, then any unit or app going into + "blocked" status immediately raises either a JujuAppError or a JujuUnitError. + The default is False. + + :param bool wait_for_active: If True, then also wait for all unit workload + statuses to be "active" as well. The default is False. + + :param float timeout: How long to wait, in seconds, for the bundle settles + before raising an asyncio.TimeoutError. If None, will wait forever. + The default is 10 minutes. + + :param float idle_period: How long, in seconds, the agent statuses of all + units of all apps need to be `idle`. This delay is used to ensure that + any pending hooks have a chance to start to avoid false positives. + The default is 15 seconds. + + :param float check_freq: How frequently, in seconds, to check the model. + The default is every half-second. + + :param str status: The status to wait for. If None, not waiting. + The default is None (not waiting for any status). + + :param int wait_for_at_least_units: The least number of units to go into the idle + state. wait_for_idle will return after that many units are available (across all the + given applications). + The default is 1 unit. + + :param int wait_for_exact_units: The exact number of units to be expected before + going into the idle state. (e.g. useful for scaling down). + When set, takes precedence over the `wait_for_units` parameter. + """ + if wait_for_active: + warnings.warn( + "wait_for_active is deprecated; use status", + DeprecationWarning, + stacklevel=2, + ) + status = "active" + + _wait_for_units = ( + wait_for_at_least_units if wait_for_at_least_units is not None else 1 + ) + + timeout = timedelta(seconds=timeout) if timeout is not None else None + idle_period = timedelta(seconds=idle_period) + start_time = datetime.now() + # Type check against the common error of passing a str for apps + if apps is not None and ( + not isinstance(apps, list) or any(not isinstance(o, str) for o in apps) + ): + raise JujuError(f"Expected a List[str] for apps, given {apps}") + + apps = apps or self.applications + idle_times: dict[str, datetime] = {} + units_ready: set[str] = set() # The units that are in the desired state + last_log_time: datetime | None = None + log_interval = timedelta(seconds=30) + + def _raise_for_status(entities: dict[str, list[str]], status: Any): + if not entities: + return + for entity_name, error_type in ( + ("Machine", JujuMachineError), + ("Agent", JujuAgentError), + ("Unit", JujuUnitError), + ("App", JujuAppError), + ): + errored = entities.get(entity_name, []) + if not errored: + continue + raise error_type( + "{}{} in {}: {}".format( + entity_name, + "s" if len(errored) > 1 else "", + status, + ", ".join(errored), + ) + ) + + if wait_for_exact_units is not None: + assert ( + isinstance(wait_for_exact_units, int) and wait_for_exact_units >= 0 + ), "Invalid value for wait_for_exact_units : %s" % wait_for_exact_units + + while True: + # The list 'busy' is what keeps this loop going, + # i.e. it'll stop when busy is empty after all the + # units are scanned + busy = [] + errors = {} + blocks = {} + for app_name in apps: + if app_name not in self.applications: + busy.append(app_name + " (missing)") + continue + app = self.applications[app_name] + app_status = await app.get_status() + if raise_on_error and app_status == "error": + errors.setdefault("App", []).append(app.name) + if raise_on_blocked and app_status == "blocked": + blocks.setdefault("App", []).append(app.name) + + # Check if wait_for_exact_units flag is used + if wait_for_exact_units is not None: + if len(app.units) != wait_for_exact_units: + busy.append( + app.name + + " (waiting for exactly %s units, current : %s)" + % (wait_for_exact_units, len(app.units)) + ) + continue + # If we have less # of units then required, then wait a bit more + elif len(app.units) < _wait_for_units: + busy.append( + app.name + + " (not enough units yet - %s/%s)" + % (len(app.units), _wait_for_units) + ) + continue + # User is waiting for at least a certain # of units, and we have enough + elif wait_for_at_least_units and len(units_ready) >= _wait_for_units: + # So no need to keep looking, we have the desired number of units ready to go, + # exit the loop. Don't just return here, though, we might still have some + # errors to raise at the end + break + for unit in app.units: + if ( + raise_on_error + and unit.machine is not None + and unit.machine.status == "error" + ): + errors.setdefault("Machine", []).append(unit.machine.id) + continue + if raise_on_error and unit.agent_status == "error": + errors.setdefault("Agent", []).append(unit.name) + continue + if raise_on_error and unit.workload_status == "error": + errors.setdefault("Unit", []).append(unit.name) + continue + if raise_on_blocked and unit.workload_status == "blocked": + blocks.setdefault("Unit", []).append(unit.name) + continue + # TODO (cderici): we need two versions of wait_for_idle, one for waiting on + # individual units, another one for waiting for an application. + # The convoluted logic below is the result of trying to do both at the same + # time + need_to_wait_more_for_a_particular_status = status and ( + unit.workload_status != status + ) + app_is_in_desired_status = (not status) or (app_status == status) + if ( + not need_to_wait_more_for_a_particular_status + and unit.agent_status == "idle" + and (wait_for_at_least_units or app_is_in_desired_status) + ): + # A unit is ready if either: + # 1) Don't need to wait more for a particular status and the agent is "idle" + # 2) We're looking for a particular status and the unit's workload, + # as well as the application, is in that status. If the user wants to + # see only a particular number of units in that state -- i.e. a subset of + # the units is needed, then we don't care about the application status + # (because e.g. app can be in 'waiting' while unit.0 is 'active' and unit.1 + # is 'waiting') + + # Either way, the unit is ready, start measuring the time period that + # it needs to stay in that state (i.e. idle_period) + units_ready.add(unit.name) + now = datetime.now() + idle_start = idle_times.setdefault(unit.name, now) + + if now - idle_start < idle_period: + busy.append( + f"{unit.name} [{unit.agent_status}] {unit.workload_status}: {unit.workload_status_message}" + ) + else: + idle_times.pop(unit.name, None) + busy.append( + f"{unit.name} [{unit.agent_status}] {unit.workload_status}: {unit.workload_status_message}" + ) + _raise_for_status(errors, "error") + _raise_for_status(blocks, "blocked") + if not busy: + break + busy = "\n ".join(busy) + if timeout is not None and datetime.now() - start_time > timeout: + raise jasyncio.TimeoutError("Timed out waiting for model:\n" + busy) + if last_log_time is None or datetime.now() - last_log_time > log_interval: + log.info("Waiting for model:\n " + busy) + last_log_time = datetime.now() + await jasyncio.sleep(check_freq) + + +def _create_consume_args(offer, macaroon, controller_info): + """Convert a typed object that has been normalised to a overridden typed + definition. + + @param offer: takes an offer and serialises it into a valid type + @param macaroon: takes a macaroon and serialises it into a valid type + @param controller_info: takes a controller information and serialises it into + a valid type. + """ + endpoints = [ + client.RemoteEndpoint( + interface=ep.interface, limit=ep.limit, name=ep.name, role=ep.role + ) + for ep in offer.endpoints + ] + users = [ + client.OfferUserDetails( + access=u.access, display_name=u.display_name, user=u.user + ) + for u in offer.users + ] + external_controller = client.ExternalControllerInfo( + addrs=controller_info.addrs, + ca_cert=controller_info.ca_cert, + controller_alias=controller_info.controller_alias, + controller_tag=controller_info.controller_tag, + ) + caveats = [Caveat(cid=c["cid"]) for c in macaroon.unknown_fields["caveats"]] + macaroon = Macaroon( + signature=macaroon.unknown_fields["signature"], + caveats=caveats, + location=macaroon.unknown_fields["location"], + identifier=macaroon.unknown_fields["identifier"], + ) + + arg = client.ConsumeApplicationArg() + arg.application_description = offer.application_description + arg.endpoints = endpoints + arg.offer_name = offer.offer_name + arg.offer_url = offer.offer_url + arg.offer_uuid = offer.offer_uuid + arg.source_model_tag = offer.source_model_tag + arg.users = users + arg.application_alias = offer.application_alias + arg.external_controller = external_controller + arg.macaroon = macaroon + + return arg + + +class CharmArchiveGenerator: + """Create a Zip archive of a local charm directory for upload to a controller. + + This is used automatically by + `Model.add_local_charm_dir <#juju.model.Model.add_local_charm_dir>`_. + """ + + def __init__(self, path): + self.path = os.path.abspath(os.path.expanduser(path)) + + def make_archive(self, path): + """Create archive of directory and write to ``path``. + + :param path: Path to archive + + Ignored:: + + * build/* - This is used for packing the charm itself and any + similar tasks. + * */.* - Hidden files are all ignored for now. This will most + likely be changed into a specific ignore list + (.bzr, etc) + + """ + zf = zipfile.ZipFile(str(path), "w", zipfile.ZIP_DEFLATED) + for dirpath, dirnames, filenames in os.walk(self.path): + relative_path = dirpath[len(self.path) + 1 :] + if relative_path and not self._ignore(relative_path): + zf.write(dirpath, relative_path) + for dirname in dirnames: + archive_name = os.path.join(relative_path, dirname) + real_path = os.path.join(dirpath, dirname) + if os.path.islink(real_path): + self._check_link(real_path) + self._write_symlink(zf, os.readlink(real_path), archive_name) + for name in filenames: + archive_name = os.path.join(relative_path, name) + if not self._ignore(archive_name): + real_path = os.path.join(dirpath, name) + self._check_type(real_path) + if os.path.islink(real_path): + self._check_link(real_path) + self._write_symlink(zf, os.readlink(real_path), archive_name) + else: + zf.write(real_path, archive_name) + zf.close() + return path + + def _check_type(self, path: str) -> str: + """Check the path""" + s = os.stat(path) + if stat.S_ISDIR(s.st_mode) or stat.S_ISREG(s.st_mode): + return path + raise ValueError( + "Invalid Charm at %s %s" % (path, "Invalid file type for a charm") + ) + + def _check_link(self, path: str) -> None: + link_path = os.readlink(path) + if link_path[0] == "/": + raise ValueError( + "Invalid Charm at %s: %s" % (path, "Absolute links are invalid") + ) + path_dir = os.path.dirname(path) + link_path = os.path.join(path_dir, link_path) + if not link_path.startswith(os.path.abspath(self.path)): + raise ValueError( + "Invalid charm at %s %s" % (path, "Only internal symlinks are allowed") + ) + + def _write_symlink( + self, zf: zipfile.ZipFile, link_target: str, link_path: str + ) -> None: + """Package symlinks with appropriate zipfile metadata.""" + info = zipfile.ZipInfo() + info.filename = link_path + info.create_system = 3 + # Magic code for symlinks / py2/3 compat + # 27166663808 = (stat.S_IFLNK | 0755) << 16 + info.external_attr = 2716663808 + zf.writestr(info, link_target) + + def _ignore(self, path: str) -> bool: + return path == "build" or path.startswith("build/") or path.startswith(".") + + +class ModelInfo(ModelEntity): + @property + def tag(self): + return tag.model(self.uuid) + + @staticmethod + def type_name_override(): + return "model" diff --git a/build/lib/juju/names.py b/build/lib/juju/names.py new file mode 100644 index 00000000..0e9270fc --- /dev/null +++ b/build/lib/juju/names.py @@ -0,0 +1,93 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import re +from enum import Enum, unique + + +@unique +class MatchType(Enum): + MATCH = 1 + SEARCH = 2 + + +MODEL = re.compile("^[a-z0-9]+[a-z0-9-]*$") + + +def match_model(val, match_type=None): + if match_type is MatchType.SEARCH: + return re.search(MODEL, val) + else: + return re.match(MODEL, val) + + +APPLICATION = re.compile("^(?:[a-z][a-z0-9]*(?:-[a-z0-9]*[a-z][a-z0-9]*)*)$") + + +def match_application(val, match_type=None): + if match_type is MatchType.SEARCH: + return re.search(APPLICATION, val) + else: + return re.match(APPLICATION, val) + + +def is_valid_application(val): + return match_application(val) is not None + + +ENDPOINT = re.compile( + "/?((?P[^\\.]*)\\.)?(?P[^:]*)(:(?P.*))?" +) + + +def match_endpoint(val, match_type=None): + if match_type is MatchType.SEARCH: + return re.search(ENDPOINT, val) + else: + return re.match(ENDPOINT, val) + + +SOURCE_ENDPOINT = re.compile("^[a-zA-Z0-9]+$") + + +def match_source_endpoint(val, match_type=None): + if match_type is MatchType.SEARCH: + return re.search(SOURCE_ENDPOINT, val) + else: + return re.match(SOURCE_ENDPOINT, val) + + +MODEL_APPLICATION = re.compile( + "(/?((?P[^/]+)/)?(?P[^.]*)(.(?P[^:]*(:.*)?))?)?" +) + + +def match_model_application(val, match_type=None): + if match_type is MatchType.SEARCH: + return re.search(MODEL_APPLICATION, val) + else: + return re.match(MODEL_APPLICATION, val) + + +valid_user_name_snippet = "[a-zA-Z0-9][a-zA-Z0-9.+-]*[a-zA-Z0-9]" +valid_user_snippet = f"(?:{valid_user_name_snippet}(?:@{valid_user_name_snippet})?)" +USER = re.compile( + f"^(?P{valid_user_name_snippet})(?:@(?P{valid_user_name_snippet}))?$" +) + + +def match_user(val, match_type=None): + if match_type is MatchType.SEARCH: + return re.search(USER, val) + else: + return re.match(USER, val) + + +RELATION = re.compile("[a-z][a-z0-9]*(?:[_-][a-z0-9]+)*") + + +def match_relation(val, match_type=None): + if match_type is MatchType.SEARCH: + return re.search(RELATION, val) + else: + return re.match(RELATION, val) diff --git a/build/lib/juju/offerendpoints.py b/build/lib/juju/offerendpoints.py new file mode 100644 index 00000000..3a39556d --- /dev/null +++ b/build/lib/juju/offerendpoints.py @@ -0,0 +1,216 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +# +# Module that parses offer endpoints +# + + +from .names import ( + MatchType, + match_application, + match_endpoint, + match_model, + match_model_application, + match_relation, + match_source_endpoint, + match_user, +) + + +class ParseError(Exception): + def __init__(self, message, *args, **kwargs): + self.message = message + super().__init__(*args, **kwargs) + + +class OfferEndpoints: + def __init__(self, application, endpoints, model=None, qualified_model_name=None): + self.application = application + self.endpoints = endpoints + self.model = model + self.qualified_model_name = qualified_model_name + + def __eq__(self, other): + if not isinstance(other, OfferEndpoints): + return NotImplemented + return ( + self.application == other.application + and self.endpoints == other.endpoints + and self.model == other.model + and self.qualified_model_name == other.qualified_model_name + ) + + +def parse_offer_endpoint(endpoint): + if ":" not in endpoint: + raise ParseError( + 'endpoint must conform to format ":"' + ) + + matches = match_endpoint(endpoint, MatchType.SEARCH) + if matches is None: + raise ParseError( + 'endpoint must conform to format ":"' + ) + model_group = matches.group("model") + application_group = matches.group("appname") + endpoints_group = matches.group("endpoints") + + qualified_model_name = None + if (model_group is not None) and (model_group != ""): + if "/" not in model_group: + raise NotImplementedError() + qualified_model_name = model_group + model_group = model_group.split("/")[0] + + if (model_group is not None) and (not match_model(model_group)): + raise ParseError(f"model name {model_group}") + if not match_application(application_group): + raise ParseError(f"application name {application_group}") + + model = model_group + application = application_group + + endpoints = endpoints_group.split(",") + if len(endpoints) == 0 or len(endpoints_group) == 0: + raise ParseError(f"specify endpoints for {application}") + + return OfferEndpoints( + application, endpoints, model=model, qualified_model_name=qualified_model_name + ) + + +class OfferURL: + def __init__(self, source="", user="", model="", application=""): + self.source = source + self.user = user + self.model = model + self.application = application + + def __eq__(self, other): + if not isinstance(other, OfferURL): + return NotImplemented + return ( + self.source == other.source + and self.user == other.user + and self.model == other.model + and self.application == other.application + ) + + def has_empty_source(self): + return self.source is None or self.source == "" + + def has_endpoint(self): + return ":" in self.application + + def as_local(self): + return OfferURL("", self.user, self.model, self.application) + + def string(self): + parts = [] + if self.user != "": + parts.append(self.user) + if self.model != "": + parts.append(self.model) + path = "/".join(parts) + path = f"{path}.{self.application}" + if self.has_empty_source(): + return path + return f"{self.source}:{path}" + + +def parse_offer_url(url): + source, rest = maybe_parse_offer_url_source(url) + + valid = url[0] != ":" + valid = valid and match_model_application(rest) + if not valid: + raise ParseError( + f"application offer URL has invalid form, must be [.: {url}" + ) + + offer_source = source + + matches = match_model_application(rest, MatchType.SEARCH) + offer_user = _get_or_else(matches.group("user"), "") + offer_model = _get_or_else(matches.group("model"), "") + offer_application = _get_or_else(matches.group("application"), "") + + if valid and (("/" in offer_model) or ("/" in offer_application)): + raise ParseError( + f"application offer URL has invalid form, must be [.: {url}" + ) + if not offer_model: + raise ParseError("application offer URL is missing model") + if not offer_application: + raise ParseError("application offer URL is missing application") + + if offer_user and not match_user(offer_user): + raise ParseError(f"user name {offer_user} not valid") + if offer_model and not match_model(offer_model): + raise ParseError(f"model name {offer_model} not valid") + + app_name = offer_application.split(":")[0] + if app_name and not match_application(app_name): + raise ParseError(f"application name {app_name} not valid") + + return OfferURL( + source=offer_source, + user=offer_user, + model=offer_model, + application=offer_application, + ) + + +def _get_or_else(val, res): + if val is None: + return res + return val + + +def maybe_parse_offer_url_source(url): + parts = url.split(":") + if len(parts) > 2: + return parts[0], ":".join(parts[slice(1, len(parts))]) + elif len(parts) == 2: + if match_source_endpoint(parts[1]): + return "", url + return (parts[0], parts[1]) + return "", url + + +class LocalEndpoint: + def __init__(self, application="", relation=None): + self.application = application + self.relation = relation + + def __eq__(self, other): + if not isinstance(other, LocalEndpoint): + return NotImplemented + return self.relation == other.relation and self.application == other.application + + +def parse_local_endpoint(url): + application_name = url + relation = None + + if ":" in url: + if url[0] == ":" or url[-1] == ":": + raise ParseError(f"endpoint {url} not valid") + + parts = url.split(":") + if len(parts) != 2: + raise ParseError(f"endpoint {url} not valid") + + application_name = parts[0] + + if not match_relation(parts[1]): + raise ParseError(f"endpoint {url} not valid") + + relation = parts[1] + + if not match_application(application_name): + raise ParseError(f"endpoint {application_name} not valid") + + return LocalEndpoint(application=application_name, relation=relation) diff --git a/build/lib/juju/origin.py b/build/lib/juju/origin.py new file mode 100644 index 00000000..6d87ef62 --- /dev/null +++ b/build/lib/juju/origin.py @@ -0,0 +1,212 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +from enum import Enum + +from . import utils +from .errors import JujuError + + +class Source(Enum): + """Source defines a origin source. Providing a hint to the controller about + what the charm identity is from the URL and origin source. + + """ + + LOCAL = "local" + CHARM_HUB = "charm-hub" + + def __str__(self): + return self.value + + +class Origin: + def __init__(self, source, channel, platform): + self.source = source + self.channel = channel + self.platform = platform + + def __str__(self): + return f"origin using source {self.source!s} for channel {self.channel} and platform {self.platform}" + + +class Risk(Enum): + STABLE = "stable" + CANDIDATE = "candidate" + BETA = "beta" + EDGE = "edge" + + def __str__(self): + return self.value + + @staticmethod + def valid(potential): + for risk in [Risk.STABLE, Risk.CANDIDATE, Risk.BETA, Risk.EDGE]: + if str(risk) == potential: + return True + return False + + +class Channel: + """Channel identifies and describes completely a store channel. + + A channel consists of, and is subdivided by, tracks, risk-levels and + - Tracks enable snap developers to publish multiple supported releases of + their application under the same snap name. + - Risk-levels represent a progressive potential trade-off between stability + and new features. + + The complete channel name can be structured as three distinct parts separated + by slashes: + + / + + """ + + def __init__(self, track=None, risk=None): + if not Risk.valid(risk): + raise JujuError(f"unexpected risk {risk}") + + self.track = track or "" + self.risk = risk + + @staticmethod + def parse(s: str): + """Parse a channel from a given string. + Parse does not take into account branches. + + """ + if not s: + raise JujuError("channel cannot be empty") + + p = s.split("/") + + risk = None + track = None + if len(p) == 1: + if Risk.valid(p[0]): + risk = p[0] + else: + track = p[0] + risk = str(Risk.STABLE) + elif len(p) == 2: + track = p[0] + risk = p[1] + else: + raise JujuError(f"channel is malformed and has too many components {s}") + + if risk is not None and not Risk.valid(risk): + raise JujuError(f"risk in channel {s} is not valid") + if track is not None and track == "": + raise JujuError(f"track in channel {s} is not valid") + + return Channel(track, risk) + + def normalize(self): + track = self.track if self.track != "" else "" + risk = self.risk if self.risk != "" else "" + return Channel(track, risk) + + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.track == other.track and self.risk == other.risk + return False + + def __str__(self): + path = self.risk + if self.track != "": + path = f"{self.track}/{path}" + return path + + def compute_base_channel(self, series): + """Determines the channel for a client.Base + A base channel is a track/risk/branch + + """ + _ch = [self.risk] + tr = utils.get_series_version(series) + if tr: + _ch = [tr, *_ch] + return "/".join(_ch) + + +class Platform: + """ParsePlatform parses a string representing a store platform. + Serialized version of platform can be expected to conform to the following: + + 1. Architecture is mandatory. + 2. OS is optional and can be dropped. Series is mandatory if OS wants + to be displayed. + 3. Series is also optional. + + To indicate something is missing `unknown` can be used in place. + + Examples: + 1. `//` + 2. `` + 3. `/` + 4. `/unknown/` + + """ + + def __init__(self, arch, series=None, os=None): + self.arch = arch + self.series = series + self.os = os + + @staticmethod + def parse(s): + if not s: + raise JujuError("platform cannot be empty") + + p = s.split("/") + + arch = None + os = None + series = None + if len(p) == 1: + arch = p[0] + elif len(p) == 2: + arch = p[0] + series = p[1] + elif len(p) == 3: + arch = p[0] + os = p[1] + series = p[2] + else: + raise JujuError(f"platform is malformed and has too many components {s}") + + if not arch: + raise JujuError(f"architecture in platform {s} is not valid") + if os is not None and os == "": + raise JujuError(f"os in platform {s} is not valid") + if series is not None and series == "": + raise JujuError(f"series in platform {s} is not valid") + + return Platform(arch, series, os) + + def normalize(self): + os = self.os if self.os is not None or self.os != "unknown" else None + series = self.series + if series is None or series == "unknown": + os = None + series = None + + return Platform(self.arch, series, os) + + def __eq__(self, other): + if isinstance(other, self.__class__): + return ( + self.arch == other.arch + and self.os == other.os + and self.series == other.series + ) + return False + + def __str__(self): + path = self.arch + if self.os is not None and self.os != "": + path = f"{path}/{self.os}" + if self.series is not None and self.series != "": + path = f"{path}/{self.series}" + return path diff --git a/build/lib/juju/placement.py b/build/lib/juju/placement.py new file mode 100644 index 00000000..3dbc99a3 --- /dev/null +++ b/build/lib/juju/placement.py @@ -0,0 +1,60 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +# +# This module allows us to parse a machine placement directive into a +# Placement object suitable for passing through the websocket API. +# +# Once https://bugs.launchpad.net/juju/+bug/1645480 is addressed, this +# module should be deprecated. +# + +from .client import client + +MACHINE_SCOPE = "#" + + +def parse(directive): + """Given a string in the format `scope:directive`, or simply `scope` + or `directive`, return a Placement object suitable for passing + back over the websocket API. + + """ + if not directive: + # Handle null case + return None + + if isinstance(directive, (list, tuple)): + results = [] + for d in directive: + results.extend(parse(d)) + return results + + if isinstance(directive, (dict, client.Placement)): + # We've been handed something that we can simply hand back to + # the api. (Forwards compatibility) + return [directive] + + # Juju 2.0 can't handle lxc containers. + directive = directive.replace("lxc", "lxd") + + if ":" in directive: + # Planner has given us a scope and directive in string form + scope, directive = directive.split(":") + return [client.Placement(scope=scope, directive=directive)] + + if directive.isdigit(): + # Planner has given us a machine id (we rely on juju core to + # verify its validity.) + return [client.Placement(scope=MACHINE_SCOPE, directive=directive)] + + if "/" in directive: + # e.g. "0/lxd/0" + # https://github.com/juju/juju/blob/master/instance/placement_test.go#L29 + return [ + client.Placement(scope=MACHINE_SCOPE, directive=directive), + ] + + # Planner has probably given us a container type. Leave it up to + # juju core to verify that it is valid. + return [client.Placement(scope=directive)] diff --git a/build/lib/juju/provisioner.py b/build/lib/juju/provisioner.py new file mode 100644 index 00000000..9f034234 --- /dev/null +++ b/build/lib/juju/provisioner.py @@ -0,0 +1,344 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +import os +import re +import shlex +import tempfile +import uuid +from subprocess import CalledProcessError + +import paramiko + +from .client import client + +arches = [ + [re.compile(r"amd64|x86_64"), "amd64"], + [re.compile(r"i?[3-9]86"), "i386"], + [re.compile(r"(arm$)|(armv.*)"), "armhf"], + [re.compile(r"aarch64"), "arm64"], + [re.compile(r"ppc64|ppc64el|ppc64le"), "ppc64el"], + [re.compile(r"s390x?"), "s390x"], +] + + +def normalize_arch(raw_arch): + """Normalize the architecture string.""" + for arch in arches: + if arch[0].match(raw_arch): + return arch[1] + + +DETECTION_SCRIPT = """#!/bin/bash +set -e +os_id=$(grep '^ID=' /etc/os-release | tr -d '"' | cut -d= -f2) +if [ "$os_id" = 'centos' ]; then + os_version=$(grep '^VERSION_ID=' /etc/os-release | tr -d '"' | cut -d= -f2) + echo "centos$os_version" +else + lsb_release -cs +fi +uname -m +grep MemTotal /proc/meminfo +cat /proc/cpuinfo +""" + +INITIALIZE_UBUNTU_SCRIPT = """set -e +(id ubuntu &> /dev/null) || useradd -m ubuntu -s /bin/bash +umask 0077 +temp=$(mktemp) +echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > $temp +install -m 0440 $temp /etc/sudoers.d/90-juju-ubuntu +rm $temp +su ubuntu -c 'install -D -m 0600 /dev/null ~/.ssh/authorized_keys' +export authorized_keys="{}" +if [ ! -z "$authorized_keys" ]; then + su ubuntu -c 'echo $authorized_keys >> ~/.ssh/authorized_keys' +fi +""" + + +class SSHProvisioner: + """Provision a manually created machine via SSH.""" + + user = "" + host = "" + private_key_path = "" + + def __init__(self, user, host, private_key_path): + self.host = host + self.user = user + self.private_key_path = private_key_path + + def _get_ssh_client(self, host: str, user: str, key: str) -> paramiko.SSHClient: + """Return a connected Paramiko ssh object. + + :param str host: The host to connect to. + :param str user: The user to connect as. + :param str key: The private key to authenticate with. + + :return: object: A paramiko.SSHClient + :raises: :class:`paramiko.ssh_exception.SSHException` if the + connection failed + """ + ssh = paramiko.SSHClient() + # Ruff warns about insecure policy of automatically accepting unknown keys + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # noqa: S507 + + pkey = None + + # Read the private key into a paramiko.RSAKey + if os.path.exists(key): + with open(key) as f: + pkey = paramiko.RSAKey.from_private_key(f) + + ####################################################################### + # There is a bug in some versions of OpenSSH 4.3 (CentOS/RHEL5) where # + # the server may not send the SSH_MSG_USERAUTH_BANNER message except # + # when responding to an auth_none request. For example, paramiko will # + # attempt to use password authentication when a password is set, but # + # the server could deny that, instead requesting keyboard-interactive.# + # The hack to workaround this is to attempt a reconnect, which will # + # receive the right banner, and authentication can proceed. See the # + # following for more info: # + # https://github.com/paramiko/paramiko/issues/432 # + # https://github.com/paramiko/paramiko/pull/438 # + ####################################################################### + + try: + ssh.connect(host, port=22, username=user, pkey=pkey) + except paramiko.ssh_exception.SSHException as e: + if str(e) == "Error reading SSH protocol banner": + # Once more, with feeling + ssh.connect(host, port=22, username=user, pkey=pkey) + else: + # Reraise the original exception + raise e + + return ssh + + def _run_command( + self, ssh: paramiko.SSHClient, cmd: str | list[str], pty: bool = True + ) -> tuple[str, str]: + """Run a command remotely via SSH. + + :param object ssh: The SSHClient + :param str cmd: The command to execute + :param list cmd: The `shlex.split` command to execute + :param bool pty: Whether to allocate a pty + + :return: tuple: The stdout and stderr of the command execution + :raises: :class:`CalledProcessError` if the command fails + """ + if isinstance(cmd, str): + cmd = shlex.split(cmd) + + if isinstance(cmd, str): + cmds = cmd + else: + cmds = " ".join(cmd) + + _stdin, stdout, stderr = ssh.exec_command(cmds, get_pty=pty) + retcode = stdout.channel.recv_exit_status() + + if retcode > 0: + output = stderr.read().strip() + raise CalledProcessError(returncode=retcode, cmd=cmd, output=output) + return ( + stdout.read().decode("utf-8").strip(), + stderr.read().decode("utf-8").strip(), + ) + + def _init_ubuntu_user(self): + """Initialize the ubuntu user. + + :return: bool: If the initialization was successful + :raises: :class:`paramiko.ssh_exception.AuthenticationException` + if the authentication fails + """ + ssh = None + try: + # Run w/o allocating a pty, so we fail if sudo prompts for a passwd + ssh = self._get_ssh_client( + self.host, + self.user, + self.private_key_path, + ) + _stdout, _stderr = self._run_command(ssh, "sudo -n true", pty=False) + except paramiko.ssh_exception.AuthenticationException as e: + raise e + finally: + if ssh: + ssh.close() + + # Infer the public key + public_key = None + public_key_path = f"{self.private_key_path}.pub" + + if not os.path.exists(public_key_path): + raise FileNotFoundError(f"Public key '{public_key_path}' doesn't exist.") + + with open(public_key_path) as f: + public_key = f.readline() + + script = INITIALIZE_UBUNTU_SCRIPT.format(public_key) + + try: + ssh = self._get_ssh_client( + self.host, + self.user, + self.private_key_path, + ) + + self._run_command( + ssh, ["sudo", "/bin/bash -c " + shlex.quote(script)], pty=True + ) + except paramiko.ssh_exception.AuthenticationException as e: + raise e + finally: + ssh.close() + + return True + + def _detect_hardware_and_os(self, ssh): + """Detect the target hardware capabilities and OS series. + + :param object ssh: The SSHClient + :return: str: A raw string containing OS and hardware information. + """ + info = { + "series": "", + "arch": "", + "cpu-cores": "", + "mem": "", + } + + stdout, _stderr = self._run_command( + ssh, + ["sudo", "/bin/bash -c " + shlex.quote(DETECTION_SCRIPT)], + pty=True, + ) + + lines = stdout.split("\n") + info["series"] = lines[0].strip() + info["arch"] = normalize_arch(lines[1].strip()) + + mem_kb = re.split(r"\s+", lines[2])[1] + + # Convert to MB + info["mem"] = int(mem_kb) // 1024 + + # Detect available CPUs + recorded = {} + for line in lines[3:]: + physical_id = "" + print(line) + + if line.find("physical id") == 0: + physical_id = line.split(":")[1].strip() + elif line.find("cpu cores") == 0: + cores = line.split(":")[1].strip() + + if physical_id not in recorded: + info["cpu-cores"] += cores + recorded[physical_id] = True + + return info + + def provision_machine(self): + """Perform the initial provisioning of the target machine. + + :return: bool: The client.AddMachineParams + :raises: :class:`paramiko.ssh_exception.AuthenticationException` + if the upload fails + """ + params = client.AddMachineParams() + + if self._init_ubuntu_user(): + try: + ssh = self._get_ssh_client(self.host, self.user, self.private_key_path) + + hw = self._detect_hardware_and_os(ssh) + params.series = hw["series"] + params.instance_id = f"manual:{self.host}" + params.nonce = f"manual:{self.host}:{uuid.uuid4()}" + params.hardware_characteristics = { + "arch": hw["arch"], + "mem": int(hw["mem"]), + "cpu-cores": int(hw["cpu-cores"]), + } + params.addresses = [ + { + "value": self.host, + "type": "ipv4", + "scope": "public", + } + ] + + except paramiko.ssh_exception.AuthenticationException as e: + raise e + finally: + ssh.close() + + return params + + async def install_agent(self, connection, nonce, machine_id): + """:param object connection: Connection to Juju API + :param str nonce: The nonce machine specification + :param str machine_id: The id assigned to the machine + + :return: bool: If the initialization was successful + """ + # The path where the Juju agent should be installed. + data_dir = "/var/lib/juju" + + # Disabling this prevents `apt-get update` from running initially, so + # charms will fail to deploy + disable_package_commands = False + + facade_cls = client.MachineManagerFacade + if connection.is_using_old_client: + facade_cls = client.ClientFacade + + facade = facade_cls.from_connection(connection) + + results = await facade.ProvisioningScript( + data_dir=data_dir, + disable_package_commands=disable_package_commands, + machine_id=machine_id, + nonce=nonce, + ) + + self._run_configure_script(results.script) + + def _run_configure_script(self, script): + """Run the script to install the Juju agent on the target machine. + + :param str script: The script returned by the ProvisioningScript API + :raises: :class:`paramiko.ssh_exception.AuthenticationException` + if the upload fails + """ + with tempfile.NamedTemporaryFile("w") as tmp_file: + tmp_file.write(script) + tmp_file.flush() + + # get ssh client + ssh = self._get_ssh_client( + self.host, + "ubuntu", + self.private_key_path, + ) + + try: + # copy the local copy of the script to the remote machine + sftp = paramiko.SFTPClient.from_transport(ssh.get_transport()) + sftp.put(tmp_file.name, tmp_file.name) + + # run the provisioning script + _stdout, _stderr = self._run_command( + ssh, + f"sudo /bin/bash {tmp_file.name}", + ) + finally: + ssh.close() diff --git a/build/lib/juju/py.typed b/build/lib/juju/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/build/lib/juju/relation.py b/build/lib/juju/relation.py new file mode 100644 index 00000000..93e80f81 --- /dev/null +++ b/build/lib/juju/relation.py @@ -0,0 +1,147 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import logging + +from . import model +from .errors import JujuEntityNotFoundError + +log = logging.getLogger(__name__) + + +class Endpoint: + def __init__(self, model, data): + self.model = model + self.data = data + + def __repr__(self): + return "".format(self.data["application-name"], self.name) + + @property + def application_name(self): + return self.data["application-name"] + + @property + def application(self): + """Application returns the underlying application model from the state. + If no application is found, then a JujuEntityNotFoundError is raised, in + this scenario it is expected that you disconnect and reconnect to the + model. + """ + app_name = self.data["application-name"] + if app_name in self.model.applications: + return self.model.applications[app_name] + raise JujuEntityNotFoundError(app_name, ["application"]) + + @property + def name(self): + return self.data["relation"]["name"] + + @property + def interface(self): + return self.data["relation"]["interface"] + + @property + def role(self): + return self.data["relation"]["role"] + + @property + def scope(self): + return self.data["relation"]["scope"] + + +class Relation(model.ModelEntity): + def __repr__(self): + return f"" + + @property + def endpoints(self): + return [Endpoint(self.model, data) for data in self.safe_data["endpoints"]] + + @property + def provides(self): + """The endpoint on the provides side of this relation, or None.""" + for endpoint in self.endpoints: + if endpoint.role == "provider": + return endpoint + return None + + @property + def requires(self): + """The endpoint on the requires side of this relation, or None.""" + for endpoint in self.endpoints: + if endpoint.role == "requirer": + return endpoint + return None + + @property + def peers(self): + """The peers endpoint of this relation, or None.""" + for endpoint in self.endpoints: + if endpoint.role == "peer": + return endpoint + return None + + @property + def is_subordinate(self): + return any(ep.scope == "container" for ep in self.endpoints) + + @property + def is_peer(self): + return any(ep.role == "peer" for ep in self.endpoints) + + def matches(self, *specs): + """Check if this relation matches relationship specs. + + Relation specs are strings that would be given to Juju to establish a + relation, and should be in the form ``[:]`` + where the ``:`` suffix is optional. If the suffix is + omitted, this relation will match on any endpoint as long as the given + application is involved. + + In other words, this relation will match a spec if that spec could have + created this relation. + + :return: True if all specs match. + """ + + # Matches expects that the underlying application exists when it walks + # over the endpoints. + # This isn't directly required, but it validates that the framework + # has all the information available to it, when you walk over all the + # relations. + # The one exception is remote- applications aren't real + # applications in the general sense of a application, but are more akin + # to a shadow application. + def model_application_exists(app_name): + model_app_name = None + if app_name in self.model.applications: + model_app_name = self.model.applications[app_name].name + elif app_name in self.model.remote_applications: + model_app_name = self.model.remote_applications[app_name].name + elif app_name in self.model.application_offers: + model_app_name = self.model.application_offers[app_name].name + return model_app_name == app_name + + for spec in specs: + if ":" in spec: + app_name, endpoint_name = spec.split(":") + else: + app_name, endpoint_name = spec, None + for endpoint in self.endpoints: + if ( + app_name == endpoint.application_name + and model_application_exists(app_name) + and endpoint_name in (endpoint.name, None) + ): + # found a match for this spec, so move to next one + break + else: + # no match for this spec + return False + return True + + @property + def applications(self): + """All applications involved in this relation.""" + return [ep.application for ep in self.endpoints] diff --git a/build/lib/juju/remoteapplication.py b/build/lib/juju/remoteapplication.py new file mode 100644 index 00000000..4cd16cdc --- /dev/null +++ b/build/lib/juju/remoteapplication.py @@ -0,0 +1,38 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import logging + +from . import model, tag + +log = logging.getLogger(__name__) + + +class RemoteApplication(model.ModelEntity): + @property + def status(self): + """Get the application status, as set by the charm's leader.""" + return self.safe_data["status"]["current"] + + @property + def status_message(self): + """Get the application status message, as set by the charm's leader.""" + return self.safe_data["status"]["message"] + + @property + def tag(self): + return tag.application(self.name) + + +class ApplicationOffer(model.ModelEntity): + @property + def tag(self): + return tag.application(self.name) + + @property + def offer_name(self): + return self.safe_data["offer-name"] + + @property + def application_name(self): + return self.safe_data["application-name"] diff --git a/build/lib/juju/secrets.py b/build/lib/juju/secrets.py new file mode 100644 index 00000000..5a47f1ef --- /dev/null +++ b/build/lib/juju/secrets.py @@ -0,0 +1,124 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +"""A utility module for secrets such as reading secret data from yaml and creating data bag for secrets.""" + +import base64 +import json +import re +from pathlib import Path + +import yaml + +from . import errors + +file_suffix = "#file" +max_value_size_bytes = 5 * 1024 +max_content_size_bytes = 64 * 1024 + + +def create_secret_data(args): + """CreateSecretData creates a secret data bag from a list of arguments. + If a key has the #base64 suffix, then the value is already base64 encoded,otherwise the value is base64 encoded as it is added to the data bag. + + If a key has the '#file' suffix, the value is read from the corresponding file. + + :return []str: bag of key value pairs for a secret + """ + data = {} + for val in args: + # Remove any base64 padding ("=") before splitting the key=value. + stripped = val.rstrip(base64.b64encode(b"=").decode("utf-8")) + idx = stripped.find("=") + if idx < 1: + raise ValueError(f"Invalid key value {val}") + + key = stripped[0:idx] + value = stripped[idx + 1 :] + + # If the key doesn't have the #file suffix, then add it to the bag and continue. + if not key.endswith(file_suffix): + data[key] = value + continue + + key = key.rstrip(file_suffix) + path = Path(value).resolve() + try: + fs = path.stat() + if fs.st_size > max_value_size_bytes: + raise ValueError( + f"Secret content in file {path} too large: {fs.st_size} bytes" + ) + content = path.read_text() + data[key] = content + except Exception as e: + raise ValueError(f"Error processing key {key}: {e}") + + return encode_values_base64(data) + + +def read_secret_data(file): + """ReadSecretData reads secret data from a YAML or JSON file as key value pairs. + + :param file str: Path to a YAML or JSON file to read values from. + + :return []str: bag of key value pairs for a secret + """ + data = {} + path = Path(file).resolve() + + try: + fs = path.stat() + if fs.st_size > max_content_size_bytes: + raise ValueError( + f"Secret content in file {path} too large: {fs.st_size} bytes" + ) + except FileNotFoundError: + raise FileNotFoundError(f"The file {path} does not exist.") + except OSError: + raise + + try: + with open(path, encoding="utf-8") as file: + data = file.read() + except Exception: + raise + + try: + data = json.loads(data) + except json.JSONDecodeError: + try: + data = yaml.safe_load(data) + except yaml.YAMLError: + raise errors.JujuNotValid(f"Invalid data file at: {file}") + + return encode_values_base64(data) + + +base64_suffix = "#base64" +key_reg_exp = re.compile("^([a-z](?:-?[a-z0-9]){2,})$") + + +def encode_values_base64(data): + """Encodes the values in the given data bag for a secret + + If a key has the #base64 suffix, then the value is already base64 encoded,otherwise the value is base64 encoded as it is added to the data bag. + """ + out = {} + content_size = 0 + for k, v in data.items(): + if len(v) > max_value_size_bytes: + raise ValueError(f"secret content for key {k} too large: {len(v)} bytes") + content_size += len(v) + if k.endswith(base64_suffix): + k = k[:-7] + if not key_reg_exp.match(k): + raise ValueError(f"Not valid key: {k}") + out[k] = v + continue + if not key_reg_exp.match(k): + raise ValueError(f"Not valid key: {k}") + out[k] = base64.b64encode(str(v).encode()).decode() + if content_size > max_content_size_bytes: + raise ValueError(f"secret content too large: {content_size} bytes") + return out diff --git a/build/lib/juju/status.py b/build/lib/juju/status.py new file mode 100644 index 00000000..12336d47 --- /dev/null +++ b/build/lib/juju/status.py @@ -0,0 +1,252 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +import logging +import sys +import warnings + +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + from backports.strenum import StrEnum + +from .client import client + +log = logging.getLogger(__name__) + + +class StatusStr(StrEnum): + """Recognised status values. + + Please keep this set exact same as the severity map below. + """ + + error = "error" + blocked = "blocked" + waiting = "waiting" + maintenance = "maintenance" + active = "active" + terminated = "terminated" + unknown = "unknown" + + +""" severity_map holds status values with a severity measure. +Status values with higher severity are used in preference to others. +""" +severity_map: dict[StatusStr, int] = { + # FIXME: Juju defines a lot more status values #1204 + StatusStr.error: 100, + StatusStr.blocked: 90, + StatusStr.waiting: 80, + StatusStr.maintenance: 70, + StatusStr.active: 60, + StatusStr.terminated: 50, + StatusStr.unknown: 40, +} + + +def derive_status(statuses: list[str | StatusStr]) -> StatusStr: + """Derive status from a set. + + derive_status is used to determine the application status from a set of unit + status values. + + :param statuses: list of known unit workload statuses + """ + current: StatusStr = StatusStr.unknown + for status in statuses: + try: + status = StatusStr(status) + except ValueError: + # Unknown Juju status, let's assume it's least important + continue + + if severity_map[status] > severity_map[current]: + current = status + return current + + +async def formatted_status(model, target=None, raw=False, filters=None): + """Returns a string that mimics the content of the information + returned in the juju status command. If the raw parameter is + enabled, the function returns a FullStatus object. + :param Model model: model object to be used + :param Fileobject target: if set expects a file object such as + sys.stdout or a file descriptor. The obtained status will + be sent to the file using the write function. If set to + `None`, this function returns a string with the formatted + status. + :param bool raw: if `true` this functions returns the raw + `FullStatus` object returned by Juju. This is similar to + invoking `get_status`. + :param str filters: Optional list of applications, units, or machines + to include, which can use wildcards ('*'). + """ + warnings.warn( + "juju.status.formatted_status is deprecated, the implementation is likely broken", + DeprecationWarning, + stacklevel=2, + ) + client_facade = client.ClientFacade.from_connection(model.connection()) + result_status = await client_facade.FullStatus(patterns=filters) + + if raw: + result_str = str(result_status) + else: + result_str = _print_status_model(result_status) + result_str += "\n" + result_str += _print_status_apps(result_status) + result_str += "\n" + result_str += _print_status_units(result_status) + result_str += "\n" + result_str += _print_status_machines(result_status) + result_str += "\n" + if target is None: + return result_str + + try: + target.write(result_str) + except Exception as e: + logging.error(e) + + return None + + +def _print_status_model(result_status): + """Private function to print the status of a model""" + m = result_status.model + # print model + result_str = "{:<25} {:<25} {:<15} {:<15} {:<30} {:<30}\n".format( + "Model", "Cloud/Region", "Version", "SLA", "Timestamp", "Notes" + ) + sla = m.unknown_fields["sla"] + cloud = m.cloud_tag.split("-")[1] + timestamp = result_status.controller_timestamp + if m.available_version is not None and m.available_version != "": + available_version = f"upgrade available: {m.available_version}" + else: + available_version = "" + result_str += "{:<25} {:<25} {:<15} {:<15} {:<30} {:<30}".format( + m.name, cloud + "/" + m.region, m.version, sla, timestamp, available_version + ) + result_str += "\n" + return result_str + + +def _print_status_apps(result_status): + """Auxiliary function to print the apps received + in a status result + """ + apps = result_status.applications + if apps is None or len(apps) == 0: + return "" + + limits = "{:<25} {:<10} {:<10} {:<5} {:<20} {:<8}" + # print header + result_str = limits.format("App", "Version", "Status", "Scale", "Charm", "Channel") + + for name, app in apps.items(): + # extract charm name from the path + # like in ch:amd64/trusty/mediawiki-28 + charm_name = app.charm.split("/")[-1] + charm_name = charm_name.split("-")[0] + work_ver = "NA" if app.workload_version is None else app.workload_version + charm_channel = "NA" if app.charm_channel is None else app.charm_channel + app_units = "NA" if app.units is None else len(app.units) + app_status = "NA" if app.status.status is None else app.status.status + result_str += "\n" + result_str += limits.format( + name, work_ver, app_status, app_units, charm_name, charm_channel + ) + result_str += "\n" + return result_str + + +def _print_status_units(result_status): + """Auxiliary function to print the units received + in a status result + """ + apps = result_status.applications + if apps is None or len(apps) == 0: + return + + limits = "{:<15} {:<15} {:<20} {:<10} {:<15} {:<10} {:<30}" + summary = "" + for app in apps.values(): + units = app.units + if units is None or len(units) == 0: + continue + + for name, unit in units.items(): + addr = unit.public_address + if addr is None: + addr = "" + + if unit.opened_ports is None: + opened_ports = "" + else: + opened_ports = ",".join(unit.opened_ports) + + info = unit.workload_status.info + if info is None: + info = "" + + step = limits.format( + name, + unit.workload_status.status, + unit.agent_status.status, + unit.machine, + addr, + opened_ports, + info, + ) + if summary == "": + summary = step + else: + summary = summary + "\n" + step + + if len(summary) == 0: + return "" + result_str = limits.format( + "Unit", "Workload", "Agent", "Machine", "Public address", "Ports", "Message" + ) + result_str += "\n" + result_str += summary + result_str += "\n" + return result_str + + +def _print_status_machines(result_status): + machines = result_status.machines + if machines is None or len(machines) == 0: + return + + limits = "{:<15} {:<15} {:<15} {:<20} {:<15} {:<30}" + summary = "" + for name, machine in machines.items(): + dns = machine.dns_name + if dns is None: + dns = "" + step = limits.format( + name, + machine.agent_status.status, + dns, + machine.instance_id, + machine.series, + machine.agent_status.info, + ) + if summary == "": + summary = step + else: + summary = summary + "\n" + step + + if summary == "": + return + result_str = limits.format( + "Machine", "State", "DNS", "Inst id", "Series", "Message" + ) + result_str += "\n" + result_str += summary + result_str += "\n" + return result_str diff --git a/build/lib/juju/tag.py b/build/lib/juju/tag.py new file mode 100644 index 00000000..5057d639 --- /dev/null +++ b/build/lib/juju/tag.py @@ -0,0 +1,64 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +# TODO: Tags should be a proper class, so that we can distinguish whether +# something is already a tag or not. For example, 'user-foo' is a valid +# username, but is ambiguous with the already-tagged username 'foo'. + + +def _prefix(prefix: str, s: str) -> str: + if s and not s.startswith(prefix): + return f"{prefix}{s}" + return s + + +def untag(prefix: str, s: str) -> str: + if s and s.startswith(prefix): + return s[len(prefix) :] + return s + + +def cloud(cloud_name: str) -> str: + return _prefix("cloud-", cloud_name) + + +def controller(controller_uuid: str) -> str: + return _prefix("controller-", controller_uuid) + + +def credential(cloud: str, user: str, credential_name: str) -> str: + credential_string = f"{cloud}_{user}_{credential_name}" + return _prefix("cloudcred-", credential_string) + + +def model(model_uuid: str) -> str: + return _prefix("model-", model_uuid) + + +def machine(machine_id: str) -> str: + return _prefix("machine-", machine_id) + + +def user(username: str) -> str: + return _prefix("user-", username) + + +def application(app_name: str) -> str: + return _prefix("application-", app_name) + + +def storage(app_name: str) -> str: + return _prefix("storage-", app_name) + + +def unit(unit_name: str) -> str: + return _prefix("unit-", unit_name.replace("/", "-")) + + +def action(action_uuid: str) -> str: + return _prefix("action-", action_uuid) + + +def space(space_name: str) -> str: + return _prefix("space-", space_name) diff --git a/build/lib/juju/unit.py b/build/lib/juju/unit.py new file mode 100644 index 00000000..4cb8e259 --- /dev/null +++ b/build/lib/juju/unit.py @@ -0,0 +1,446 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import logging + +import pyrfc3339 + +from juju.errors import JujuAPIError, JujuError + +from . import model, tag +from .annotationhelper import _get_annotations, _set_annotations +from .client import client + +log = logging.getLogger(__name__) + + +class Unit(model.ModelEntity): + name: str + + @property + def agent_status(self): + """Returns the current agent status string.""" + return self.safe_data["agent-status"]["current"] + + @property + def agent_status_since(self): + """Get the time when the `agent_status` was last updated.""" + return pyrfc3339.parse(self.safe_data["agent-status"]["since"]) + + @property + def is_subordinate(self): + """True if the unit is subordinate of another unit""" + return self.safe_data["subordinate"] + + @property + def principal_unit(self): + """Returns the name of the unit of which this unit is a subordinate to. + Returns '' for principal units themselves. + """ + return self.safe_data["principal"] + + @property + def agent_status_message(self): + """Get the agent status message.""" + return self.safe_data["agent-status"]["message"] + + @property + def workload_status(self): + """Returns the current workload status string.""" + return self.safe_data["workload-status"]["current"] + + @property + def workload_status_since(self): + """Get the time when the `workload_status` was last updated.""" + return pyrfc3339.parse(self.safe_data["workload-status"]["since"]) + + @property + def workload_status_message(self): + """Get the workload status message.""" + return self.safe_data["workload-status"]["message"] + + @property + def machine(self): + """Get the machine object for this unit.""" + machine_id = self.safe_data["machine-id"] + if machine_id: + return self.model.machines.get(machine_id, None) + else: + return None + + @property + def public_address(self): + """Get the public address. + + This property is deprecated, use get_public_address method. + """ + return self.safe_data["public-address"] or None + + @property + def tag(self): + return tag.unit(self.name) + + def get_subordinates(self): + """Returns the unit objects that are subordinates to this unit + + :return [Unit] + """ + return [ + u + for u_name, u in self.model.units.items() + if u.is_subordinate and u.principal_unit == self.name + ] + + async def destroy( + self, destroy_storage=False, dry_run=False, force=False, max_wait=None + ): + """Destroy this unit.""" + app_facade = client.ApplicationFacade.from_connection(self.connection) + + log.debug("Destroying %s", self.name) + + return await app_facade.DestroyUnit( + units=[ + { + "unit-tag": self.tag, + "destroy-storage": destroy_storage, + "force": force, + "max-wait": max_wait, + "dry-run": dry_run, + } + ] + ) + + remove = destroy + + async def get_public_address(self): + """Return the public address of this unit. + + :return int public-address + """ + addr = self.safe_data["public-address"] or None + if addr is not None: + return addr + + app_facade = client.ApplicationFacade.from_connection(self.connection) + def_result = await app_facade.UnitsInfo(entities=[client.Entity(self.tag)]) + if def_result is not None and len(def_result.results) > 1: + raise JujuAPIError("expected one result") + return def_result.results[0].result.get("public-address", None) + + async def resolved(self, retry=False): + """Mark unit errors resolved. + + :param bool retry: Re-execute failed hooks + :returns: A :class:`juju.client._definitions.ErrorResults` instance. + """ + app_facade = client.ApplicationFacade.from_connection(self.connection) + + log.debug("Resolving %s", self.name) + + return await app_facade.ResolveUnitErrors( + all_=False, retry=retry, tags={"entities": [{"tag": self.tag}]} + ) + + async def add_storage(self, storage_name, pool=None, count=1, size=1024): + """Creates a storage and adds it to this unit. + + :param: str storage_name: Name of the storage + :param: str pool: the storage pool to provision storage instances from. Must + be a name from 'juju storage-pools'. The default pool is available via + executing 'juju model-config storage-default-block-source'. + :param: int count: the number of storage instances to provision from of + . Must be a positive integer. The default count is "1". May be restricted + by the charm, which can specify a maximum number of storage instances per unit. + :param: int size: the required size of the storage instance, in MiB. + + :return: []str storage_tags + """ + constraints = client.StorageConstraints(count=count, size=size) + if pool: + constraints = client.StorageConstraints(pool=pool, count=count, size=size) + + storage_facade = client.StorageFacade.from_connection(self.connection) + res = await storage_facade.AddToUnit( + storages=[ + client.StorageAddParams( + name=storage_name, + unit=self.tag, + storage=constraints, + ) + ] + ) + result = res.results[0] + if result.error is not None: + raise JujuError(f"{result.error}") + storage_details = result.result + return storage_details.storage_tags + + async def attach_storage(self, storage_ids=[]): + """Attaches existing storage to this unit. + + :param [str] storage_ids: existing storage ids to attach to the unit + :return: + """ + if not storage_ids: + raise JujuError(f"Expected a storage ID to be attached to unit {self.name}") + + storage_facade = client.StorageFacade.from_connection(self.connection) + return await storage_facade.Attach( + ids=[ + client.StorageAttachmentId( + storage_tag=tag.storage(s_id), + unit_tag=self.tag, + ) + for s_id in storage_ids + ] + ) + + async def detach_storage(self, *storage_ids, force=False): + """Detaches storage from units. + + :param bool force: Forcefully detach storage + :param [str] storage_ids: + :return: + """ + if not storage_ids: + raise JujuError("Expected at least one storage ID") + + storage_facade = client.StorageFacade.from_connection(self.connection) + ret = await storage_facade.DetachStorage( + force=force, + ids=client.StorageAttachmentIds( + ids=[ + client.StorageAttachmentId( + storage_tag=tag.storage(s), + unit_tag=self.tag, + ) + for s in storage_ids + ] + ), + ) + if ret.results[0].error: + raise JujuError(ret.results[0].error.message) + + async def run(self, command, timeout=None, block=False): + """Run command on this unit. + + :param str command: The command to run + :param int timeout: Time, in seconds, to wait before command is + considered failed + :param bool block: A flag to use this function in synchronized fashion. + Useful with older versions of juju, i.e. getting the result without + having to call ``action.wait()`` separately. + :returns: A :class:`juju.action.Action` instance. + + Note that this is very similarly to unit.run_action only enqueues the action. + You will need to call ``action.wait()`` on the resulting `Action` instance + if you wish to block until the action is complete. + + """ + action = client.ActionFacade.from_connection(self.connection) + + log.debug("Running `%s` on %s", command, self.name) + + if timeout: + # Convert seconds to nanoseconds + timeout = int(timeout * 1000000000) + + # It's not enough to only check for the old_client on the connection here + # The old client's ActionFacade is updated to version 7, so + # 2.9 track client may be using either ActionFacade v6 or v7 too + old_facade = client.ActionFacade.best_facade_version(self.connection) <= 6 + + res = await action.Run( + applications=[], + commands=command, + machines=[], + timeout=timeout, + units=[self.name], + ) + + action_result = res.results[0] if old_facade else res.actions[0] + action = action_result.action + + action_id = action.tag + if action_id.startswith("action-"): + # strip the action- part of "action-" tag + action_id = action_id[7:] + + error = action_result.error + if error: + raise JujuError(f"Action error - {error.code} : {error.message}") + + action = await self.model._wait_for_new("action", action_id) + if block: + return await action.wait() + return action + + async def run_action(self, action_name, **params): + """Run an action on this unit. + + :param str action_name: Name of action to run + :param **params: Action parameters + :returns: A :class:`juju.action.Action` instance. + + Note that this only enqueues the action. You will need to call + ``action.wait()`` on the resulting `Action` instance if you wish + to block until the action is complete. + + """ + action_facade = client.ActionFacade.from_connection(self.connection) + log.debug("Starting action `%s` on %s", action_name, self.name) + + old_client = self.connection.is_using_old_client + + op = action_facade.Enqueue if old_client else action_facade.EnqueueOperation + res = await op( + actions=[ + client.Action( + name=action_name, + parameters=params, + receiver=self.tag, + ) + ] + ) + + _action = res.results[0] if old_client else res.actions[0] + action = _action.action + error = _action.error + + if error and error.code == "not found": + raise ValueError("Action `%s` not found on %s" % (action_name, self.name)) + elif error: + raise Exception("Unknown action error: %s" % error.serialize()) + action_id = action.tag[len("action-") :] + log.debug("Action started as %s", action_id) + # we mustn't use wait_for_action because that blocks until the + # action is complete, rather than just being in the model + return await self.model._wait_for_new("action", action_id) + + async def scp_to( + self, source, destination, user="ubuntu", proxy=False, scp_opts="" + ): + """Transfer files to this unit. + + :param str source: Local path of file(s) to transfer + :param str destination: Remote destination of transferred files + :param str user: Remote username + :param bool proxy: Proxy through the Juju API server + :param scp_opts: Additional options to the `scp` command + :type scp_opts: str or list + """ + await self.machine.scp_to( + source, destination, user=user, proxy=proxy, scp_opts=scp_opts + ) + + async def scp_from( + self, source, destination, user="ubuntu", proxy=False, scp_opts="" + ): + """Transfer files from this unit. + + :param str source: Remote path of file(s) to transfer + :param str destination: Local destination of transferred files + :param str user: Remote username + :param bool proxy: Proxy through the Juju API server + :param scp_opts: Additional options to the `scp` command + :type scp_opts: str or list + """ + await self.machine.scp_from( + source, destination, user=user, proxy=proxy, scp_opts=scp_opts + ) + + async def ssh(self, command, user="ubuntu", proxy=False, ssh_opts=None): + """Execute a command over SSH on this unit. + + :param str command: Command to execute + :param str user: Remote username + :param bool proxy: Proxy through the Juju API server + :param str ssh_opts: Additional options to the `ssh` command + + """ + return await self.machine.ssh(command, user, proxy, ssh_opts) + + async def is_leader_from_status(self): + """Check to see if this unit is the leader. Returns True if so, and + False if it is not, or if leadership does not make sense + (e.g., there is no leader in this application.) + + This method is a kluge that calls FullStatus in the + ClientFacade to get its information. Once + https://bugs.launchpad.net/juju/+bug/1643691 is resolved, we + should add a simple .is_leader property, and deprecate this + method. + + """ + unit_parts = self.name.split("/") + app = unit_parts[0] + + client_facade = client.ClientFacade.from_connection(self.connection) + + status = await client_facade.FullStatus(patterns=None) + # FullStatus may be more up to date than our model, and the + # unit may have gone away, or we may be doing something silly, + # like trying to fetch leadership for a subordinate, which + # will not be filed where we expect in the model. In those + # cases, we may simply return False, as a nonexistent or + # subordinate unit is not a leader. + if not status.applications.get(app): + return False + + # We will attempt to look in two places for a leader property based on + # if the unit is subordinate or not. These variables allow for more + # generic non discriminate checks + target_apps = [app] + is_subordinate = False + + # Is the application a subordinate? If so change our data variables to + # the parent + if status.applications[app].subordinate_to: + is_subordinate = True + target_apps = status.applications[app].subordinate_to + + for target_app in target_apps: + app_data = status.applications[target_app] + + if not app_data.units: + continue + + if app_data.units.get(self.name): + is_leader = app_data.units[self.name].leader + return is_leader if is_leader else False + + if not is_subordinate: + continue + + for unit in app_data.units.values(): + if unit.subordinates and unit.subordinates.get(self.name): + is_leader = unit.subordinates[self.name].leader + return is_leader if is_leader else False + + return False + + async def get_metrics(self): + """Get metrics for the unit. + + :return: Dictionary of metrics for this unit. + + """ + metrics = await self.model.get_metrics(self.tag) + return metrics[self.name] + + async def get_annotations(self): + """Get annotations on this unit. + + :return dict: The annotations for this unit + """ + return await _get_annotations(self.tag, self.connection) + + async def set_annotations(self, annotations): + """Set annotations on this unit. + + :param annotations map[string]string: the annotations as key/value + pairs. + + """ + return await _set_annotations(self.tag, annotations, self.connection) diff --git a/build/lib/juju/url.py b/build/lib/juju/url.py new file mode 100644 index 00000000..3ad90546 --- /dev/null +++ b/build/lib/juju/url.py @@ -0,0 +1,188 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +from enum import Enum +from urllib.parse import urlparse + +from .errors import JujuError + + +class Schema(Enum): + """Charm URL schema kinds.""" + + LOCAL = "local" + CHARM_STORE = "cs" + CHARM_HUB = "ch" + + def matches(self, potential): + return str(self.value) == str(potential) + + def __str__(self): + return str(self.value) + + +class URL: + """Private URL class for this library internals only. + + Should be instantiated by `URL.parse` constructor. + """ + + name: str + + def __init__( + self, + schema, + user=None, + name: str | None = None, + revision=None, + series=None, + architecture=None, + ): + self.schema = schema + self.user = user + # the parse method will set the correct value later + self.name = name # type: ignore + self.series = series + + # 0 can be a valid revision, hence the more verbose check. + if revision is None: + revision = -1 + self.revision = revision + self.architecture = architecture + + @staticmethod + def parse(s: str, default_store=Schema.CHARM_HUB) -> URL: + """Parse parses the provided charm URL string into its respective + structure. + + A missing schema is assumed to be 'ch'. + + """ + u = urlparse(s) + if u.query != "" or u.fragment != "" or u.username or u.password: + raise JujuError(f"charm or bundle URL {u} has unrecognized parts") + + if Schema.CHARM_STORE.matches(u.scheme) or ( + u.scheme == "" and Schema.CHARM_STORE.matches(default_store) + ): + c = parse_v1_url(Schema.CHARM_STORE, u, s) + else: + c = parse_v2_url(u, s, default_store) + + if not c or not c.schema: + raise JujuError(f"expected schema for charm or bundle URL {u}") + return c + + def with_revision(self, rev): + return URL( + self.schema, self.user, self.name, rev, self.series, self.architecture + ) + + def with_series(self, series): + return URL( + self.schema, self.user, self.name, self.revision, series, self.architecture + ) + + def path(self): + parts = [] + if self.user is not None: + parts.append(f"~{self.user}") + if self.architecture is not None: + parts.append(self.architecture) + if self.series is not None: + parts.append(self.series) + if self.revision is not None and self.revision >= 0: + parts.append(f"{self.name}-{self.revision}") + else: + parts.append(self.name) + return "/".join(parts) + + def __eq__(self, other): + if isinstance(other, self.__class__): + return ( + self.schema == other.schema + and self.user == other.user + and self.name == other.name + and self.revision == other.revision + and self.series == other.series + and self.architecture == other.architecture + ) + return False + + def __str__(self): + return f"{self.schema!s}:{self.path()}" + + +def parse_v1_url(schema, u, s) -> URL: + c = URL(schema) + + parts = u.path.split("/") + if len(parts) < 1 or len(parts) > 4: + raise JujuError(f"charm or bundle URL has invalid form {s}") + + # ~ + if parts[0].startswith("~"): + if schema == Schema.LOCAL: + raise JujuError(f"local charm or bundle URL with username {s}") + c.user = parts[0][1:] + parts = parts[1:] + + if len(parts) > 2: + raise JujuError(f"charm or bundle URL has invalid form {s}") + + # + if len(parts) == 2: + c.series = parts[0] + parts = parts[1:] + # TODO (stickupkid) - validate the series. + + if len(parts) < 1: + raise JujuError(f"URL without charm or bundle name {s}") + + (c.name, c.revision) = extract_revision(parts[0]) + # TODO (stickupkid) - validate the name. + + return c + + +def parse_v2_url(u, s, default_store) -> URL: + if not u.scheme: + c = URL(default_store) + elif Schema.CHARM_HUB.matches(u.scheme): + c = URL(Schema.CHARM_HUB) + elif Schema.LOCAL.matches(u.scheme): + c = URL(Schema.LOCAL) + else: + raise JujuError(f"invalid charm url schema {u.scheme}") + + parts = u.path.split("/") + num = len(parts) + if num == 0 or num > 3: + raise JujuError(f"charm or bundle URL {s} malformed") + + name = "" + if num == 3: + c.architecture, c.series, name = parts[0], parts[1], parts[2] + elif num == 2: + c.architecture, name = parts[0], parts[1] + else: + name = parts[0] + + (c.name, c.revision) = extract_revision(name) + # TODO (stickupkid) - validate the name. + + return c + + +def extract_revision(name): + revision = -1 + for i in range(len(name) - 1, -1, -1): + c = name[i] + if c.isnumeric(): + continue + if c == "-" and i != (len(name) - 1): + revision = int(name[(i + 1) :]) + name = name[:i] + break + return (name, revision) diff --git a/build/lib/juju/user.py b/build/lib/juju/user.py new file mode 100644 index 00000000..7ff0fcc3 --- /dev/null +++ b/build/lib/juju/user.py @@ -0,0 +1,183 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. + +import logging + +import pyrfc3339 + +from . import errors, tag +from .client import client + +log = logging.getLogger(__name__) + + +class User: + def __init__(self, controller, user_info, secret_key=None): + self.controller = controller + self._user_info = user_info + self._secret_key = secret_key + + @property + def tag(self): + return tag.user(self.username) + + @property + def username(self): + return self._user_info.username + + @property + def display_name(self): + return self._user_info.display_name + + @property + def last_connection(self): + return pyrfc3339.parse(self._user_info.last_connection) + + @property + def access(self): + """Identifies the controller access levels of this user""" + return self._user_info.access + + @property + def date_created(self): + return self._user_info.date_created + + @property + def enabled(self): + return not self._user_info.disabled + + @property + def disabled(self): + return self._user_info.disabled + + @property + def created_by(self): + return self._user_info.created_by + + @property + def secret_key(self): + return self._secret_key + + async def set_password(self, password): + """Update this user's password.""" + await self.controller.change_user_password(self.username, password) + self._user_info.password = password + + async def modify_model_access(self, acl, action, model_name): + """Grants or revokes the given access level for this user for a given model + + :param str acl: Model access levels (see access module) + :param str action: grant/revoke + :param str model_name: Name of the model + + :return bool: True if access changed, Error if user already has it + """ + modelmanager_facade = client.ModelManagerFacade.from_connection( + self.controller.connection() + ) + models = await self.controller.model_uuids() + if model_name not in models: + raise errors.JujuError(f"Unable to find model : {model_name}") + changes = client.ModifyModelAccess( + acl, action, tag.model(models[model_name]), self.tag + ) + await modelmanager_facade.ModifyModelAccess(changes=[changes]) + return True + + async def modify_controller_access(self, acl, action): + """Grants or revokes the given access level for this user on the current controller + + :param str acl: Controller access levels (see access module) + :param str action: grant/revoke + + :return bool: True if access changed, Error if user already has it + """ + controller_facade = client.ControllerFacade.from_connection( + self.controller.connection() + ) + changes = client.ModifyControllerAccess(acl, action, self.tag) + await controller_facade.ModifyControllerAccess(changes=[changes]) + + new_access = acl + if action == "revoke": + new_access = "" + self._user_info.access = new_access + return True + + async def modify_offer_access(self, acl, action, offer_url): + """Grants or revokes the given access level for this user on a given offer + + :param str acl: Controller access levels (see access module) + :param str action: grant/revoke + :param str offer_url: url for the offer + + :return bool: True if access changed, Error if user already has it + """ + application_offers_facade = client.ApplicationOffersFacade.from_connection( + self.controller.connection() + ) + changes = client.ModifyOfferAccess(acl, action, offer_url, self.tag) + await application_offers_facade.ModifyOfferAccess(changes=[changes]) + return True + + async def grant_or_revoke(self, acl, action, **kwargs): + """Grants or revokes the given access level of this user on model, offer or controller, + depending on the access level (see the access module) + + :param str acl: Access control level + :param str action: 'grant' or 'revoke' + + Depending on the access level, the available keyword parameters are: + :param str model_name: name of the model if acl is one of model access levels + :param str offer_url: url for the offer if acl is one of offer access levels + + :return: True if access changed, False if user already has it + """ + try: + if "model_name" in kwargs: + return await self.modify_model_access(acl, action, kwargs["model_name"]) + elif "offer_url" in kwargs: + return await self.modify_offer_access(acl, action, kwargs["offer_url"]) + else: + return await self.modify_controller_access(acl, action) + except errors.JujuError as e: + if "user already has" in str(e): + return False + else: + raise + + async def grant(self, acl, **kwargs): + """Grant the given access level of this user on model, offer or controller, depending on + the access level (see the access module) + + :param str acl: Access control level + + Depending on the access level, the available keyword parameters are: + :param str model_name: name of the model if acl is one of model access levels + :param str offer_url: url for the offer if acl is one of offer access levels + + :return: None or Error + """ + return await self.grant_or_revoke(acl, "grant", **kwargs) + + async def revoke(self, acl="login", **kwargs): + """The opposite of user.grant(). Revokes the given access level of this user on model, + offer or controller, depending on the given access level. + + :param str acl: Access control level (see access module) + + Available keyword parameters are: + :param str model_name: name of the model if acl is one of model access levels + :param str offer_url: url for the offer if acl is one of offer access levels + """ + return await self.grant_or_revoke(acl, "revoke", **kwargs) + + async def disable(self): + """Disable this user.""" + await self.controller.disable_user(self.username) + self._user_info.disabled = True + + async def enable(self): + """Re-enable this user.""" + await self.controller.enable_user(self.username) + self._user_info.disabled = False diff --git a/build/lib/juju/utils.py b/build/lib/juju/utils.py new file mode 100644 index 00000000..710fcc56 --- /dev/null +++ b/build/lib/juju/utils.py @@ -0,0 +1,602 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +from __future__ import annotations + +import asyncio +import base64 +import os +import textwrap +import zipfile +from collections import defaultdict +from pathlib import Path +from typing import Any + +import yaml +from pyasn1.codec.der.encoder import encode +from pyasn1.type import char, univ + +from . import errors, jasyncio, origin +from .client import client +from .errors import JujuError + + +async def execute_process(*cmd, log=None): + """Wrapper around asyncio.create_subprocess_exec.""" + p = await asyncio.create_subprocess_exec( + *cmd, + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await p.communicate() + if log: + log.debug("Exec %s -> %d", cmd, p.returncode) + if stdout: + log.debug(stdout.decode("utf-8")) + if stderr: + log.debug(stderr.decode("utf-8")) + return p.returncode == 0 + + +def juju_config_dir(): + """Resolves and returns the path string to the juju configuration folder + for the juju CLI tool. Of the following items, returns the first option + that works (top to bottom): + + * $JUJU_DATA + * $XDG_DATA_HOME/juju + * ~/.local/share/juju + """ + # Set it to ~/.local/share/juju as default + config_dir = Path("~/.local/share/juju") + + # Check $JUJU_DATA + if juju_data := os.environ.get("JUJU_DATA"): + config_dir = Path(juju_data) + # Secondly check: $XDG_DATA_HOME for ~/.local/share + elif xdg_data_home := os.environ.get("XDG_DATA_HOME"): + config_dir = Path(xdg_data_home) / "juju" + + return str(config_dir.expanduser().resolve()) + + +def juju_ssh_key_paths(): + """Resolves and returns the path strings for public and private ssh keys + for juju CLI. + """ + config_dir = juju_config_dir() + public_key_path = os.path.join(config_dir, "ssh", "juju_id_rsa.pub") + private_key_path = os.path.join(config_dir, "ssh", "juju_id_rsa") + + return public_key_path, private_key_path + + +def _read_ssh_key(): + """Inner function for read_ssh_key, suitable for passing to our + Executor. + """ + public_key_path_str, _ = juju_ssh_key_paths() + ssh_key_path = Path(public_key_path_str) + with ssh_key_path.open("r") as ssh_key_file: + ssh_key = ssh_key_file.readlines()[0].strip() + return ssh_key + + +async def read_ssh_key(): + """Attempt to read the local juju admin's public ssh key, so that it can be + passed on to a model. + """ + loop = asyncio.get_running_loop() + return await loop.run_in_executor(None, _read_ssh_key) + + +class IdQueue: + """Wrapper around asyncio.Queue that maintains a separate queue for each + ID. + """ + + _queues: dict[int, asyncio.Queue[dict[str, Any] | Exception]] + + def __init__(self): + self._queues = defaultdict(asyncio.Queue) + # FIXME cleanup needed. + # in some cases an Exception is put into the queue. + # if the main coro exits, this exception will be logged as "never awaited" + # we gotta do something about that to keep the output clean. + # + # Additionally, it's conceivable that a response is put in the queue + # and then an exception is put via put_all() + # the reader only ever fetches one item, and exception is "never awaited" + # rewrite put_all to replace the pending response instead. + + async def get(self, id_: int) -> dict[str, Any]: + value = await self._queues[id_].get() + del self._queues[id_] + if isinstance(value, Exception): + raise value + return value + + async def put(self, id_: int, value: dict[str, Any]): + await self._queues[id_].put(value) + + async def put_all(self, value: Exception): + for queue in self._queues.values(): + await queue.put(value) + + +async def block_until(*conditions, timeout=None, wait_period=0.5): + """Return only after all conditions are true. + + If a timeout occurs, it cancels the task and raises + asyncio.TimeoutError. + """ + + async def _block(): + while not all(c() for c in conditions): + await asyncio.sleep(wait_period) + + await asyncio.shield(asyncio.wait_for(_block(), timeout)) + + +async def block_until_with_coroutine( + condition_coroutine, timeout=None, wait_period=0.5 +): + """Return only after the given coroutine returns True. + + If a timeout occurs, it cancels the task and raises + asyncio.TimeoutError. + """ + + async def _block(): + while not await condition_coroutine(): + await asyncio.sleep(wait_period) + + await asyncio.shield(asyncio.wait_for(_block(), timeout=timeout)) + + +async def wait_for_bundle(model, bundle: str | Path, **kwargs) -> None: + """Helper to wait for just the apps in a specific bundle. + + Equivalent to loading the bundle, pulling out the app names, and calling:: + + await model.wait_for_idle(app_names, **kwargs) + """ + try: + bundle_path = Path(bundle) + if bundle_path.is_file(): + bundle = bundle_path.read_text() + elif (bundle_path / "bundle.yaml").is_file(): + bundle = bundle_path / "bundle.yaml" + except OSError: + pass + content: dict[str, Any] = yaml.safe_load(textwrap.dedent(bundle).strip()) + apps = list(content.get("applications", content.get("services")).keys()) + await model.wait_for_idle(apps, **kwargs) + + +async def run_with_interrupt(task, *events, log=None): + """Awaits a task while allowing it to be interrupted by one or more + `asyncio.Event`s. + + If the task finishes without the events becoming set, the results of the + task will be returned. If the event become set, the task will be cancelled + ``None`` will be returned. + + :param task: Task to run + :param events: One or more `asyncio.Event`s which, if set, will interrupt + `task` and cause it to be cancelled. + """ + task = jasyncio.create_task_with_handler(task, "tmp", log) + event_tasks = [jasyncio.ensure_future(event.wait()) for event in events] + done, pending = await jasyncio.wait( + [task, *event_tasks], return_when=jasyncio.FIRST_COMPLETED + ) + for f in pending: + f.cancel() # cancel unfinished tasks + for f in pending: + try: + await f + except jasyncio.CancelledError: + pass + for f in done: + f.exception() # prevent "exception was not retrieved" errors + if task in done: + return task.result() # may raise exception + else: + return None + + +class Addrs(univ.SequenceOf): + """Internal.""" + + componentType = char.PrintableString() + + +class RegistrationInfo(univ.Sequence): + """ASN.1 representation of: + + type RegistrationInfo struct { User string + + Addrs []string + + SecretKey []byte + + ControllerName string } + """ + + pass + + +def generate_user_controller_access_token( + username, controller_endpoints, secret_key, controller_name +): + """Implement in python what is currently done in GO. + + https://github.com/juju/juju/blob/a5ab92e/cmd/juju/user/utils.go#L16 + + :param username: name of the user to register + :param controller_endpoints: juju controller endpoints list in the format : + :param secret_key: base64 encoded string of the secret-key generated by juju + :param controller_name: name of the controller to register to. + """ + # Secret key is returned as base64 encoded string in: + # https://websockets.readthedocs.io/en/stable/_modules/websockets/protocol.html#WebSocketCommonProtocol.recv + # Decoding it before marshalling into the ASN.1 message + secret_key = base64.b64decode(secret_key) + addr = Addrs() + for endpoint in controller_endpoints: + addr.append(endpoint) + + registration_string = RegistrationInfo() + registration_string.setComponentByPosition(0, char.PrintableString(username)) + registration_string.setComponentByPosition(1, addr) + registration_string.setComponentByPosition(2, univ.OctetString(secret_key)) + registration_string.setComponentByPosition(3, char.PrintableString(controller_name)) + registration_string = encode(registration_string) + remainder = len(registration_string) % 3 + registration_string += b"\0" * (3 - remainder) + return base64.urlsafe_b64encode(registration_string) + + +def get_local_charm_data(path, yaml_file): + """Retrieve Metadata of a Charm from its path. + + :patam str path: Path of charm directory or .charm file :patam str + yaml_ + file: + name of the yaml file, can be either "metadata.yaml", or + "manifest.yaml", or "charmcraft.yaml" + + :return: Object of charm metadata + """ + if str(path).endswith(".charm"): + with zipfile.ZipFile(str(path), "r") as charm_file: + metadata = yaml.load(charm_file.read(yaml_file), Loader=yaml.FullLoader) + else: + entity_path = Path(path) + metadata_path = entity_path / yaml_file + if not metadata_path.exists(): + return {} + metadata = yaml.load(metadata_path.read_text(), Loader=yaml.FullLoader) + + return metadata + + +def get_local_charm_metadata(path): + return get_local_charm_data(path, "metadata.yaml") + + +def get_local_charm_manifest(path): + return get_local_charm_data(path, "manifest.yaml") + + +def get_local_charm_charmcraft_yaml(path): + return get_local_charm_data(path, "charmcraft.yaml") + + +PRECISE = "precise" +QUANTAL = "quantal" +RARING = "raring" +SAUCY = "saucy" +TRUSTY = "trusty" +UTOPIC = "utopic" +VIVID = "vivid" +WILY = "wily" +XENIAL = "xenial" +YAKKETY = "yakkety" +ZESTY = "zesty" +ARTFUL = "artful" +BIONIC = "bionic" +COSMIC = "cosmic" +DISCO = "disco" +EOAN = "eoan" +FOCAL = "focal" +GROOVY = "groovy" +HIRSUTE = "hirsute" +IMPISH = "impish" +JAMMY = "jammy" +KINETIC = "kinetic" +LUNAR = "lunar" +MANTIC = "mantic" +NOBLE = "noble" + +UBUNTU_SERIES = { + PRECISE: "12.04", + QUANTAL: "12.10", + RARING: "13.04", + SAUCY: "13.10", + TRUSTY: "14.04", + UTOPIC: "14.10", + VIVID: "15.04", + WILY: "15.10", + XENIAL: "16.04", + YAKKETY: "16.10", + ZESTY: "17.04", + ARTFUL: "17.10", + BIONIC: "18.04", + COSMIC: "18.10", + DISCO: "19.04", + EOAN: "19.10", + FOCAL: "20.04", + GROOVY: "20.10", + HIRSUTE: "21.04", + IMPISH: "21.10", + JAMMY: "22.04", + KINETIC: "22.10", + LUNAR: "23.04", + MANTIC: "23.10", + NOBLE: "24.04", +} + +KUBERNETES = "kubernetes" +KUBERNETES_SERIES = {KUBERNETES: "kubernetes"} + +ALL_SERIES_VERSIONS = {**UBUNTU_SERIES, **KUBERNETES_SERIES} + + +def get_series_version(series_name): + """get_series_version outputs the version of the OS based on the given + series e.g. jammy -> 22.04, kubernetes -> kubernetes. + + :param str series_name: name of the series + :return str: os version + """ + if series_name not in ALL_SERIES_VERSIONS: + raise errors.JujuError("Unknown series : %s", series_name) + return ALL_SERIES_VERSIONS[series_name] + + +def get_version_series(version): + """get_version_series is the opposite of the get_series_version. It outputs + the series based on given OS version. + + :param str version: version of the OS + return str: name of the series corresponding to the given version + """ + if version not in UBUNTU_SERIES.values(): + raise errors.JujuError("Unknown version : %s", version) + return list(UBUNTU_SERIES.keys())[list(UBUNTU_SERIES.values()).index(version)] + + +def get_local_charm_base(series, charm_path, base_class): + """Deduce the base [channel/osname] of a local charm based on what we know + already. + + :param str series: This may come from the argument or the + metadata.yaml + :param str charm_path: Path of charm directory/.charm file + :param class base_class: + :return: Instance of the baseCls with channel/osname information + """ + channel_for_base = "" + os_name_for_base = "" + + # We should know the series, so use it to get a channel + if series: + channel_for_base = get_series_version(series) if series else "" + if channel_for_base: + # we currently only support ubuntu series (statically) + # TODO (cderici) : go juju/core/series/supported.go and get the + # others here too + if series in KUBERNETES_SERIES: + os_name_for_base = "kubernetes" + else: + os_name_for_base = "ubuntu" + + # Check the charm manifest + if channel_for_base == "": + charm_manifest = get_local_charm_manifest(charm_path) + if "bases" in charm_manifest: + channel_for_base = charm_manifest["bases"][0]["channel"] + os_name_for_base = charm_manifest["bases"][0]["name"] + + # Also check the charmcraft.yaml + if channel_for_base == "": + charmcraft_yaml = get_local_charm_charmcraft_yaml(charm_path) + if "bases" in charmcraft_yaml: + channel_for_base = charmcraft_yaml["bases"][0]["run-on"][0]["channel"] + os_name_for_base = charmcraft_yaml["bases"][0]["run-on"][0]["name"] + + if channel_for_base == "": + raise errors.JujuError("Unable to determine base for charm : %s" % charm_path) + + # Legacy k8s charms - assume ubuntu focal + # as per juju/cmd/juju/application/utils.DeduceOrigin() + if channel_for_base == "kubernetes" or os_name_for_base == "kubernetes": + channel_for_base = "20.04/stable" + os_name_for_base = "ubuntu" + return base_class(channel_for_base, os_name_for_base) + + +def base_channel_to_series(channel): + """Returns the series string using the track inside the base channel. + + :param str channel: is track/risk (e.g. 20.04/stable) + :return: str series (e.g. focal) + """ + return get_version_series(origin.Channel.parse(channel).track) + + +def parse_base_arg(base): + """Parses a given base into a Client.Base object :param base str : The base + to deploy a charm (e.g. ubuntu@22.04) + """ + client.CharmBase() + if not (isinstance(base, str) and "@" in base): + raise errors.JujuError( + f"expected base string to contain os and channel separated by '@', got : {base}" + ) + + name, channel = base.split("@") + return client.Base(name=name, channel=channel) + + +DEFAULT_SUPPORTED_LTS = "jammy" +DEFAULT_SUPPORTED_LTS_BASE = client.Base(channel="22.04", name="ubuntu") + + +def base_channel_from_series(track, risk, series): + return ( + origin.Channel(track=track, risk=risk) + .normalize() + .compute_base_channel(series=series) + ) + + +def get_os_from_series(series): + if series in UBUNTU_SERIES: + return "ubuntu" + raise JujuError(f"os for the series {series} needs to be added") + + +def get_base_from_origin_or_channel(origin_or_channel, series=None): + channel, os_name = None, None + if series: + channel = base_channel_from_series( + origin_or_channel.track, origin_or_channel.risk, series + ) + os_name = get_os_from_series(series) + return client.Base(channel=channel, name=os_name) + + +def series_for_charm(requested_series, supported_series): + """series_for_charm takes a requested series and a list of series supported + by a charm and returns the series which is relevant. + + If the requested series is empty, then the first supported series is + used, otherwise the requested series is validated against the + supported series. + """ + if len(supported_series) == 1 and supported_series[0] == "": + raise JujuError("invalid supported series reported by charm : ['']") + if len(supported_series) == 0: + if requested_series == "": + raise JujuError("missing series") + return requested_series + + # use the charm default + if requested_series == "": + return supported_series[-1] + + for s in supported_series: + if requested_series == s: + return requested_series + raise JujuError( + f"requested series {requested_series} is not among the supported series {supported_series}" + ) + + +def user_requested(series_arg, supported_series, force): + series = series_for_charm(series_arg, supported_series) + if force: + series = series_arg + # Todo (cderici): validate the series with workload_series to see if juju is + # supporting that + return series + + +def series_selector( + series_arg="", charm_url=None, model_config=None, supported_series=[], force=False +): + """Select series to deploy on. + + series_selector corresponds to the CharmSeries() in + https://github.com/juju/juju/blob/develop/core/charm/series_selector.go + + determines what series to use with a charm. + Order of preference is: + - user requested with --series or defined by bundle when deploying + - user requested in charm's url (e.g. juju deploy jammy/ubuntu) + - model default, if set, acts like --series + - default from charm metadata supported series / series in url + - default LTS + """ + # User has requested a series with --series. + if series_arg: + return user_requested(series_arg, supported_series, force) + + # User specified a series in the charm URL, e.g. + # juju deploy precise/ubuntu. + if charm_url and charm_url.series: + return user_requested(charm_url.series, supported_series, force) + + # No series explicitly requested by the user. + # Use model default series, if explicitly set and supported by the charm. + if model_config and model_config["default-base"].value: + default_base = model_config["default-base"].value + base = parse_base_arg(default_base) + series = base_channel_to_series(base.channel) + return user_requested(series, supported_series, force) + + # Next fall back to the charm's list of series, filtered to what's supported + # by Juju. Preserve the order of the supported series from the charm + # metadata, as the order could be out of order compared to Ubuntu series + # order (precise, xenial, bionic, trusty, etc). + try: + # TODO (cderici): restrict the supported_series with JujuSupportedSeries + return user_requested("", supported_series, force) + except JujuError: + pass + + # Charm hasn't specified a default (likely due to being a local charm + # deployed by path). Last chance, best we can do is default to LTS. + return DEFAULT_SUPPORTED_LTS + + +def should_upgrade_resource(available_resource, existing_resources, arg_resources): + """Determine if the given resource should be upgraded. + + Called in the context of upgrade_charm. Given a resource R, takes a look + at the resources we already have and decides if we need to refresh R. + + :param dict[str] available_resource: The dict representing the + client.Resource coming from the charmhub api. We're considering if + we need to refresh this during upgrade_charm. :param dict[str] + existing_resources: The dict coming from + resources_facade.ListResources representing the resources of the + currently deployed charm. :param dict[str] arg_resources: user + provided resources to be refreshed + + :result bool: The decision to refresh the given resource + """ + # should upgrade resource? + res_name = available_resource.get("Name", available_resource.get("name")) + + if res_name in arg_resources: + return True + + # do we have it already? + if res_name in existing_resources: + # no upgrade, if it's upload + if existing_resources[res_name].origin == "upload": + return False + # no upgrade, if upstream doesn't have a newer revision of the resource + # available + available_rev = available_resource.get( + "Revision", available_resource.get("revision", -1) + ) + u_fields = existing_resources[res_name].unknown_fields + existing_rev = u_fields.get("Revision", u_fields.get("revision", -1)) + if existing_rev >= available_rev: + return False + return True diff --git a/build/lib/juju/version.py b/build/lib/juju/version.py new file mode 100644 index 00000000..b3e36f02 --- /dev/null +++ b/build/lib/juju/version.py @@ -0,0 +1,9 @@ +# Copyright 2023 Canonical Ltd. +# Licensed under the Apache V2, see LICENCE file for details. +"""Client version definitions.""" + +LTS_RELEASES = ["jammy", "focal", "bionic", "xenial", "trusty", "precise"] + +DEFAULT_ARCHITECTURE = "amd64" + +CLIENT_VERSION = "3.6.0.0" diff --git a/docs/conf.py b/docs/conf.py index a21bbfed..41c1b4c0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,6 +48,7 @@ "sphinx.ext.viewcode", "sphinxcontrib.asyncio", "automembersummary", +# "sphinx_tabs.tabs" ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/howto/#manage-machines.rst# b/docs/howto/#manage-machines.rst# new file mode 100644 index 00000000..bcb3f92c --- /dev/null +++ b/docs/howto/#manage-machines.rst# @@ -0,0 +1,161 @@ +.. _manage-machines: + +How to manage machines +====================== + +> See also: :ref:`juju:machine` + + +Add a machine +------------- + + +To add a machine to a model, on a connected Model object, use the `add_machine()` method. For example: + +.. code:: python + + await my_model.add_machine() + + +> See more: `add_machine() `_, `Model (module) `_ + + + +List all machines +----------------- + +To see a list the names of all the available machines on a model, on a connected Model object, use the `get_machines()` method. For example: + +.. code:: python + + await my_model.get_machines() + + +To get a list of the machines as Machine objects on a model, use the `machines` property on the Model object. This allows direct interaction with any of the machines on the model. For example: + +.. code:: python + + machines = my_model.machines + my_machine = machines[0] # Machine object + print(my_machine.status) + + +> See more: `get_machines() `_, `Model (module) `_, `Model.machines (property) `_, `Machine (object) `_ + + +View details about a machine +---------------------------- + +To see details about a machine, on a connected Model object, get a hold of the Machine object within the model using the `machines` property. This allows direct interaction with the machine, such as accessing all the details (via the object properties) for that machine. For example: + +.. code:: python + + my_machine = await my_model.machines[0] + # Then we can access all the properties to view details + print(my_machine.addresses) + print(my_machine.agent_version) + print(my_machine.hostname) + print(my_machine.status) + + +> See more: `Model (module) `_, `Model.machines (property) `_, `Machine (object) `_ + +Show the status of a machine +---------------------------- + +To see the status of a machine, on a connected Model object, get a hold of the Machine object within the model using the `machines` property. The status is then retrieved directly via the Machine object properties, in this case the `status` property. For example: + +.. code:: python + + my_machine = await my_model.machines[0] + print(my_machine.status) + + +> See more: `Model (module) `_, `Model.machines (property) `_, `Machine (object) `_, `Machine.status (property) `_ + + +Manage constraints for a machine +-------------------------------- +> See also: :ref:`juju:constraint` + +**Set values.** To set constraint values for an individual machine when you create it manually, on a connected Model, use the `add_machine()` method, passing constraints as a parameter. For example: + +.. code:: python + + machine = await model.add_machine( + constraints={ + 'arch': 'amd64', + 'mem': 256 * MB, + }) + + +**Get values.** The `python-libjuju` client does not currently support getting constraint values for for an individual machine. However, to retrieve machine constraints on a model, on a connected Model, use the `get_constraints()` method. For example: + +.. code:: python + + await my_model.get_constraints() + + +Note that this will return `None` if no constraints have been set on the model. + +> See more: `add_machine() `_, `get_constraints() `_, `Model (module) `_ + + +Execute a command inside a machine +---------------------------------- + +To run a command in a machine, on a Machine object, use the `ssh()` method, passing a command as a parameter. For example: + +.. code:: python + + output = await my_machine.ssh("echo test") + assert 'test' in output + + +To run a command in all the machines corresponding to an application, on an Application object, use the `run()` method, passing the command as a parameter. For example: + +.. code:: python + + output = await my_application.run("echo test") + assert 'test' in output + + + +> See more: `ssh() `_, `Machine (object) `_, `run() `_, `Application (object) `_ + + +Copy files securely between machines +------------------------------------ + +To copy files securely between machines, on a Machine object, use the `scp_to()` and `scp_from()` methods, passing source and destination parameters for the transferred files or directories. For example: + +.. code:: python + + # Transfer from local machine to Juju machine represented by my_machine object + with open(file_name, 'r') as f: + await my_machine.scp_to(f.name, 'testfile') + + # Transfer from my_machine to local machine + with open(file_name, 'w') as f: + await my_machine.scp_from('testfile', f.name) + assert f.read() == b'contents_of_file' + + # Pass -r for recursively copy a directory via the `scp_opts` parameter. + await my_machine.scp_to('my_directory', 'testdirectory', scp_opts=['-r']) + + +> See more: `scp_to() `_, `scp_from() `_, `Machine (object) `_ + + + +Remove a machine +---------------- +> See also: :ref:`juju:removing-things` + +To remove a machine, on a Machine object, use the `destroy()` method. For example: + +.. code:: python + + await my_machine.destroy() + +> See more: `destroy() `_, `Machine (object) `_re diff --git a/docs/howto/index.rst b/docs/howto/index.rst new file mode 100644 index 00000000..88e1fb39 --- /dev/null +++ b/docs/howto/index.rst @@ -0,0 +1,27 @@ +**How-to guides** + +.. toctree:: + :glob: + :maxdepth: 2 + + Manage python-libjuju + Manage clouds + Manage credentials + Manage controllers + Manage users + Manage SSH keys + Manage models + Manage charms + Manage applications + Manage resources (charms) + Manage actions + Manage relations + Manage offers + Manage units + Manage secrets + Manage secret backends + Manage machines + Manage storage + Manage storage pools + Manage spaces + diff --git a/docs/howto/manage-actions.rst b/docs/howto/manage-actions.rst new file mode 100644 index 00000000..05c8a9f3 --- /dev/null +++ b/docs/howto/manage-actions.rst @@ -0,0 +1,43 @@ +.. _manage-actions: + +How to manage actions +===================== + + +> See also: :ref:`juju:action` + + + +List all actions +---------------- + + +To list the actions defined for a deployed application, use the `get_actions()` method on the `Application` object to get all the actions defined for this application. + +.. code:: python + + await my_app.get_actions() + + +> See more: `Application (object) `_, `get_actions (method) `_ + + +Run an action + +To run an action on a unit, use the `run_action()` method on a Unit object of a deployed application. + +Note that "running" an action on a unit, enqueues an action to be performed. The result will be an Action object to interact with. You will need to call `action.wait()` on that object to wait for the action to complete and retrieve the results. + +.. code:: python + + # Assume we deployed a git application + my_app = await model.deploy('git', application_name='git', channel='stable') + my_unit = my_app.units[0] + + action = await my_unit.run_action('add-repo', repo='myrepo') + await action.wait() # will return the result for the action + +> See more: `Unit (object) `_, `Action (object) `_, `Unit.run_action (method) `_, `Action.wait() (method) `_ + + + diff --git a/docs/howto/manage-applications.rst b/docs/howto/manage-applications.rst new file mode 100644 index 00000000..faf5a371 --- /dev/null +++ b/docs/howto/manage-applications.rst @@ -0,0 +1,241 @@ +.. _manage-applications: + +How to manage applications +========================== + +> See also: :ref:`juju:application` + +Deploy an application +--------------------- + +To deploy an application, find and deploy a charm / bundle that delivers it. + +> See more: :ref:`deploy-a-charm` + +View details about an application +--------------------------------- + +To view details about an application on python-libjuju, you may use various `get_*` methods that are defined for applications. + +For example, to get the config for an application, call `get_config()` method on an `Application` object: + +.. code:: python + + config = await my_app.get_config() + + +> See more: `Application.get_config (method) `_, `Application (methods) `_ + + +Trust an application with a credential +-------------------------------------- + +Some applications may require access to the backing cloud in order to fulfil their purpose (e.g., storage-related tasks). In such cases, the remote credential associated with the current model would need to be shared with the application. When the Juju administrator allows this to occur the application is said to be *trusted*. + +To trust an application during deployment in python-libjuju, you may call the `Model.deploy()` with the `trust` parameter: + +.. code:: python + + await my_model.deploy(..., trust=True, ...) + +To trust an application after deployment, you may use the `Application.set_trusted()` method: + +.. code:: python + + await my_app.set_trusted(True) + + +> See more: `Application.set_trusted (method) `_, `Application.get_trusted (method) `_ + + +Run an application action +------------------------- + +> See more: :ref:`manage-actions` + +Configure an application +------------------------ + +**Get values.** To view the existing configuration for an application on python-libjuju, you may use the `Application.get_config()` method: + +.. code:: python + + config = await my_app.get_config() + + +**Set values.** To set configuration values for an application on python-libjuju: + +* To configure an application at deployment, simply provide a `config` map during the `Model.deploy()` call: + +.. code:: python + + await my_model.deploy(..., config={'redirect-map':'https://demo'}, ...) + + +* To configure an application post deployment, you may use the `Application.set_config()` method, similar to passing config in the deploy call above: + +.. code:: python + + await my_app.set_config(config={'redirect-map':'https://demo'}) + + +> See more: `Application.set_config (method) `_, `Application.get_config (method) `_ + + +.. _scale-an-application: +Scale an application +-------------------- + +> See also: :ref:`juju:scaling` + +Scale an application vertically +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To scale an application vertically, set constraints for the resources that the application's units will be deployed on. + +> See more: :ref:`manage-constraints-for-an-application` + +Scale an application horizontally +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To scale an application horizontally, control the number of units. + +> See more: :ref:`control-the-number-of-units` + + +Make an application highly available +------------------------------------ +> See also: :ref:`juju:high-availability` + +1. Find out if the charm delivering the application supports high availability natively or not. If the latter, find out what you need to do. This could mean integrating with a load balancing reverse proxy, configuring storage etc. + +> See more: `Charmhub `_ + +2. Scale up horizontally as usual. + +> See more: {ref}`How to scale an application horizontally <5476md>` + +Every time a unit is added to an application, Juju will spread out that application's units, distributing them evenly as supported by the provider (e.g., across multiple availability zones) to best ensure high availability. So long as a cloud's availability zones don't all fail at once, and the charm and the charm's application are well written (changing leaders, coordinating across units, etc.), you can rest assured that cloud downtime will not affect your application. + +> See more: `Charmhub | wordpress `_, `Charmhub | mediawiki `_, `Charmhub | haproxy `_ + +Integrate an application with another application +------------------------------------------------- + +> See more: :ref:`manage-relations` + + +Manage an application’s public availability over the network +------------------------------------------------------------ + +To expose some or all endpoints of an application over a network, you may use the `Application.expose()` method, as follows: + +.. code:: python + + await my_app.expose(exposed_endpoints=None) # everything's reachable from 0.0.0.0/0. + + +To expose to specific CIDRs or spaces, you may use an `ExposedEndpoint` object to describe that, as follows: + +.. code:: python + + # For spaces + await my_app.expose(exposed_endpoints={"": ExposedEndpoint(to_spaces=["alpha"]) }) + + # For cidrs + await my_app.expose(exposed_endpoints={"": ExposedEndpoint(to_cidrs=["10.0.0.0/24"])}) + + # You may use both at the same time too + await my_app.expose(exposed_endpoints={ + "ubuntu": ExposedEndpoint(to_spaces=["alpha"], to_cidrs=["10.0.0.0/24"]) + }) + + + +To unexpose an application, use the `Application.unexpose()` method: + +.. code:: python + + await my_app.unexpose() # unexposes the entire application + + await my_app.unexpose(exposed_endpoints=["ubuntu"]) # unexposes the endpoint named "ubuntu" + + +> See more: `ExposedEndpoint (methods) `_, `Application.expose() `_, `Application.unexpose() `_ + + +.. _manage-constraints-for-an-application: +Manage constraints for an application +------------------------------------- + +> See also: :ref:`juju:constraint` + +**Set values.** To set constraints for application in python-libjuju: + +* To set at deployment, simply provide a `constraints` map during the `Model.deploy()` call: + +.. code:: python + + await my_model.deploy(..., constraints={, 'arch': 'amd64', 'mem': 256}, ...) + + +* To set constraints post deployment, you may use the `Application.set_contraints()` method, similar to passing constraints in the deploy call above: + +.. code:: python + + await my_app.set_constraints(constraints={, 'arch': 'amd64', 'mem': 256}) + + +**Get values.** To see what constraints are set on an application, use the `Application.get_constraints()` method: + +.. code:: python + + await my_app.get_constraints() + + +> See more: `Application.set_contraints() `_, `Application.get_constraints (method) `_ + + +Change space bindings for an application +---------------------------------------- + +To set bindings for an application on python-libjuju, simply pass the `bind` parameter at the `Model.deploy()` call: + +.. code:: python + + await my_model.deploy(..., bind="db=db db-client=db public admin-api=public", ...) + +Python-libjuju currently doesn't support resetting space bindings post deployment, please use the `juju-cli` for that. + +> See more: [`Model.deploy()` (method) <5476md>` + +Upgrade an application +---------------------- + +To upgrade an application, update its charm. + +> See more: :ref:`update-a-charm` + +.. _remove-an-application: + +Remove an application +--------------------- + +> See also: :ref:`juju:removing-things` + +To remove an application from a model in python-libjuju, you have two choices: + +(1) If you have a reference to a connected model object (connected to the model you're working on), then you may use the `Model.remove_application()` method: + +.. code:: python + + await my_model.remove_application(my_app.name) + + +(2) If you have a reference to the application you want to remove, then you may use the `Application.destroy()` directly on the application object you want to remove: + +.. code:: python + + await my_app.destroy() + +> See more: `Model.remove_application (method) `_, `Application.destroy (method) `_ diff --git a/docs/howto/manage-charm-resources.rst b/docs/howto/manage-charm-resources.rst new file mode 100644 index 00000000..9f7c5ef7 --- /dev/null +++ b/docs/howto/manage-charm-resources.rst @@ -0,0 +1,52 @@ +.. _manage-charm-resources: + +How to manage charm resources +============================= + +> See also: :ref:`juju:resource-charm` + +When you deploy / update an application from a charm, that automatically deploys / updates any charm resources, using the defaults specified by the charm author. However, you can also specify resources manually (e.g., to try a resource released only to `edge` or to specify a non-Charmhub resource). This document shows you how. + + +Find out the resources available for a charm +-------------------------------------------- + +To find out what resources are available for a charm on Charmhub, on a connected Model object, select the `charmhub` object associated with the model, and use the `list_resources()` method, passing the name of the charm as an argument. For example: + +.. code:: python + + await model.charmhub.list_resources('postgresql-k8s') + +> See more: `charmhub (property) `_, `Model (module) `_ + +Specify the resources to be deployed with a charm +------------------------------------------------- + +To specify a resource during deployment, on a connected Model object, use the `deploy` method, passing the resources as a parameter. For example: + +.. code:: python + + resources = {"file-res": "test.file"} + app = await model.deploy(charm_path, resources=resources) + +To update a resource after deployment by uploading file from local disk, on an Application object, use the `attach_resource()` method, passing resource name, file name and the file object as parameters. + +.. code:: python + + with open(str(charm_path / 'test.file')) as f: + app.attach_resource('file-res', 'test.file', f) + + + +> See more: `deploy() `_, `attach_resource() `_, `Model (module) `_ + +View the resources deployed with a charm +---------------------------------------- + +To view the resources that have been deployed with a charm, on an Application object, use the `get_resources()` method. For example: + +.. code:: python + + await my_app.get_resources() + +> See more: `get_resources() `_, `Model (module) `_ diff --git a/docs/howto/manage-charms.rst b/docs/howto/manage-charms.rst new file mode 100644 index 00000000..cd34a674 --- /dev/null +++ b/docs/howto/manage-charms.rst @@ -0,0 +1,108 @@ +.. _manage-charms: + +How to manage charms or bundles +=============================== +> See also: :ref:`juju:charm` + +This document shows various ways in which you may interact with a charm or a bundle. + + +Query Charmhub for available charms / bundles +--------------------------------------------- + +To query Charmhub for the charms / bundles on python-libjuju, you can use the `find` method on the `CharmHub` object that's built-in on each `Model` object: + +.. code:: python + + await model.charmhub.find('wordpress') + + + +View details about a Charmhub charm / bundle +-------------------------------------------- + +To view details about a particular Charmhub charm / bundle on python-libjuju, you can use the `info` method on the `CharmHub` object that's built-in on each `Model` object: + +.. code:: python + + await model.charmhub.info('wordpress') + + + +Find out the resources available for a charm +-------------------------------------------- + +> See more: :ref:`manage-charm-resources` + +.. _deploy-a-charm: +Deploy a charm / bundle +----------------------- + +To deploy a Charmhub charm / bundle using python-libjuju, you can use the `deploy` method on the `Model` object: + + +.. code:: python + + m = model.Model() + await m.connect() + + # deploy a charm + await m.deploy('mysql') + + # deploy a bundle + await m.deploy('kubeflow') + + # deploy a local charm + await m.deploy('./mini_ubuntu-20.04-amd64.charm') + + # deploy a local charm with a resource + await m.deploy('./demo-api-charm_ubuntu-22.04-amd64.charm', resources={'demo-server-image=ghcr.io/beliaev-maksim/api_demo_server':'0.0.9'}) + + # deploy a local bundle + await m.deploy('./mediawiki-model-bundle.yaml') + + # deploy a bundle with an overlay + await m.deploy('mediawiki', overlays=['./custom-mediawiki.yaml']) + + # generic openstack example + await m.deploy('./bundle-focal-yoga.yaml', overlays=['./overlay-focal-yoga-mymaas.yaml', './overlay-focal-yoga-mymaas-shared-filesystem.yaml']) + + +> See more: `Model.deploy() `_ + + +.. _update-a-charm: +Update a charm +-------------- + +To update a charm on python-libjuju, you can use the `upgrade_charm` (aliased as `refresh`) method on the `Application` object: + +.. code:: python + + # upgrade to latest revision on the channel + await my_app.upgrade_charm() + + # upgrade to the latest revision on a given channel + await my_app.upgrade_charm(channel='latest/edge') + + # upgrade to a particular revision + await my_app.upgrade_charm(revision=3) + + # upgrade with a local charm + await my_app.upgrade_charm(path='./path/to/juju-test') + + # replace a charm completely with another charm + await my_app.upgrade_charm(switch='./path/to/juju-test') + + # Note that the path and switch parameters are mutually exclusive. + +> See more: `Application.upgrade_charm() `_ + + +Remove a charm / bundle +----------------------- + +As a charm / bundle is just the *means* by which (an) application(s) are deployed, there is no way to remove the *charm* / *bundle*. What you *can* do, however, is remove the *application* / *model*. + +> See more: :ref:`remove-an-application` + diff --git a/docs/howto/manage-clouds.rst b/docs/howto/manage-clouds.rst new file mode 100644 index 00000000..40fc6bde --- /dev/null +++ b/docs/howto/manage-clouds.rst @@ -0,0 +1,78 @@ +.. _manage-clouds: + +How to manage clouds +==================== + +> See also: :ref:`juju:cloud`, :ref:`juju:list-of-supported-clouds` + +This document shows how to manage your existing cloud(s) with Juju. + + +Add a cloud +----------- + + +With `python-libjuju`, you can only add a cloud definition to a controller you've already bootstrapped with the `juju` client. + +To add a cloud, use the `Controller.add_cloud()` method on a connected `Controller` object. For example: + +.. code:: python + + from juju.client import client as jujuclient + + await my_controller.add_cloud("my-cloud", + jujuclient.Cloud( + auth_types=["userpass"], + endpoint="http://localhost:1234", + type_="kubernetes", + )) + + + + +> See more: `add_cloud (method) `_, `Cloud (object) `_ + + +View all the known clouds +------------------------- + +To get all clouds known to the controller, you may use the `Controller.clouds()` method on a connected `Controller` object. It will return a list of Cloud objects. + +.. code:: python + + await my_controller.clouds() + +> See more: `clouds (method) `_, `Cloud (object) `_ + + +View details about a cloud +-------------------------- + +To get more detail about a particular cloud, you may use the `Controller.cloud()` method on a connected `Controller` object. It will return a Cloud object. + +.. code:: python + + await my_controller.cloud() + + + +> See more: `cloud (method) `_, `Cloud (object) `_ + + +Manage cloud credentials +------------------------ +> See more: :ref:`manage-credentials` + + +Remove a cloud +-------------- +> See also: :ref:`juju:removing-things` + +To remove a cloud definition, you may use the `Controller.remove_cloud()` method on a connected `Controller` object. + +.. code:: python + + await my_controller.remove_cloud() + + +> See more: `remove_cloud (method) `_ diff --git a/docs/howto/manage-controllers.rst b/docs/howto/manage-controllers.rst new file mode 100644 index 00000000..9be6f5aa --- /dev/null +++ b/docs/howto/manage-controllers.rst @@ -0,0 +1,52 @@ +.. _manage-controllers: + +How to manage controllers +========================= + +> See also: :ref:`juju:controller` + + +This document demonstrates various ways in which you can interact with a controller. + + + +Bootstrap a controller +---------------------- + +> See also: :ref:`juju:list-of-supported-clouds` + +With the `python-libjuju` client, you can only connect to a pre-existing controller. To bootstrap a controller, see the `juju` client. + + +View details about a controller +------------------------------- + +To view details about a controller in `python-libjuju`, with a connected controller object (below, `controller`), you can call the `Controller.info()` function to retrieve information about the connected controller: + +.. code:: python + + await controller.info() + +> See more: `Controller.info() `_ + + +Switch to a different controller +-------------------------------- + +To switch to a different controller with `python-libjuju`, simply connect to the controller you want to work with, which is done by calling `connect` on the `Controller `_ object (below, `controller`): + +.. code:: python + + from juju.model import Controller + + controller = Controller() + await controller.connect() # will connect to the "current" controller + + await controller.connect('mycontroller') # will connect to the controller named "mycontroller" + + +Note that if the `controller` object is already connected to a controller, then that connection will be closed before making the new connection. + +> See more: `Controller.connect() `_, `Connect with Authentication `_, `Connect with explicit endpoints `_ + + diff --git a/docs/howto/manage-credentials.rst b/docs/howto/manage-credentials.rst new file mode 100644 index 00000000..79e19bfd --- /dev/null +++ b/docs/howto/manage-credentials.rst @@ -0,0 +1,22 @@ +.. _manage-credentials: + +How to manage credentials +========================= + +> See also: :ref:`juju:credential` + + +Update a credential +------------------- + +To update a credential, on a connected `Controller` object, use the `Controller.add_credential()` method. `add_credential` is an upsert method (where it inserts if the given credential is new, and updates if the given credential name already exists). + +.. code:: python + + from juju.client import client as jujuclient + + my_controller.add_credential("my-credential", + jujuclient.CloudCredential(auth_type="jsonfile", attrs={'file':'path_to_cred_file'}) + +> See more: `add_credential (method) `_ + diff --git a/docs/howto/manage-machines.rst b/docs/howto/manage-machines.rst new file mode 100644 index 00000000..e5940db3 --- /dev/null +++ b/docs/howto/manage-machines.rst @@ -0,0 +1,161 @@ +.. _manage-machines: + +How to manage machines +====================== + +> See also: :ref:`juju:machine` + + +Add a machine +------------- + + +To add a machine to a model, on a connected Model object, use the `add_machine()` method. For example: + +.. code:: python + + await my_model.add_machine() + + +> See more: `add_machine() `_, `Model (module) `_ + + + +List all machines +----------------- + +To see a list the names of all the available machines on a model, on a connected Model object, use the `get_machines()` method. For example: + +.. code:: python + + await my_model.get_machines() + + +To get a list of the machines as Machine objects on a model, use the `machines` property on the Model object. This allows direct interaction with any of the machines on the model. For example: + +.. code:: python + + machines = my_model.machines + my_machine = machines[0] # Machine object + print(my_machine.status) + + +> See more: `get_machines() `_, `Model (module) `_, `Model.machines (property) `_, `Machine (object) `_ + + +View details about a machine +---------------------------- + +To see details about a machine, on a connected Model object, get a hold of the Machine object within the model using the `machines` property. This allows direct interaction with the machine, such as accessing all the details (via the object properties) for that machine. For example: + +.. code:: python + + my_machine = await my_model.machines[0] + # Then we can access all the properties to view details + print(my_machine.addresses) + print(my_machine.agent_version) + print(my_machine.hostname) + print(my_machine.status) + + +> See more: `Model (module) `_, `Model.machines (property) `_, `Machine (object) `_ + +Show the status of a machine +---------------------------- + +To see the status of a machine, on a connected Model object, get a hold of the Machine object within the model using the `machines` property. The status is then retrieved directly via the Machine object properties, in this case the `status` property. For example: + +.. code:: python + + my_machine = await my_model.machines[0] + print(my_machine.status) + + +> See more: `Model (module) `_, `Model.machines (property) `_, `Machine (object) `_, `Machine.status (property) `_ + + +Manage constraints for a machine +-------------------------------- +> See also: :ref:`juju:constraint` + +**Set values.** To set constraint values for an individual machine when you create it manually, on a connected Model, use the `add_machine()` method, passing constraints as a parameter. For example: + +.. code:: python + + machine = await model.add_machine( + constraints={ + 'arch': 'amd64', + 'mem': 256 * MB, + }) + + +**Get values.** The `python-libjuju` client does not currently support getting constraint values for for an individual machine. However, to retrieve machine constraints on a model, on a connected Model, use the `get_constraints()` method. For example: + +.. code:: python + + await my_model.get_constraints() + + +Note that this will return `None` if no constraints have been set on the model. + +> See more: `add_machine() `_, `get_constraints() `_, `Model (module) `_ + + +Execute a command inside a machine +---------------------------------- + +To run a command in a machine, on a Machine object, use the `ssh()` method, passing a command as a parameter. For example: + +.. code:: python + + output = await my_machine.ssh("echo test") + assert 'test' in output + + +To run a command in all the machines corresponding to an application, on an Application object, use the `run()` method, passing the command as a parameter. For example: + +.. code:: python + + output = await my_application.run("echo test") + assert 'test' in output + + + +> See more: `ssh() `_, `Machine (object) `_, `run() `_, `Application (object) `_ + + +Copy files securely between machines +------------------------------------ + +To copy files securely between machines, on a Machine object, use the `scp_to()` and `scp_from()` methods, passing source and destination parameters for the transferred files or directories. For example: + +.. code:: python + + # Transfer from local machine to Juju machine represented by my_machine object + with open(file_name, 'r') as f: + await my_machine.scp_to(f.name, 'testfile') + + # Transfer from my_machine to local machine + with open(file_name, 'w') as f: + await my_machine.scp_from('testfile', f.name) + assert f.read() == b'contents_of_file' + + # Pass -r for recursively copy a directory via the `scp_opts` parameter. + await my_machine.scp_to('my_directory', 'testdirectory', scp_opts=['-r']) + + +> See more: `scp_to() `_, `scp_from() `_, `Machine (object) `_ + + + +Remove a machine +---------------- +> See also: :ref:`juju:removing-things` + +To remove a machine, on a Machine object, use the `destroy()` method. For example: + +.. code:: python + + await my_machine.destroy() + +> See more: `destroy() `_, `Machine (object) `_ diff --git a/docs/howto/manage-models.rst b/docs/howto/manage-models.rst new file mode 100644 index 00000000..b1f1d4d8 --- /dev/null +++ b/docs/howto/manage-models.rst @@ -0,0 +1,89 @@ +.. _manage-models: + +How to manage models +==================== + +> See also: :ref:`juju:model` + +Add a model +----------- + +To add a model, on a connected controller, call the `add_model` function. For example, below we're adding a model called `test-model` on the `controller`: + +.. code:: python + + await controller.add_model("test-model") + +> See more: `Controller.add_model() `_, `juju_model (module) `_, `juju_controller (module) `_ + + +View all the models available on a controller +--------------------------------------------- + +To view all the models available on a controller, call the `Controller.list_models()` function: + +.. code:: python + + await controller.list_models() + +> See more: `Controller.list_models() `_ + + +Switch to a different model +--------------------------- + +In `python-libjuju`, switching to a different model means simply connecting to the model you want to work with, which is done by calling `connect` on the `Model `_ object: + +.. code:: python + + from juju.model import Model + + model = Model() + await model.connect() # will connect to the "current" model + + await model.connect(model_name="test-model") # will connect to the model named "test-model" + +Note that if the `model` object is already connected to a model, then that connection will be closed before making the new connection. + +> See more: `Model.connect() `_ + + +View the status of a model +-------------------------- + +TBA + + + +View details about a model +-------------------------- + +TBA + + +Configure a model +----------------- +> See also: :ref:`juju:model-configuration`, :ref:`juju:list-of-model-configuration-keys` + +TBA + + +Manage constraints for a model +------------------------------ +> See also: :ref:`juju-constraint` + +TBA + + +Destroy a model +--------------- + +To destroy a model, with a connected controller object, call the `Controller.destroy_model()` function. For example: + +.. code:: python + + await controller.destroy_model("test-model") + + +> See more: `Controller.destroy_model() `_ + diff --git a/docs/howto/manage-offers.rst b/docs/howto/manage-offers.rst new file mode 100644 index 00000000..d6f112d1 --- /dev/null +++ b/docs/howto/manage-offers.rst @@ -0,0 +1,90 @@ +.. _manage-offers: + +How to manage offers +==================== + +> See also: :ref:`juju:offer` + +This document shows how to manage offers. + + +Create an offer +--------------- +> Who: User with :ref:`juju:user-access-offer-admin` + +To create an offer, use the `create_offer()` method on a connected Model object. + +.. code:: python + + # Assume a deployed mysql application + await my_model.deploy('mysql') + # Expose the database endpoint of the mysql application + await my_model.create_offer('mysql:database', offer_name='hosted-mysql') + +> See more: `create_offer() `_ + + +Control access to an offer +-------------------------- +> Who: User with :ref:`juju:user-access-offer-admin` + +The access levels for offers can be applied in the same way the model or controller access for a given user. Use the `grant()` and `revoke()` methods on a User object to grant or revoke access to an offer. + +.. code:: python + + # Grant Bob consume access to an offer + await user_bob.grant('consume', offer_name='admin/default.hosted-mysql') + + # Revoke Bob's consume access (he will be left with read access) + await user_bob.revoke('consume', offer_name='admin/default.hosted-mysql') + +> See more: `User (object)` + + +.. _integrate-with-an-offer: +Integrate with an offer +----------------------- +> Who: User with :ref:`juju:user-access-offer-consume` + +To integrate with an offer, on a connected model, use the `Model.integrate()` method with a consumed offer url. For example: + +.. code:: python + + # Integrate via offer url + await my_model.integrate('mediawiki:db', 'admin/default.hosted-mysql') + + # Integrate via an offer alias created when consumed + await my_model.consume('admin/prod.hosted_mysql', application_alias="mysql-alias") + await my_model.integrate('mediawiki:db', 'mysql-alias') + + # Remove a consumed offer: + await my_model.remove_saas('mysql-alias') + +> See more: `Model.integrate() `_, `Model.consume() `_, `Model.remove_saas() `_ + + +Inspect integrations with an offer +---------------------------------- +> Who: User with :ref:`juju:user-access-offer-admin` + +To see all connections to one or more offers, use the `list_offers()` method on a connected Model object. + +.. code:: python + + await my_model.list_offers() + +> See more: `list_offers() `_ + + +Remove an offer +--------------- +> Who: User with :ref:`juju:user-access-offer-admin` + +To remove an offer, use the `remove_offer()` method on a connected Model. If the offer is used in an integration, then the `force=True` parameter is required to remove the offer, in which case the integration is also removed. + +.. code:: python + + await my_model.remove_offer('admin/mymodel.ubuntu', force=True) + +> See more: `remove_offer() `_ + diff --git a/docs/howto/manage-python-libjuju.rst b/docs/howto/manage-python-libjuju.rst new file mode 100644 index 00000000..c65597dd --- /dev/null +++ b/docs/howto/manage-python-libjuju.rst @@ -0,0 +1,73 @@ +.. _manage-python-libjuju: + +How to manage python-libjuju +============================ + +> See also: :ref:`juju:client` + + +Install `python-libjuju` +------------------------ + +In PyPI, which is the Python repository that `pip` is drawing modules from, `python-libjuju` is simply referred to as `juju`. You can install it directly via `pip`: + +.. code:: bash + + pip3 install juju + + +Use `python-libjuju` +-------------------- + +1. After installing `python-libjuju`, import it into your Python script as follows: + +.. code:: + + import juju + +You can also import specific modules to use, depending on your use case: + +.. code:: + + from juju import model + +or + +.. code:: + + from juju import controller + + +Examples of different use cases of this client can be found in the docs, as well as in the `examples +directory in the repository `_ which can be run using ``tox``. For +example, to run ``examples/connect_current_model.py``, use: + +.. code:: bash + + tox -e example -- examples/connect_current_model.py + + +Or you can directly run it via python as well: + +.. code:: + + $ python3 examples/connect_current_model.py + + +To experiment with the library in a REPL, launch Python repl with asyncio module loaded, as follows: + +.. code:: + + $ python3 -m asyncio + +and then, for example to connect to the current model and fetch status: + +.. code:: + + >>> from juju.model import Model + >>> model = Model() + >>> await model.connect_current() + >>> status = await model.get_status() + +Whichever your chosen method, use the `python-libjuju` how-to guides and the reference to build up your deployment. + diff --git a/docs/howto/manage-relations.rst b/docs/howto/manage-relations.rst new file mode 100644 index 00000000..9f83ca33 --- /dev/null +++ b/docs/howto/manage-relations.rst @@ -0,0 +1,59 @@ +.. _manage-relations: + +How to manage relations +======================= + +> See also: :ref:`juju:relation` + +Add a relation +-------------- + +The procedure differs slightly depending on whether the applications that you want to integrate are on the same model or rather on different models. + +Add a same-model relation +~~~~~~~~~~~~~~~~~~~~~~~~~ +> See also: :ref:`juju:same-model-relation` + +To add a same-model relation between two applications, on a connected Model, use the `integrate()` method. + +.. code:: python + + await my_model.integrate('mysql', 'mediawiki') + + # Integrate with particular endpoints + await my_model.integrate('mysql', 'mediawiki:db') + + +> See more: `integrate() `_ + +Add a cross-model relation +~~~~~~~~~~~~~~~~~~~~~~~~~~ +> See also: :ref:`juju:cross-model-relation` + + +In a cross-model relation there is also an 'offering' model and a 'consuming' model. The admin of the 'offering' model 'offers' an application for consumption outside of the model and grants an external user access to it. The user on the 'consuming' model can then find an offer to use, consume the offer, and integrate an application on their model with the 'offer' via the same `integrate` command as in the same-model case (just that the offer must be specified in terms of its offer URL or its consume alias). This creates a local proxy for the offer in the consuming model, and the application is subsequently treated as any other application in the model. + +> See more: :ref:`integrate-with-an-offer` + +View all the current relations +------------------------------ + +To view the current relations in a model, directly access the Model's `relations` property. + +.. code:: python + + my_model.relations + +> See more: `Model.relations (property) `_ + + +Remove a relation +----------------- + +To remove a relation, use the `remove_relation()` method on an Application object. + +.. code:: python + + await my_app.remove_relation('mediawiki', 'mysql:db') + +> See more: `remove_relation() `_ diff --git a/docs/howto/manage-secret-backends.rst b/docs/howto/manage-secret-backends.rst new file mode 100644 index 00000000..7f2eddea --- /dev/null +++ b/docs/howto/manage-secret-backends.rst @@ -0,0 +1,62 @@ +.. _manage-secret-backends: + +How to manage secret backends +============================= + +> See also: :ref:`juju:secret-backend` + + +Starting with Juju `3.1.0`, you can also manage secret backends in a number of ways. + + +Add a secret backend to a model +------------------------------- + +To add a secret backend to a controller, on a connected Controller, use the `add_secret_backends()` method, passing the `id`, `name`, `backend_type`, and `config` as arguments. For example: + +.. code:: python + + await my_controller.add_secret_backends("1001", "myvault", "vault", {"endpoint": vault_url, "token": keys["root_token"]}) + +> See more: `add_secret_backend() `_, `Controller (module) `_ + + +View all the secret backends available on a controller +------------------------------------------------------ + +To view all the secret backends available in the controller, on a connected Controller, use the `list_secret_backends()` method. + +.. code:: python + + list = await my_controller.list_secret_backends() + +> See more: `list_secret_backends() `_, `Controller (module) `_ + + +Update a secret backend +----------------------- + +To update a secret backend on the controller, on a connected Controller, use the `update_secret_backends()` method, passing the backend name as argument, along with the updated information, such as `name_change` for a new name. For example: + +.. code:: python + + await my_controller.update_secret_backends( + "myvault", + name_change="changed_name") + +Check out the documentation for the full list of arguments. + +> See more: `update_secret_backend() `_, `Controller (module) `_ + +Remove a secret backend +----------------------- + +To remove a secret backend on the controller, on a connected Controller, use the `remove_secret_backends()` method, passing the backend name as argument. For example: + +.. code:: python + + await my_controller.remove_secret_backends("myvault") + +Check out the documentation for the full list of arguments. + +> See more: `remove_secret_backend() `_, `Controller (module) `_ diff --git a/docs/howto/manage-secrets.rst b/docs/howto/manage-secrets.rst new file mode 100644 index 00000000..bc497cf4 --- /dev/null +++ b/docs/howto/manage-secrets.rst @@ -0,0 +1,80 @@ +.. _manage-secrets: + +How to manage secrets +===================== + +> See also: :ref:`juju:secret` + +Charms can use relations to share secrets, such as API keys, a database’s address, credentials and so on. This document demonstrates how to interact with them as a Juju user. + + +Add a secret +------------ + +To add a (user) secret, on a connected Model, use the `add_secret()` method, passing the name of the secret and the data as arguments. For example: + +.. code:: python + + await model.add_secret(name='my-apitoken', data_args=['token=34ae35facd4']) + +> See more: `add_secret() `_, `Model (module) `_ + + +View all the available secrets +------------------------------ + +To view all the (user and charm) secrets available in a model, on a connected Model, use the `list_secrets()` method. + +.. code:: python + + await model.list_secrets() + +> See more: `list_secrets() `_, `Model (module) `_ + + + +Grant access to a secret +------------------------ + +Given a model that contains both your (user) secret and the application(s) that you want to grant access to, to grant the application(s) access to the secret, on a connected Model, use the `grant_secret()` method, passing the name of the secret and the application name as arguments. For example: + +.. code:: python + + await model.grant_secret('my-apitoken', 'ubuntu') + +Similarly, you can use the `revoke_secret()` method to revoke access to a secret for an application. + +.. code:: python + + await model.revoke_secret('my-apitoken', 'ubuntu') + +> See more: `grant_secret() `_, `revoke_secret() `_, `Model (module) `_ + + +Update a secret +--------------- +> *This feature is opt-in because Juju automatically removing secret content might result in data loss.* + +To update a (user) secret, on a connected Model, use the `update_secret()` method, passing the name of the secret and the updated info arguments. You may pass in `data_args`, `new_name`, `file` and `info` to update the secret (check out the documentation for details). For example: + +.. code:: python + + await model.update_secret(name='my-apitoken', new_name='new-token') + +> See more: `update_secret() `_, `Model (module) `_ + + +Remove a secret +--------------- + +To remove a secret from a model, on a connected Model, use the `remove_secret()` method, passing the name of the secret as an argument. For example: + +.. code:: python + + # Remove all the revisions of a secret + await model.remove_secret('my-apitoken') + + # Remove the revision 2 of a secret + await model.remove_secret('my-apitoken', revision=2) + +> See more: `remove_secret() `_, `Model (module) `_ diff --git a/docs/howto/manage-spaces.rst b/docs/howto/manage-spaces.rst new file mode 100644 index 00000000..d429e817 --- /dev/null +++ b/docs/howto/manage-spaces.rst @@ -0,0 +1,36 @@ +.. _manage-spaces: + +How to manage spaces +==================== + +> See also: :ref:`juju:space` + + +Add a space +----------- + +To create and add a new space, on a connected Model object, use the `add_space()` method, passing a name for the space and associated subnets. For example: + +.. code:: python + + await my_model.add_space("db-space", ["172.31.0.0/20"]) + +> See more: `add_space() `_, `Model (module) `_ + + + +View available spaces +---------------------- + +To view available spaces, on a connected Model, use the `get_spaces()` method. + +.. code:: python + + await my_model.get_spaces() + +> See more: `get_spaces() `_, `Model (module) `_ + + + + + diff --git a/docs/howto/manage-ssh-keys.rst b/docs/howto/manage-ssh-keys.rst new file mode 100644 index 00000000..0f0917b9 --- /dev/null +++ b/docs/howto/manage-ssh-keys.rst @@ -0,0 +1,46 @@ +.. _manage-ssh-keys: + +How to manage SSH keys +====================== + +> See also: :ref:`juju:ssh-key` + + +Add an SSH key +-------------- + +To add a public `ssh` key to a model, on a connected Model object, use the `add_ssh_key()` method, passing a name for the key and the actual key payload. For example: + +.. code:: python + + SSH_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1I8QDP79MaHEIAlfh933zqcE8LyUt9doytF3YySBUDWippk8MAaKAJJtNb+Qsi+Kx/RsSY02VxMy9xRTp9d/Vr+U5BctKqhqf3ZkJdTIcy+z4hYpFS8A4bECJFHOnKIekIHD9glHkqzS5Vm6E4g/KMNkKylHKlDXOafhNZAiJ1ynxaZIuedrceFJNC47HnocQEtusPKpR09HGXXYhKMEubgF5tsTO4ks6pplMPvbdjxYcVOg4Wv0N/LJ4ffAucG9edMcKOTnKqZycqqZPE6KsTpSZMJi2Kl3mBrJE7JbR1YMlNwG6NlUIdIqVoTLZgLsTEkHqWi6OExykbVTqFuoWJJY3BmRAcP9H3FdLYbqcajfWshwvPM2AmYb8V3zBvzEKL1rpvG26fd3kGhk3Vu07qAUhHLMi3P0McEky4cLiEWgI7UyHFLI2yMRZgz23UUtxhRSkvCJagRlVG/s4yoylzBQJir8G3qmb36WjBXxpqAGHfLxw05EQI1JGV3ReYOs= user@somewhere" + await my_model.add_ssh_key('admin', SSH_KEY) + + +> See more: `add_ssh_key() `https://pythonlibjuju.readthedocs.io/en/latest/api/juju.model.html#juju.model.Model.add_ssh_key>`_, `Model (module) `_ + + +View the available SSH keys +--------------------------- + +To list the currently known SSH keys for the current model, on a connected Model object, use the `get_ssh_keys()` method. For example: + +.. code:: python + + await my_model.get_ssh_keys() + + +> See more: `get_ssh_keys() `_, `Model (module) `_ + + +Remove an SSH key +----------------- + +To remove an SSH key, on a connected Model object, use the `remove_ssh_key()` method, passing the user name and the key as parameters. For example: + +.. code:: python + + SSH_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1I8QDP79MaHEIAlfh933zqcE8LyUt9doytF3YySBUDWippk8MAaKAJJtNb+Qsi+Kx/RsSY02VxMy9xRTp9d/Vr+U5BctKqhqf3ZkJdTIcy+z4hYpFS8A4bECJFHOnKIekIHD9glHkqzS5Vm6E4g/KMNkKylHKlDXOafhNZAiJ1ynxaZIuedrceFJNC47HnocQEtusPKpR09HGXXYhKMEubgF5tsTO4ks6pplMPvbdjxYcVOg4Wv0N/LJ4ffAucG9edMcKOTnKqZycqqZPE6KsTpSZMJi2Kl3mBrJE7JbR1YMlNwG6NlUIdIqVoTLZgLsTEkHqWi6OExykbVTqFuoWJJY3BmRAcP9H3FdLYbqcajfWshwvPM2AmYb8V3zBvzEKL1rpvG26fd3kGhk3Vu07qAUhHLMi3P0McEky4cLiEWgI7UyHFLI2yMRZgz23UUtxhRSkvCJagRlVG/s4yoylzBQJir8G3qmb36WjBXxpqAGHfLxw05EQI1JGV3ReYOs= user@somewhere" + await my_model.remove_ssh_key('admin', SSH_KEY) + +> See more: `remove_ssh_key() `_, `Model (module) `_ diff --git a/docs/howto/manage-storage-pools.rst b/docs/howto/manage-storage-pools.rst new file mode 100644 index 00000000..b63a8a60 --- /dev/null +++ b/docs/howto/manage-storage-pools.rst @@ -0,0 +1,56 @@ +.. _manage-storage-pools: + +How to manage storage pools +=========================== + +> See also: :ref:`juju:storage-pool` + +Create a storage pool +--------------------- + +To create a storage pool, on a connected Model object, use the `create_storage_pool()` method, passing the name of the pool and the provider type. For example: + +.. code:: python + + await my_model.create_storage_pool("test-pool", "lxd") + +> See more: `create_storage_pool() `_, `Model (module) `_ + + +View the available storage pools +-------------------------------- + +To view the available storage pools, on a connected Model object, use the `list_storage_pools()` method. For example: + +.. code:: python + + await my_model.list_storage_pools() + +> See more: `list_storage_pools() `_, `Model (module) `_ + + +Update a storage pool +--------------------- + +To update an existing storage pool attributes, on a connected Model object, use the `update_storage_pool()` method, passing the name of the storage and the attribute values to update. For example: + +.. code:: python + + await my_model.update_storage_pool( + "operator-storage", + attributes={"volume-type":"provisioned-iops", "iops"="40"}) + +> See more: `update_storage_pool() `_, `Model (module) `_ + + +Remove a storage pool +--------------------- + +To remove a storage pool, on a connected Model object, use the `remove_storage_pool()` method, passing the name of the storage. For example: + +.. code:: python + + await my_model.remove_storage_pool("test-pool") + +> See more: `remove_storage_pool() `_, `Model (module) `_ + diff --git a/docs/howto/manage-storage.rst b/docs/howto/manage-storage.rst new file mode 100644 index 00000000..05460c4c --- /dev/null +++ b/docs/howto/manage-storage.rst @@ -0,0 +1,83 @@ +.. _manage-storage: + +How to manage storage +===================== + + +> See also: :ref:`juju:storage` + +This document shows how to manage storage. This will enable you to allocate resources at a granular level and can be useful in optimizing the deployment of an application. The level of sophistication is limited by your cloud (whether it supports dynamic storage or storage configuration attributes), and by the charm in charge of that application (whether it supports storage persistence, additional cache, etc.). + + +Add storage +----------- + +To create and attach a storage instance to a unit, on a Unit object, use the `add_storage()` method, passing the storage name as an argument. For example: + +.. code:: python + + await my_unit.add_storage("pgdata", size=512) + + +To attach an existing storage to an application during deployment, on a connected Model object, use the `attach_storage` parameter of the `deploy()` method. + +.. code:: python + + await model.deploy('postgresql', attach_storage=[tag.storage("my-storage")]) + + +> See more: `add_storage() `_, `Unit (module) `_, `deploy() `_, `Model (module) `_ + +List available storage +----------------------- + +To list available storage instances, on a connected Model object, use the `list_storage()` method. For example: + +.. code:: python + + await model.list_storage() + +> See more: `list_storage() `_, `Model (module) `_ + + + +Detach storage +-------------- + +To detach a storage instance from a unit, on a Unit object, use the `detach_storage()` method, passing the storage id as an argument. For example: + +.. code:: python + + await my_unit.detach_storage("osd-devices/2") + + +> See more: `detach_storage() `_, `Unit (module) `_ + + +Attach storage +-------------- + +To attach an existing storage instance to a unit, on a Unit object, use the `attach_storage()` method, passing the storage id as an argument. For example: + +.. code:: python + + await my_unit.attach_storage(["osd-devices/2"]) + +> See more: `attach_storage() `_, `Unit (module) `_ + + +Remove storage +-------------- +> See also: :ref:`juju-removing-things` + +To remove a storage instance, on a connected Model object, use the `remove_storage()` method, passing the storage id as an argument. For example: + +.. code:: python + + # use force=True to remove storage even if it is currently attached + await my_model.remove_storage(["osd-devices/2"], force=True) + + +> See more: `remove_storage() `_, `Model (module) `_ + + diff --git a/docs/howto/manage-units.rst b/docs/howto/manage-units.rst new file mode 100644 index 00000000..5e041458 --- /dev/null +++ b/docs/howto/manage-units.rst @@ -0,0 +1,103 @@ +.. _manage-units: + +How to manage units +=================== + +> See also: :ref:`juju:unit` + +Add a unit +---------- + +To add a unit in `python-libjuju` client, you simply call `add_unit()` on your `Application` object, as follows: + +.. code:: python + + my_app.add_unit(count=3) + +> See more: `Application (module) `_, `Application.add_unit() `_, `Application.scale() `_ + +.. _control-the-number-of-units: +Control the number of units +--------------------------- + +To control the number of units of an application in `python-libjuju` client, you can use the `Application.add_unit()` and `Application.destroy_units()` methods, or the `Application.scale()` method, depending on whether you're working on a CAAS system (e.g. Kubernetes), or an IAAS system (e.g. lxd). + +If you're on an IAAS system (machine applications): + +.. code:: python + + u = my_app.add_unit() + my_app.destroy_units(u.name) # Note that the argument is the name of the unit + + # You may give multiple unit names to destroy at once + my_app.destroy_units(u1.name, u2.name) + + + If you're on a CAAS sytem (k8s applications): + +.. code:: python + + my_app.scale(4) + + +> See more: `Application (module) `_, `Application.add_unit() `_, `Application.scale() `_, `Application.destroy_units() `_ + + +Show details about a unit +------------------------- + +Too see details about a unit in `python-libjuju` client, you can use various fields and methods of a `Unit` object. For example, to get the `public_address` of a unit: + +.. code:: python + + my_unit.get_public_address() + +Or, to see if the unit is a leader: + +.. code:: python + + my_unit.is_leader_from_status() + +> See more: `Unit (methods) `_, `Unit.get_public_address() `_, `Unit.is_leader_from_status() `_ + + +Show the status of a unit +------------------------- + +To get the status of a unit on `pylibjuju-client`, you can use various (dynamically updated) status fields defined on a Unit object, such as: + +.. code:: python + + workload_st = my_unit.workload_status + agent_st = my_unit.agent_status + +> See more: `Unit status `_, `Unit (methods) `_, `Unit.workload_status (field) `_, `Unit.agent_status (field) `_ + + +Mark unit errors as resolved +---------------------------- + +To mark unit errors as resolved in the `python-libjuju` client, you can call the `resolved()` method on a `Unit` object: + +.. code:: python + + my_unit.resolved() + +> See more: `Unit.resolved()` + + +Remove a unit +------------- + +To remove individual units on `python-libjuju` client, simply use the `Application.destroy_units()` method: + + +.. code:: python + + my_app.destroy_units(u.name) # Note that the argument is the name of the unit + + # You may give multiple unit names to destroy at once + my_app.destroy_units(u1.name, u2.name) + +> See more: `Application (module) `_, `Application.destroy_units() `_ + diff --git a/docs/howto/manage-users.rst b/docs/howto/manage-users.rst new file mode 100644 index 00000000..540bec54 --- /dev/null +++ b/docs/howto/manage-users.rst @@ -0,0 +1,135 @@ +.. _manage-users: + + +How to manage users +=================== + +> See also: :ref:`juju:user` + + +Add a user +---------- + + +To add a user to a controller, on a connected Controller object, use the `add_user()` method. + +.. code:: python + + await my_controller.add_user("alex") + +> See more: `add_user() `_ + + +List all the known users +------------------------ + +To view a list of all the users known (i.e., allowed to log in) to a controller, on a connected Controller object, use the `get_users()` method. + +.. code:: python + + await my_controller.get_users() + +> See more: `get_users() `_ + + +View details about a user +------------------------- + +To view details about a specific user, on a connected Controller, use the `get_user()` method to retrieve a User object that encapsulates everything about that user. Using that object, you can access all the details (via the object properties) for that user. + +.. code:: python + + user_object = await my_controller.get_user("alice") + # Then we can access all the properties to view details + print(user_object.display_name) + print(user_object.access) + print(user_object.date_created) + print(user_object.last_connection) + +> See more: `get_user() `_, `User (module) `_ + + +View details about the current user +----------------------------------- + +To see details about the current user, on a connected Controller, use the `get_current_user()` method to retrieve a User object that encapsulates everything about the current user. Using that object, you can access all the details (via the object properties) for that user. + +.. code:: python + + user_object = await my_controller.get_current_user() + # Then we can access all the properties to view details + print(user_object.display_name) + print(user_object.access) + print(user_object.date_created) + print(user_object.last_connection) + +> See more: `get_current_user() `_, `User (module) `_ + + +Manage a user's access level +---------------------------- +> See also: :ref:`juju:user-access-levels` + +To manage a user's access to a controller, a model, or an offer, on a User object, use the `grant()` and `revoke()` methods to grant or revoke a certain access level to a user. + +.. code:: python + + # grant a superuser access to the controller (that the user is on) + await user_object.grant('superuser') + + # grant user the access to see a model + await user_object.grant("read", model_name="test-model") + + # revoke ‘read’ (and ‘write’) access from user for application offer ‘fred/prod.hosted-mysql’: + await user_object.revoke("read", offer_url="fred/prod.hosted-mysql") + +> See more: `grant() `_, `revoke() `_, `User (module) `_ + + +Manager a user's login details +------------------------------ + +To set or change a user's password, on a User object, use the `set_password()` method. + +.. code:: python + + await user_object.set_password('123') + + +> See more: `set_password() `_, `User (module) `_ + +Manage a user's enabled status +------------------------------ + +To enable or disable a user, on a User object, use the `enable()` and `disable()` methods. + +.. code:: python + + await user_object.enable() + + await user_object.disable() + +You can also check if a user is enabled or disabled using the `enabled` and `disabled` properties on the Unit object. + +.. code:: python + + # re-enable a disabled user + if user_object.disabled: + await user_object.enable() + + +> See more: `enable() `_, `disable() `_, `User (module) `_ + + +Remove a user +------------- + +To remove a user, on a connected Controller object, use the `remove_user()` method. + +.. code:: python + + await my_controller.remove_user("bob") + + +> See more: `remove_user() `_, `User (module) `_ + diff --git a/docs/index.rst b/docs/index.rst index 07eb01c1..789685bb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,6 +10,14 @@ Table of Contents ----------------- +.. toctree:: + :caption: How-to guides + :glob: + :maxdepth: 3 + + howto/index + + .. toctree:: :caption: Overview :glob: From ecaac2213057037fa2d6bc1e29eddce424768d46 Mon Sep 17 00:00:00 2001 From: James Garner Date: Thu, 19 Dec 2024 17:44:43 +1300 Subject: [PATCH 2/4] fix: remove unwanted files, update .gitignore, run pre-commit --- .gitignore | 1 + build/lib/juju/__init__.py | 3 - build/lib/juju/access.py | 49 - build/lib/juju/action.py | 24 - build/lib/juju/annotation.py | 12 - build/lib/juju/annotationhelper.py | 38 - build/lib/juju/application.py | 1096 - build/lib/juju/bundle.py | 1197 - build/lib/juju/charm.py | 12 - build/lib/juju/charmhub.py | 181 - build/lib/juju/client/__init__.py | 3 - build/lib/juju/client/_client.py | 236 - build/lib/juju/client/_client1.py | 550 - build/lib/juju/client/_client10.py | 674 - build/lib/juju/client/_client11.py | 481 - build/lib/juju/client/_client12.py | 466 - build/lib/juju/client/_client17.py | 817 - build/lib/juju/client/_client19.py | 845 - build/lib/juju/client/_client2.py | 326 - build/lib/juju/client/_client20.py | 845 - build/lib/juju/client/_client3.py | 605 - build/lib/juju/client/_client4.py | 572 - build/lib/juju/client/_client5.py | 285 - build/lib/juju/client/_client6.py | 781 - build/lib/juju/client/_client7.py | 1014 - build/lib/juju/client/_client8.py | 68 - build/lib/juju/client/_client9.py | 347 - build/lib/juju/client/_definitions.py | 22723 ---------------- build/lib/juju/client/client.py | 35 - build/lib/juju/client/codegen.py | 53 - build/lib/juju/client/connection.py | 924 - build/lib/juju/client/connector.py | 232 - build/lib/juju/client/facade.py | 989 - build/lib/juju/client/facade_versions.py | 139 - build/lib/juju/client/flags.py | 17 - build/lib/juju/client/gocookies.py | 108 - build/lib/juju/client/jujudata.py | 190 - build/lib/juju/client/overrides.py | 418 - build/lib/juju/client/proxy/__init__.py | 3 - build/lib/juju/client/proxy/factory.py | 29 - .../juju/client/proxy/kubernetes/__init__.py | 3 - .../lib/juju/client/proxy/kubernetes/proxy.py | 83 - build/lib/juju/client/proxy/proxy.py | 26 - build/lib/juju/client/runner.py | 25 - build/lib/juju/constraints.py | 254 - build/lib/juju/controller.py | 1009 - build/lib/juju/delta.py | 140 - build/lib/juju/errors.py | 156 - build/lib/juju/exceptions.py | 6 - build/lib/juju/jasyncio.py | 174 - build/lib/juju/juju.py | 37 - build/lib/juju/loop.py | 12 - build/lib/juju/machine.py | 310 - build/lib/juju/model.py | 3359 --- build/lib/juju/names.py | 93 - build/lib/juju/offerendpoints.py | 216 - build/lib/juju/origin.py | 212 - build/lib/juju/placement.py | 60 - build/lib/juju/provisioner.py | 344 - build/lib/juju/py.typed | 0 build/lib/juju/relation.py | 147 - build/lib/juju/remoteapplication.py | 38 - build/lib/juju/secrets.py | 124 - build/lib/juju/status.py | 252 - build/lib/juju/tag.py | 64 - build/lib/juju/unit.py | 446 - build/lib/juju/url.py | 188 - build/lib/juju/user.py | 183 - build/lib/juju/utils.py | 602 - build/lib/juju/version.py | 9 - docs/conf.py | 2 +- docs/howto/#manage-machines.rst# | 161 - docs/howto/index.rst | 1 - docs/howto/manage-actions.rst | 13 +- docs/howto/manage-applications.rst | 46 +- docs/howto/manage-charm-resources.rst | 6 +- docs/howto/manage-charms.rst | 37 +- docs/howto/manage-clouds.rst | 4 +- docs/howto/manage-controllers.rst | 10 +- docs/howto/manage-credentials.rst | 9 +- docs/howto/manage-machines.rst | 24 +- docs/howto/manage-models.rst | 13 +- docs/howto/manage-offers.rst | 17 +- docs/howto/manage-python-libjuju.rst | 19 +- docs/howto/manage-relations.rst | 6 +- docs/howto/manage-secret-backends.rst | 8 +- docs/howto/manage-secrets.rst | 20 +- docs/howto/manage-spaces.rst | 9 +- docs/howto/manage-ssh-keys.rst | 4 +- docs/howto/manage-storage-pools.rst | 13 +- docs/howto/manage-storage.rst | 16 +- docs/howto/manage-units.rst | 27 +- docs/howto/manage-users.rst | 29 +- docs/index.rst | 6 +- 94 files changed, 160 insertions(+), 46300 deletions(-) delete mode 100644 build/lib/juju/__init__.py delete mode 100644 build/lib/juju/access.py delete mode 100644 build/lib/juju/action.py delete mode 100644 build/lib/juju/annotation.py delete mode 100644 build/lib/juju/annotationhelper.py delete mode 100644 build/lib/juju/application.py delete mode 100644 build/lib/juju/bundle.py delete mode 100644 build/lib/juju/charm.py delete mode 100644 build/lib/juju/charmhub.py delete mode 100644 build/lib/juju/client/__init__.py delete mode 100644 build/lib/juju/client/_client.py delete mode 100644 build/lib/juju/client/_client1.py delete mode 100644 build/lib/juju/client/_client10.py delete mode 100644 build/lib/juju/client/_client11.py delete mode 100644 build/lib/juju/client/_client12.py delete mode 100644 build/lib/juju/client/_client17.py delete mode 100644 build/lib/juju/client/_client19.py delete mode 100644 build/lib/juju/client/_client2.py delete mode 100644 build/lib/juju/client/_client20.py delete mode 100644 build/lib/juju/client/_client3.py delete mode 100644 build/lib/juju/client/_client4.py delete mode 100644 build/lib/juju/client/_client5.py delete mode 100644 build/lib/juju/client/_client6.py delete mode 100644 build/lib/juju/client/_client7.py delete mode 100644 build/lib/juju/client/_client8.py delete mode 100644 build/lib/juju/client/_client9.py delete mode 100644 build/lib/juju/client/_definitions.py delete mode 100644 build/lib/juju/client/client.py delete mode 100644 build/lib/juju/client/codegen.py delete mode 100644 build/lib/juju/client/connection.py delete mode 100644 build/lib/juju/client/connector.py delete mode 100644 build/lib/juju/client/facade.py delete mode 100644 build/lib/juju/client/facade_versions.py delete mode 100644 build/lib/juju/client/flags.py delete mode 100644 build/lib/juju/client/gocookies.py delete mode 100644 build/lib/juju/client/jujudata.py delete mode 100644 build/lib/juju/client/overrides.py delete mode 100644 build/lib/juju/client/proxy/__init__.py delete mode 100644 build/lib/juju/client/proxy/factory.py delete mode 100644 build/lib/juju/client/proxy/kubernetes/__init__.py delete mode 100644 build/lib/juju/client/proxy/kubernetes/proxy.py delete mode 100644 build/lib/juju/client/proxy/proxy.py delete mode 100644 build/lib/juju/client/runner.py delete mode 100644 build/lib/juju/constraints.py delete mode 100644 build/lib/juju/controller.py delete mode 100644 build/lib/juju/delta.py delete mode 100644 build/lib/juju/errors.py delete mode 100644 build/lib/juju/exceptions.py delete mode 100644 build/lib/juju/jasyncio.py delete mode 100644 build/lib/juju/juju.py delete mode 100644 build/lib/juju/loop.py delete mode 100644 build/lib/juju/machine.py delete mode 100644 build/lib/juju/model.py delete mode 100644 build/lib/juju/names.py delete mode 100644 build/lib/juju/offerendpoints.py delete mode 100644 build/lib/juju/origin.py delete mode 100644 build/lib/juju/placement.py delete mode 100644 build/lib/juju/provisioner.py delete mode 100644 build/lib/juju/py.typed delete mode 100644 build/lib/juju/relation.py delete mode 100644 build/lib/juju/remoteapplication.py delete mode 100644 build/lib/juju/secrets.py delete mode 100644 build/lib/juju/status.py delete mode 100644 build/lib/juju/tag.py delete mode 100644 build/lib/juju/unit.py delete mode 100644 build/lib/juju/url.py delete mode 100644 build/lib/juju/user.py delete mode 100644 build/lib/juju/utils.py delete mode 100644 build/lib/juju/version.py delete mode 100644 docs/howto/#manage-machines.rst# diff --git a/.gitignore b/.gitignore index f979a2fb..567a5ec4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ pytestdebug.log deb_dist *.tar.gz .idea/ +build/ diff --git a/build/lib/juju/__init__.py b/build/lib/juju/__init__.py deleted file mode 100644 index 9da2b17a..00000000 --- a/build/lib/juju/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright 2024 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -"""Python Library for Juju.""" diff --git a/build/lib/juju/access.py b/build/lib/juju/access.py deleted file mode 100644 index de3c8b7a..00000000 --- a/build/lib/juju/access.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -from .errors import JujuNotValid - -# No permissions at all -NO_ACCESS = "" - -# Model permissions - -READ_ACCESS = "read" -WRITE_ACCESS = "write" -CONSUME_ACCESS = "consume" -ADMIN_ACCESS = "admin" -MODEL_ACCESS_LEVELS = {READ_ACCESS, WRITE_ACCESS, CONSUME_ACCESS, ADMIN_ACCESS} - -# Controller permissions - -LOGIN_ACCESS = "login" -ADD_MODEL_ACCESS = "add-model" -SUPERUSER_ACCESS = "superuser" -CONTROLLER_ACCESS_LEVELS = {LOGIN_ACCESS, ADD_MODEL_ACCESS, SUPERUSER_ACCESS} - -OFFER_ACCESS_LEVELS = {READ_ACCESS, CONSUME_ACCESS, ADMIN_ACCESS} - -ALL_ACCESS_LEVELS = MODEL_ACCESS_LEVELS.union(CONTROLLER_ACCESS_LEVELS) - - -def validate_access_level(access): - if access not in ALL_ACCESS_LEVELS: - raise JujuNotValid("access level", access) - - -def validate_offer_access(access): - validate_access_level() - if access not in OFFER_ACCESS_LEVELS: - raise JujuNotValid("offer access level", access) - - -def validate_model_access(access): - validate_access_level(access) - if access not in MODEL_ACCESS_LEVELS: - raise JujuNotValid("model access level", access) - - -def validate_controller_access(access): - validate_access_level(access) - if access not in CONTROLLER_ACCESS_LEVELS: - raise JujuNotValid("controller access level", access) diff --git a/build/lib/juju/action.py b/build/lib/juju/action.py deleted file mode 100644 index 7aa729d6..00000000 --- a/build/lib/juju/action.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -from . import model - - -class Action(model.ModelEntity): - def __init__(self, entity_id, model, history_index=-1, connected=True): - super().__init__(entity_id, model, history_index, connected) - self.results = {} - self._status = self.data["status"] - - @property - def status(self): - return self._status - - async def fetch_output(self): - completed_action = await self.model._get_completed_action(self.id) - self.results = completed_action.output or {} - self._status = completed_action.status - - async def wait(self): - self.results or await self.fetch_output() - return self diff --git a/build/lib/juju/annotation.py b/build/lib/juju/annotation.py deleted file mode 100644 index 2d70eafa..00000000 --- a/build/lib/juju/annotation.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import logging - -from . import model - -log = logging.getLogger(__name__) - - -class Annotation(model.ModelEntity): - pass diff --git a/build/lib/juju/annotationhelper.py b/build/lib/juju/annotationhelper.py deleted file mode 100644 index d8369125..00000000 --- a/build/lib/juju/annotationhelper.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import logging - -from .client import client -from .errors import JujuError - -log = logging.getLogger(__name__) - - -async def _get_annotations(entity_tag, connection): - """Get annotations for the specified entity - - :return dict: The annotations for the entity - """ - facade = client.AnnotationsFacade.from_connection(connection) - result = (await facade.Get(entities=[{"tag": entity_tag}])).results[0] - if result.error is not None: - raise JujuError(result.error) - return result.annotations - - -async def _set_annotations(entity_tag, annotations, connection): - """Set annotations on the specified entity. - - :param annotations map[string]string: the annotations as key/value - pairs. - """ - # TODO: ensure annotations is dict with only string keys - # and values. - log.debug("Updating annotations on %s", entity_tag) - facade = client.AnnotationsFacade.from_connection(connection) - args = client.EntityAnnotations( - entity=entity_tag, - annotations=annotations, - ) - return await facade.Set(annotations=[args]) diff --git a/build/lib/juju/application.py b/build/lib/juju/application.py deleted file mode 100644 index a57e336c..00000000 --- a/build/lib/juju/application.py +++ /dev/null @@ -1,1096 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import hashlib -import json -import logging -from pathlib import Path -from typing import Dict, List, Optional, Union - -from typing_extensions import deprecated - -from . import jasyncio, model, tag, utils -from .annotationhelper import _get_annotations, _set_annotations -from .bundle import get_charm_series, is_local_charm -from .client import _definitions, client -from .errors import JujuApplicationConfigError, JujuError -from .origin import Channel -from .placement import parse as parse_placement -from .relation import Relation -from .status import derive_status -from .url import URL -from .utils import block_until -from .version import DEFAULT_ARCHITECTURE - -log = logging.getLogger(__name__) - - -class Application(model.ModelEntity): - """Represents the current state of a deployed application. - - In the current library version, as well entire 2.x and 3.x series, - the data is supplied by Juju AllWatcher notifications, also known as deltas - for the specific application. The fields are declared here: - https://github.com/juju/juju/blob/be8a779/core/multiwatcher/types.go#L191-L209 - - The fields marked deprecated below will be removed in version 4.0 because - a different API must be used against Juju 4. - """ - - @property - def name(self) -> str: - return self.entity_id - - @property - def exposed(self) -> bool: - return self.safe_data["exposed"] - - @property - @deprecated("Application.owner_tag is deprecated and will be removed in v4") - def owner_tag(self) -> str: - return self.safe_data["owner-tag"] - - @property - def life(self) -> str: - return self.safe_data["life"] - - @property - @deprecated("Application.min_units is deprecated and will be removed in v4") - def min_units(self) -> int: - return self.safe_data["min-units"] - - @property - def constraints(self) -> Dict[str, Union[str, int, bool]]: - return self.safe_data["constraints"] - - @property - @deprecated("Application.subordinate is deprecated and will be removed in v4") - def subordinate(self) -> bool: - return self.safe_data["subordinate"] - - @property - @deprecated( - "Application.workload_version is deprecated and will be removed in v4, use Unit.workload_version instead." - ) - def workload_version(self) -> str: - return self.safe_data["workload-version"] - - @property - def _unit_match_pattern(self): - return rf"^{self.entity_id}.*$" - - def _facade(self): - return client.ApplicationFacade.from_connection(self.connection) - - def _facade_version(self): - return client.ApplicationFacade.best_facade_version(self.connection) - - def on_unit_add(self, callable_): - """Add a "unit added" observer to this entity, which will be called - whenever a unit is added to this application. - - """ - self.model.add_observer(callable_, "unit", "add", self._unit_match_pattern) - - def on_unit_remove(self, callable_): - """Add a "unit removed" observer to this entity, which will be called - whenever a unit is removed from this application. - - """ - self.model.add_observer(callable_, "unit", "remove", self._unit_match_pattern) - - @property - def units(self): - return [ - unit for unit in self.model.units.values() if unit.application == self.name - ] - - @property - def subordinate_units(self): - """Returns the subordinate units of this application""" - return [u for u in self.units if u.is_subordinate] - - @property - def relations(self) -> List[Relation]: - return [rel for rel in self.model.relations if rel.matches(self.name)] - - def related_applications(self, endpoint_name=None): - apps = {} - for rel in self.relations: - if rel.is_peer: - local_ep, remote_ep = rel.endpoints - else: - - def is_us(ep): - return ep.application.name == self.name - - local_ep, remote_ep = sorted(rel.endpoints, key=is_us) - if endpoint_name is not None and endpoint_name != local_ep.name: - continue - apps[remote_ep.application.name] = remote_ep.application - return apps - - @property - def status(self): - """Get the application status. - - If the application is unknown it will attempt to derive the unit - workload status and highlight the most relevant (severity). - """ - status = self.safe_data["status"]["current"] - if status == "unset": - known_statuses = [unit.workload_status for unit in self.units] - # If the self.get_status() is called (i.e. the status - # is received by FullStatus from the API) then add - # that into this computation as it might be more up - # to date (and more severe). - known_statuses.append(self._status) - return derive_status(known_statuses) - return status - - @property - def status_message(self): - """Get the application status message, as set by the charm's leader.""" - return self.safe_data["status"]["message"] - - @property - def tag(self): - return tag.application(self.name) - - async def add_relation(self, local_relation, remote_relation): - """.. deprecated:: 2.9.9 - Use ``relate()`` instead - """ - return await self.relate(local_relation, remote_relation) - - async def relate(self, local_relation, remote_relation): - """Add a relation to another application. - - :param str local_relation: Name of relation on this application - :param str remote_relation: Name of relation on the other - application in the form '[:]' - - """ - if ":" not in local_relation: - local_relation = f"{self.name}:{local_relation}" - - return await self.model.relate(local_relation, remote_relation) - - async def add_unit(self, count=1, to=None, attach_storage=[]): - """Add one or more units to this application. - - :param int count: Number of units to add - :param [str] attach_storage: Existing storage to attach to the deployed unit - (not available on k8s models) - :param str to: Placement directive, e.g.:: - '23' - machine 23 - 'lxc:7' - new lxc container on machine 7 - '24/lxc/3' - lxc container 3 or machine 24 - - If None, a new machine is provisioned. - - """ - if self.model.info.type_ == "caas": - log.warning( - "adding units to a container-based model not supported, auto-switching to scale" - ) - return await self.scale(scale_change=count) - - app_facade = self._facade() - - log.debug("Adding %s unit%s to %s", count, "" if count == 1 else "s", self.name) - - result = await app_facade.AddUnits( - application=self.name, - placement=parse_placement(to) if to else None, - num_units=count, - attach_storage=attach_storage, - ) - - return await jasyncio.gather(*[ - jasyncio.ensure_future(self.model._wait_for_new("unit", unit_id)) - for unit_id in result.units - ]) - - add_units = add_unit - - async def scale(self, scale=None, scale_change=None): - """Set or adjust the scale of this (K8s) application. - - One or the other of scale or scale_change must be provided. - - :param int scale: Scale to which to set this application. - :param int scale_change: Amount by which to adjust the scale of this - application (can be positive or negative). - """ - app_facade = self._facade() - - if (scale, scale_change) == (None, None): - raise ValueError("Must provide either scale or scale_change") - - log.debug( - "Scaling application %s %s %s", - self.name, - "to" if scale else "by", - scale or scale_change, - ) - - await app_facade.ScaleApplications( - applications=[ - client.ScaleApplicationParams( - application_tag=self.tag, scale=scale, scale_change=scale_change - ) - ] - ) - - async def destroy_relation( - self, local_relation, remote_relation, block_until_done: bool = False - ): - """Remove a relation to another application. - - :param str local_relation: Name of relation on this application - :param str remote_relation: Name of relation on the other - application in the form '[:]' - :param bool block_until_done: Wait until the relation is completely removed. - - """ - if ":" not in local_relation: - local_relation = f"{self.name}:{local_relation}" - - app_facade = self._facade() - - log.debug("Destroying relation %s <-> %s", local_relation, remote_relation) - - await app_facade.DestroyRelation(endpoints=[local_relation, remote_relation]) - if block_until_done: - await block_until( - lambda: not any( - relation.matches(local_relation, remote_relation) - for relation in self.relations - ) - ) - - remove_relation = destroy_relation - - async def destroy_unit(self, *unit_names): - """Destroy units by name.""" - return await self.model.destroy_units(*unit_names) - - destroy_units = destroy_unit - - async def destroy(self, destroy_storage=False, force=False, no_wait=False): - """Remove this application from the model. - - :param bool destroy_storage: Destroy storage attached to application unit. (=false) - :param bool force: Completely remove an application and all its dependencies. (=false) - :param bool no_wait: Rush through application removal without waiting for each individual step to complete (=false) - :param bool block: Blocks until the application is removed from the model - """ - if no_wait and not force: - raise JujuError("--no-wait without --force is not valid") - - app_facade = self._facade() - - log.debug( - f"Destroying {self.name} with parameters -- destroy-storage : {destroy_storage} -- force : {force} -- no-wait : {no_wait}" - ) - - res = await app_facade.DestroyApplication( - applications=[ - client.DestroyApplicationParams( - application_tag=self.tag, - destroy_storage=destroy_storage, - force=force, - max_wait=0 if no_wait else None, - ) - ] - ) - return res - - remove = destroy - - def supports_granular_expose_parameters(self): - """Returns true if the controller supports granular, per-endpoint - expose parameters. - """ - return self._facade_version() >= 13 - - async def expose(self, exposed_endpoints=None): - """Make a subset of the application endpoints or the entire application - available over the network. - - If the exposed_endpoints argument is not provided, all opened port - ranges for the application will become reachable from 0.0.0.0/0. - - On juju 2.9 and onwards, the exposed_endpoints argument may be used - to specify a list of spaces and or CIDRs that should be able to - reach the port ranges opened for a particular subnet. The - exposed_endpoints parameter is a map where keys are endpoint names - or the empty string ("") which works as a wildcard for all endpoints - and values are ExposedEndpoint instances. - - When targeting an older juju controller, the exposed_endpoints param - is not supported and an error will be raised if it is provided. - """ - app_facade = self._facade() - ctrl_supports_expose_parameters = self.supports_granular_expose_parameters() - - if exposed_endpoints is not None: - if not isinstance(exposed_endpoints, dict): - raise ValueError( - "endpoints must be a dictionary with ExposedEndpoint values" - ) - - # The bundle changes code will pass in raw dicts with the exposed - # endpoint data. We need to convert those into ExposedEndpoints - for k, v in exposed_endpoints.items(): - if not isinstance(v, ExposedEndpoint): - exposed_endpoints[k] = ExposedEndpoint.from_dict(v) - - # Check if the specified exposed_endpoints would cause security - # issues when applied to a pre 2.9 controller. - has_more_than_one_endpoints = len(exposed_endpoints) > 1 - has_non_wildcard_endpoint = ( - len(exposed_endpoints) > 0 and "" not in exposed_endpoints - ) - has_wildcard_endpoint_with_spaces_or_non_wildcard_cidrs = ( - "" in exposed_endpoints - and ( - exposed_endpoints[""].includes_non_wildcard_cidrs() - or exposed_endpoints[""].includes_spaces() - ) - ) - - is_security_risk = not ctrl_supports_expose_parameters and ( - has_more_than_one_endpoints - or has_non_wildcard_endpoint - or has_wildcard_endpoint_with_spaces_or_non_wildcard_cidrs - ) - - if is_security_risk: - raise JujuError( - "controller does not support granular expose parameters; applying this change would make all open application ports accessible from 0.0.0.0/0" - ) - - for endpoint, expose_details in exposed_endpoints.items(): - access_from = "from CIDRs 0.0.0.0/0 and ::/0" - if isinstance(expose_details, ExposedEndpoint): - access_from = str(expose_details) - - if endpoint == "": - log.debug( - "expose all endpoints of %s and allow access %s", - self.name, - access_from, - ) - else: - log.debug( - "override expose settings for endpoint %s of %s and %s", - endpoint, - self.name, - access_from, - ) - - # Map ExposedEndpoint entries to a dict we can pass to the facade. - exposed_endpoints = {k: v.to_dict() for k, v in exposed_endpoints.items()} - else: - log.debug( - "expose all endpoints of %s and allow access from CIDRs 0.0.0.0/0 and ::/0", - self.name, - ) - - if not ctrl_supports_expose_parameters: - return await app_facade.Expose(application=self.name) - - return await app_facade.Expose( - application=self.name, exposed_endpoints=exposed_endpoints - ) - - async def unexpose(self, exposed_endpoints=None): - """Prevent a subset of the application endpoints or the entire - application from being reached over the network. - - If the exposed_endpoints argument is not provided, the entire - application will be unexposed. - - On juju 2.9 and onwards, the exposed_endpoints argument may be used - to specify a list of endpoint names whose port ranges should be - unexposed. - - When targeting an older juju controller, the exposed_endpoints param - is not supported and an error will be raised if it is provided. - """ - app_facade = self._facade() - facade_version = self._facade_version() - - # Check if an endpoint list is provided - if exposed_endpoints is not None and len(exposed_endpoints) > 0: - if facade_version < 13: - raise JujuError( - "controller does not support granular expose parameters; applying this change would unexpose the application" - ) - - log.debug( - "Unexposing endpoints %s of %s", ",".join(exposed_endpoints), self.name - ) - return await app_facade.Unexpose( - application=self.name, exposed_endpoints=exposed_endpoints - ) - - # Just expose the entire application - log.debug("Unexposing %s", self.name) - return await app_facade.Unexpose(application=self.name) - - async def get_series(self): - """Return the series on which the application is deployed - - :return: str series - """ - app_facade = self._facade() - - log.debug("Getting series for %s", self.name) - - results = await app_facade.Get(application=self.name) - if self._facade_version() >= 15: - base_channel = results.base.channel - return utils.base_channel_to_series(base_channel) - return results.series - - async def get_config(self): - """Return the configuration settings dict for this application.""" - app_facade = self._facade() - - log.debug("Getting config for %s", self.name) - - return (await app_facade.Get(application=self.name)).config - - async def get_trusted(self): - """Return the trusted configuration setting for this application.""" - if self.model.info.agent_version < client.Number.from_json("2.4.0"): - raise NotImplementedError( - f"trusted is not supported on model version {self.model.info.agent_version}" - ) - - app_facade = self._facade() - - log.debug("Getting config for %s", self.name) - - config = await app_facade.Get(application=self.name) - if "trust" in config.config: - return config.config["trust"]["value"] is True - - app_config = config.application_config - return app_config["trust"]["value"] is True - - async def set_trusted(self, trust: bool): - """Set the trusted configuration of the application. - - :param bool trust: Trust the application or not - """ - if self.model.info.agent_version < client.Number.from_json("2.4.0"): - raise NotImplementedError( - f"trusted is not supported on model version {self.model.info.agent_version}" - ) - - # clamp trust to exactly the value juju expects, rather than allowing - # anything in the config. - app_facade = self._facade() - - config = {"trust": json.dumps(trust)} - log.debug("Setting config for %s: %s", self.name, config) - - # Unfortunately we have to do this in a lazy fashion, attempting to use - # the method early will cause an error. Attempting to call this - # dynamically causes issues with how the client code is wired up... we - # end up with a missing _toPy attr. - # Using a lambda to only throw it away when it's wrong seems a problem - # as well. - config_method = None - if self._facade_version() < 13: - config_method = app_facade.SetApplicationsConfig - else: - config_method = app_facade.SetConfigs - return await config_method( - args=[ - { - "application": self.name, - "config": config, - } - ] - ) - - async def get_constraints(self): - """Return the machine constraints dict for this application.""" - app_facade = self._facade() - - log.debug("Getting constraints for %s", self.name) - - result = (await app_facade.Get(application=self.name)).constraints - return vars(result) if result else result - - async def get_actions(self, schema=False): - """Get actions defined for this application. - - :param bool schema: Return the full action schema - :return dict: The charms actions, empty dict if none are defined. - """ - actions = {} - entity = {"tag": self.tag} - action_facade = client.ActionFacade.from_connection(self.connection) - results = ( - await action_facade.ApplicationsCharmsActions(entities=[entity]) - ).results - for result in results: - if result.application_tag == self.tag and result.actions: - actions = result.actions - break - if not schema: - actions = {k: v.description for k, v in actions.items()} - return actions - - async def get_status(self): - """Get the application status using info from the FullStatus - as well, because it might be more up to date than our model - - :return: str status - """ - client_facade = client.ClientFacade.from_connection(self.connection) - - full_status = await client_facade.FullStatus(patterns=None) - _app = full_status.applications.get(self.name, None) - if not _app: - raise JujuError(f"application is not in FullStatus : {self.name}") - - self._status = derive_status([self.status, _app.status.status]) - return self._status - - def attach_resource(self, resource_name, file_name, file_obj): - """Updates the resource for an application by uploading file from - local disk to the Juju controller. - - :param str resource_name: Name of the resource to be updated. - :param str file_name: Name of the local file to be uploaded. - :param TextIOWrapper file_obj: Actual object to be read for data. - """ - conn, headers, path_prefix = self.connection.https_connection() - - url = f"{path_prefix}/applications/{self.name}/resources/{resource_name}" - - data = file_obj.read() - - headers["Content-Type"] = "application/octet-stream" - headers["Content-Length"] = len(data) - data_bytes = data if isinstance(data, bytes) else bytes(data, "utf-8") - headers["Content-Sha384"] = hashlib.sha384(data_bytes).hexdigest() - - file_name = str(file_name) - if not file_name.startswith("./"): - file_name = "./" + file_name - - headers["Content-Disposition"] = f'form-data; filename="{file_name}"' - headers["Accept-Encoding"] = "gzip" - headers["Bakery-Protocol-Version"] = 3 - headers["Connection"] = "close" - - conn.request("PUT", url, data, headers) - response = conn.getresponse() - result = response.read().decode() - if not response.status == 200: - raise JujuError(result) - - async def get_resources(self): - """Return resources for this application. - - Returns a dict mapping resource name to - :class:`~juju._definitions.CharmResource` instances. - """ - facade = client.ResourcesFacade.from_connection(self.connection) - response = await facade.ListResources(entities=[client.Entity(self.tag)]) - - resources = dict() - for result in response.results: - for resource in result.charm_store_resources or []: - resources[resource.name] = resource - for resource in result.resources or []: - if resource.charmresource: - resource = resource.charmresource - resources[resource.name] = resource - return resources - - async def run(self, command, timeout=None): - """Run command on all units for this application. - - :param str command: The command to run - :param int timeout: Time to wait before command is considered failed - - """ - action = client.ActionFacade.from_connection(self.connection) - - log.debug("Running `%s` on all units of %s", command, self.name) - - # TODO this should return a list of Actions - return await action.Run( - applications=[self.name], - commands=command, - machines=[], - timeout=timeout, - units=[], - ) - - @property - def charm_name(self): - """Get the charm name of this application - - :return str: The name of the charm - """ - return URL.parse(self.charm_url).name - - @property - def charm_url(self): - """Get the charm url for this application - - :return str: The charm url - """ - return self.safe_data["charm-url"] - - async def get_annotations(self): - """Get annotations on this application. - - :return dict: The annotations for this application - """ - return await _get_annotations(self.tag, self.connection) - - async def set_annotations(self, annotations): - """Set annotations on this application. - - :param annotations map[string]string: the annotations as key/value - pairs. - - """ - return await _set_annotations(self.tag, annotations, self.connection) - - async def set_config(self, config): - """Set configuration options for this application. - - :param config: Dict of configuration to set - """ - app_facade = self._facade() - - log.debug("Setting config for %s: %s", self.name, config) - - str_config = {} - for k, v in config.items(): - if isinstance(v, str): - str_config[k] = v - elif isinstance(v, dict): - # pairs with a value of None are ignored - if v.get("value", False): - str_config[k] = str(v.get("value")) - else: - raise JujuApplicationConfigError(config, [k, v]) - - return await app_facade.SetConfigs( - args=[ - { - "application": self.name, - "config": str_config, - } - ] - ) - - async def reset_config(self, to_default): - """Restore application config to default values. - - :param list to_default: A list of config options to be reset to their - default value. - """ - app_facade = self._facade() - - log.debug("Restoring default config for %s: %s", self.name, to_default) - - return await app_facade.UnsetApplicationsConfig( - args=[ - { - "application": self.name, - "options": to_default, - } - ] - ) - - async def set_constraints(self, constraints): - """Set machine constraints for this application. - - :param dict constraints: Dict of machine constraints - - """ - app_facade = self._facade() - - log.debug("Setting constraints for %s: %s", self.name, constraints) - - return await app_facade.SetConstraints( - application=self.name, constraints=constraints - ) - - async def refresh( - self, - channel: Optional[str] = None, - force: bool = False, - force_series: bool = False, - force_units: bool = False, - path: Optional[Union[Path, str]] = None, - resources: Optional[Dict[str, str]] = None, - revision: Optional[int] = None, - switch: Optional[str] = None, - ): - """Refresh the charm for this application. - - :param str|None channel: Channel to use when getting the charm from the - charm store, e.g. 'development' - :param bool force_series: Refresh even if series of deployed - application is not supported by the new charm - :param bool force_units: Refresh all units immediately, even if in - error state - :param Path|str|None path: Refresh to a charm located at path - :param dict[str,str]|None resources: Dictionary of resource name/filepath pairs - :param int|None revision: Explicit refresh revision - :param str|None switch: URL of a different charm to cross-grade to - - """ - if switch is not None and path is not None: - raise ValueError("switch and path are mutually exclusive") - - if switch is not None and revision is not None: - raise ValueError("switch and revision are mutually exclusive") - - app_facade = self._facade() - charms_facade = client.CharmsFacade.from_connection(self.connection) - - # 1 - Figure out the destination origin and destination charm_url - # 2 - Then take care of the resources - # 3 - Finally execute the upgrade - - # Get the charm URL and charm origin of the given application is running at present. - charm_url_origin_result = await app_facade.GetCharmURLOrigin( - application=self.name - ) - if charm_url_origin_result.error is not None: - err = charm_url_origin_result.error - raise JujuError(f"{err.code} : {err.message}") - - current_origin = charm_url_origin_result.charm_origin - if path is not None or (switch is not None and is_local_charm(switch)): - local_path = path or switch - assert local_path - - await self.local_refresh( - charm_origin=current_origin, - force=force, - force_series=force_series, - force_units=force_units, - path=local_path, - resources=resources, - ) - return - - origin = _refresh_origin(current_origin, channel, revision) - - # If switch is not None at this point, that means it's a switch to a store charm - charm_url = switch or charm_url_origin_result.url - parsed_url = URL.parse(charm_url) - charm_name = parsed_url.name - - if parsed_url.schema is None: - raise JujuError( - f"A ch: or cs: schema is required for application refresh, given : {parsed_url!s}" - ) - - # Resolve the given charm URLs with an optionally specified preferred channel. - # Channel provided via CharmOrigin. - resolved_charm_with_channel_results = await charms_facade.ResolveCharms( - resolve=[ - client.ResolveCharmWithChannel( - charm_origin=origin, - switch_charm=bool(switch), - reference=charm_url, - ) - ] - ) - resolved_charm = resolved_charm_with_channel_results.results[0] - - # Get the destination origin and destination charm_url from the resolved charm - if resolved_charm.error is not None: - err = resolved_charm.error - raise JujuError(f"{err.code} : {err.message}") - dest_origin = resolved_charm.charm_origin - charm_url = resolved_charm.url - - # Add the charm with the destination url and origin - charm_origin_result = await charms_facade.AddCharm( - url=charm_url, force=force, charm_origin=dest_origin - ) - if charm_origin_result.error is not None: - err = charm_origin_result.error - raise JujuError(f"{err.code} : {err.message}") - - # Now take care of the resources: - - # user supplied resources to be used in refresh, - # will override the default values if there's any - arg_resources = resources or {} - - # need to process the given resources, as they can be - # paths or revisions - _arg_res_filenames = {} - _arg_res_revisions = {} - for res, filename_or_rev in arg_resources.items(): - if isinstance(filename_or_rev, int): - _arg_res_revisions[res] = filename_or_rev - else: - _arg_res_filenames[res] = filename_or_rev - - # Get the existing resources from the ResourcesFacade - request_data = [client.Entity(self.tag)] - resources_facade = client.ResourcesFacade.from_connection(self.connection) - response = await resources_facade.ListResources(entities=request_data) - existing_resources = { - resource.name: resource for resource in response.results[0].resources - } - - charmhub = self.model.charmhub - charm_resources = await charmhub.list_resources(charm_name) - - # Compute the difference btw resources needed and the existing resources - resources_to_update = [ - resource - for resource in charm_resources - if utils.should_upgrade_resource( - resource, existing_resources, arg_resources - ) - ] - - # Update the resources - if resources_to_update: - request_data = [] - for resource in resources_to_update: - res_name = resource.get("Name", resource.get("name")) - request_data.append( - client.CharmResource( - description=resource.get( - "Description", resource.get("description") - ), - name=res_name, - path=_arg_res_filenames.get( - res_name, resource.get("Path", resource.get("filename", "")) - ), - revision=_arg_res_revisions.get(res_name, -1), - type_=resource.get("Type", resource.get("type")), - origin="store", - ) - ) - - response = await resources_facade.AddPendingResources( - application_tag=self.tag, - charm_url=charm_url, - resources=request_data, - charm_origin=dest_origin, - ) - pending_ids = response.pending_ids - resource_ids = { - resource.get("Name", resource.get("name")): id_ - for resource, id_ in zip(resources_to_update, pending_ids) - } - else: - resource_ids = None - - set_charm_args = { - "application": self.entity_id, - "charm_url": charm_url, - "charm_origin": dest_origin, - "config_settings": None, - "config_settings_yaml": None, - "force": force, - "force_units": force_units, - "resource_ids": resource_ids, - "storage_constraints": None, - } - if self.connection.is_using_old_client: - set_charm_args["force_series"] = force_series - - # Update the application - await app_facade.SetCharm(**set_charm_args) - - await self.model.block_until(lambda: self.data["charm-url"] == charm_url) - - upgrade_charm = refresh - - async def local_refresh( - self, - *, - charm_origin: _definitions.CharmOrigin, - force: bool, - force_series: bool, - force_units: bool, - path: Union[Path, str], - resources: Optional[Dict[str, str]], - ): - """Refresh the charm for this application with a local charm. - - :param dict charm_origin: The charm origin of the destination charm - we're refreshing to - :param bool force: Refresh even if validation checks fail - :param bool force_series: Refresh even if series of deployed - application is not supported by the new charm - :param bool force_units: Refresh all units immediately, even if in - error state - :param Path|str path: Refresh to a charm located at path - :param dict resources: Dictionary of resource name/filepath pairs - - """ - app_facade = self._facade() - - if isinstance(path, str) and path.startswith("local:"): - path = path[6:] - local_path = Path(path) - charm_dir = local_path.expanduser().resolve() - model_config = await self.get_config() - - series = await self.get_series() or self.model.info.get("default-series", "") - if not series: - metadata = utils.get_local_charm_metadata(charm_dir) - await get_charm_series(metadata, self.model) - - if not series: - default_series = model_config.get("default-series") - if default_series: - series = default_series.value - charm_url = await self.model.add_local_charm_dir(charm_dir, series) - metadata = utils.get_local_charm_metadata(local_path) - if resources is not None: - resources = await self.model.add_local_resources( - self.entity_id, charm_url, metadata, resources=resources - ) - - # We know this charm is a local charm, but this charm origin could be - # the charm origin of a charmhub charm. Ensure that we update/remove - # the appropriate fields. - charm_origin.source = "local" - charm_origin.track = None - charm_origin.risk = None - charm_origin.branch = None - charm_origin.hash_ = None - charm_origin.id_ = None - charm_origin.revision = URL.parse(charm_url).revision - - set_charm_args = { - "application": self.entity_id, - "charm_origin": charm_origin, - "charm_url": charm_url, - "config_settings": None, - "config_settings_yaml": None, - "force": force, - "force_units": force_units, - "resource_ids": resources, - "storage_constraints": None, - } - - if self.connection.is_using_old_client: - set_charm_args["force_series"] = force_series - - # Update application - await app_facade.SetCharm(**set_charm_args) - - await self.model.block_until(lambda: self.data["charm-url"] == charm_url) - - async def get_metrics(self): - """Get metrics for this application's units. - - :return: Dictionary of unit_name:metrics - - """ - return await self.model.get_metrics(self.tag) - - -def _refresh_origin( - current_origin: client.CharmOrigin, - channel: Optional[str] = None, - revision: Optional[int] = None, -) -> client.CharmOrigin: - chan = None if channel is None else Channel.parse(channel).normalize() - - return client.CharmOrigin( - source=current_origin.source, - track=chan.track if chan else current_origin.track, - risk=chan.risk if chan else current_origin.risk, - revision=revision if revision is not None else current_origin.revision, - base=current_origin.base, - architecture=current_origin.get("architecture", DEFAULT_ARCHITECTURE), - ) - - -class ExposedEndpoint: - """ExposedEndpoint stores the list of CIDRs and space names which should be - allowed access to the port ranges that the application has opened for a - particular endpoint. Both lists are optional; if empty, the opened port - ranges will be reachable from any source IP address. - """ - - def __init__(self, to_spaces=None, to_cidrs=None): - if to_spaces is not None and not isinstance(to_spaces, list): - raise ValueError("to_spaces must be a list of space names or None") - if to_cidrs is not None and not isinstance(to_cidrs, list): - raise ValueError("to_cidrs must be a list of CIDRs or None") - - self.to_cidrs = to_cidrs - self.to_spaces = to_spaces - - def includes_spaces(self): - return self.to_spaces is not None and len(self.to_spaces) > 0 - - def includes_non_wildcard_cidrs(self): - to_cidrs = self.to_cidrs or [] - non_wildcard_cidrs = filter(lambda x: x == "0.0.0.0/0" or x == "::/0", to_cidrs) - return len(list(non_wildcard_cidrs)) > 0 - - @classmethod - def from_dict(cls, data): - d = data or {} - if not isinstance(d, dict): - raise ValueError( - "expected a dictionary with fields: expose-to-spaces and expose-to-cidrs" - ) - - to_spaces = None - if "expose-to-spaces" in d and isinstance(d["expose-to-spaces"], list): - to_spaces = d["expose-to-spaces"] - to_cidrs = None - if "expose-to-cidrs" in d and isinstance(d["expose-to-cidrs"], list): - to_cidrs = d["expose-to-cidrs"] - - return cls(to_spaces=to_spaces, to_cidrs=to_cidrs) - - def to_dict(self): - d = {} - if self.to_cidrs is not None: - d["expose-to-cidrs"] = self.to_cidrs - if self.to_spaces is not None: - d["expose-to-spaces"] = self.to_spaces - return d - - def __str__(self): - descr = "" - if self.to_spaces is not None and len(self.to_spaces) > 0: - if len(self.to_spaces) == 1: - descr = f"from space {self.to_spaces[0]}" - elif len(self.to_spaces) > 1: - descr = "from spaces {}".format(",".join(self.to_spaces)) - - if self.to_cidrs is not None and len(self.to_cidrs) > 0: - descr = descr + " and " - - if self.to_cidrs is not None: - if len(self.to_cidrs) == 1: - descr = descr + f"from CIDR {self.to_cidrs[0]}" - elif len(self.to_cidrs) > 1: - descr = descr + "from CIDRs {}".format(",".join(self.to_cidrs)) - - return descr diff --git a/build/lib/juju/bundle.py b/build/lib/juju/bundle.py deleted file mode 100644 index ad6600b3..00000000 --- a/build/lib/juju/bundle.py +++ /dev/null @@ -1,1197 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -import base64 -import io -import logging -import os -import zipfile -from contextlib import closing -from pathlib import Path -from typing import TYPE_CHECKING, Mapping, cast - -import requests -import yaml -from toposort import toposort_flatten - -from . import jasyncio, utils -from .client import client -from .constraints import parse as parse_constraints -from .errors import JujuError -from .origin import Channel, Source -from .url import URL, Schema -from .utils import get_base_from_origin_or_channel - -if TYPE_CHECKING: - from .constraints import StorageConstraintDict - from .model import Model - -log = logging.getLogger(__name__) - - -class BundleHandler: - """Handle bundles by using the API to translate bundle YAML into a plan of - steps and then dispatching each of those using the API. - """ - - def __init__(self, model, trusted=False, forced=False): - self.model = model - self.trusted = trusted - self.forced = forced - self.bundle = None - self.overlays = [] - self.overlay_removed_charms = set() - - self.plan = [] - self.references = {} - self._units_by_app = {} - self.origins = {} - - for unit_name, unit in model.units.items(): - app_units = self._units_by_app.setdefault(unit.application, []) - app_units.append(unit_name) - - self.bundle_facade = client.BundleFacade.from_connection(model.connection()) - self.client_facade = client.ClientFacade.from_connection(model.connection()) - self.app_facade = client.ApplicationFacade.from_connection(model.connection()) - self.ann_facade = client.AnnotationsFacade.from_connection(model.connection()) - self.machine_manager_facade = client.MachineManagerFacade.from_connection( - model.connection() - ) - - # Feature detect if we have the new charms facade, otherwise fallback - # to the client facade, when making calls. - best_facade_version = client.CharmsFacade.best_facade_version( - model.connection() - ) - if best_facade_version is not None and best_facade_version > 2: - self.charms_facade = client.CharmsFacade.from_connection(model.connection()) - else: - self.charms_facade = None - - # This describes all the change types that the BundleHandler supports. - change_type_cls = [ - AddApplicationChange, - AddCharmChange, - AddMachineChange, - AddRelationChange, - AddUnitChange, - CreateOfferChange, - ConsumeOfferChange, - ExposeChange, - ScaleChange, - SetAnnotationsChange, - ] - self.change_types = {} - for change_cls in change_type_cls: - self.change_types[change_cls.method()] = change_cls - - async def _validate_bundle(self, bundle): - """Validate the bundle for known issues, raises an error if it - encounters a known problem - """ - apps_dict = bundle.get("applications", {}) - for app_name in self.applications: - app_dict = apps_dict[app_name] - app_trusted = app_dict.get("trust") - if (not self.trusted and not self.forced) and app_trusted: - raise JujuError( - "Bundle cannot be deployed without trusting applications with your cloud credentials.\n" - "Please repeat the deploy command with the --trust argument if you consent to trust the following application\n" - f" - {app_name}\n" - ) - return bundle - - async def _handle_local_charms(self, bundle, bundle_dir): - """Search for references to local charms (i.e. filesystem paths) - in the bundle. Upload the local charms to the model, and replace - the filesystem paths with appropriate 'local:' paths in the bundle. - - Return the modified bundle. - - :param dict bundle: Bundle dictionary - :return: Modified bundle dictionary - - """ - apps, args = [], [] - - default_series = bundle.get("series") - apps_dict = bundle.get("applications", {}) - for app_name in self.applications: - app_dict = apps_dict[app_name] - charm_dir = app_dict["charm"] - try: - charm_path = (bundle_dir / charm_dir).resolve() - if not ( - charm_path.is_dir() - or ( - charm_path.is_file() and charm_path.suffix in (".charm", ".zip") - ) - ): - continue - charm_dir = str(charm_path) - except ValueError: - pass - except FileNotFoundError: - continue - series = app_dict.get("series") or default_series - if not series: - metadata = utils.get_local_charm_metadata(charm_dir) - series = await get_charm_series(metadata, self.model) - if not series: - base = utils.get_local_charm_base(None, charm_path, client.Base) - series = utils.base_channel_to_series(base.channel) - if not series: - raise JujuError( - f"Couldn't determine series for charm at {charm_dir}. " - "Add a 'series' key to the bundle." - ) - # Keep track of what we need to update. We keep a list of apps - # that need to be updated, and a corresponding list of args - # needed to update those apps. - apps.append(app_name) - args.append((charm_dir, series)) - - if apps: - # If we have apps to update, spawn all the coroutines concurrently - # and wait for them to finish. - charm_urls = await jasyncio.gather(*[ - self.model.add_local_charm_dir(*params) for params in args - ]) - - # Update the 'charm:' entry for each app with the new 'local:' url. - for app_name, charm_url, (charm_dir, series) in zip(apps, charm_urls, args): - metadata = utils.get_local_charm_metadata(charm_dir) - resources = await self.model.add_local_resources( - app_name, - charm_url, - metadata, - resources=bundle.get("applications", {app_name: {}})[app_name].get( - "resources", {} - ), - ) - apps_dict[app_name]["charm"] = charm_url - apps_dict[app_name]["resources"] = resources - origin = client.CharmOrigin(source="local", risk="stable") - if not self.model.connection().is_using_old_client: - origin.base = utils.get_local_charm_base( - series, charm_dir, client.Base - ) - self.origins[charm_url] = {str(None): origin} - - return bundle - - def _resolve_include_file_config(self, bundle_dir): - """If any of the applications (including the ones in the overlays) - have "config: include-file:..." or "config: - include-base64:...", then we have to resolve and inline them - into the bundle here because they're all files with local - relative paths, so backend can't handle them. - - """ - bundle_apps = [self.bundle.get("applications", {})] - overlay_apps = [overlay.get("applications", {}) for overlay in self.overlays] - - for apps in bundle_apps + overlay_apps: - for app_name, app in apps.items(): - if app and "options" in app: - if "config" in app["options"] and app["options"][ - "config" - ].startswith("include-file"): - # resolve the file - if not bundle_dir: - raise NotImplementedError( - "unable to resolve paths for config:include-file for non-local charms" - ) - try: - config_path = ( - bundle_dir - / Path(app["options"]["config"].split("//")[1]) - ).resolve() - except IndexError: - raise JujuError( - "the path for the included file should start with // and be relative to the bundle" - ) - if not config_path.exists(): - raise JujuError( - "unable to locate config file : %s for : %s" - % (config_path, app_name) - ) - - # get the contents of the file - config_contents = yaml.safe_load(config_path.read_text()) - - # inline the configurations for the current app into - # the app['options'] - for key, val in config_contents[app_name].items(): - app["options"][key] = val - - # remove the 'include-file' config - app["options"].pop("config") - - for option_key, option_val in app["options"].items(): - if isinstance(option_val, str) and option_val.startswith( - "include-base64" - ): - # resolve the file - if not bundle_dir: - raise NotImplementedError( - "unable to resolve paths for config:include-base64 for non-local charms" - ) - try: - base64_path = ( - bundle_dir / Path(option_val.split("//")[1]) - ).resolve() - except IndexError: - raise JujuError( - "the path for the included base64 file should start with // and be relative to the bundle" - ) - - if not base64_path.exists(): - raise JujuError( - "unable to locate the base64 file : %s for : %s" - % (base64_path, app_name) - ) - - # inline the base64 encoded config value - base64_contents = base64.b64decode(base64_path.read_text()) - app["options"][option_key] = base64_contents - - return self.bundle, self.overlays - - async def fetch_plan(self, bundle, origin, overlays=[]): - """fetch_plan is called by the model.deploy(). It gathers the information about the - bundle to be deployed (whether local or CharmHub), straightens it up, applies overlays - if any overlays are given. Validates the bundle against known issues. Resolves and adds - local charms if there's any in the bundle. Resolves and adds --include-file configs if - there's any. Finally it calls the BundleFacade.GetChanges() to get the plan for the - bundle to be handed to the execute_plan() by the model.deploy(). Note that it doesn't - return the plan, just saves it in the self (BundleHandler) to be used later. - - :param client.URL bundle_url: the url of the bundle to be deployed - :param client.CharmOrigin origin: the origin of the bundle to be deployed - :param [string] overlays: paths for the yaml files containing overlays to be applied to - the bundle during deployment - - :returns: None - """ - bundle_dir = None - - if is_local_bundle(str(bundle)): - path = str(bundle) - if path.startswith("local:"): - path = path[6:] - bundle_yaml, bundle_dir = read_local_bundle(path) - - else: - if client.CharmsFacade.best_facade_version(self.model.connection()) < 3: - url = URL.parse(bundle, default_store=Schema.CHARM_STORE) - else: - url = URL.parse(bundle) - path = url.path() - bundle_yaml = await self._download_bundle(bundle, origin) - - if not bundle_yaml: - raise JujuError("empty bundle, nothing to deploy") - - _bundles = [b for b in yaml.safe_load_all(bundle_yaml)] - self.overlays = _bundles[1:] - self.bundle = _bundles[0] - - if overlays != []: - for overlay_yaml_path in overlays: - try: - overlay_contents = Path(overlay_yaml_path).read_text() - except OSError as e: - raise JujuError( - "unable to open overlay %s \n %s" % (overlay_yaml_path, e) - ) - self.overlays.extend(yaml.safe_load_all(overlay_contents)) - - # gather the names of the removed charms so model.deploy - # wouldn't wait for them to appear in the model - for overlay in self.overlays: - overlay_apps = overlay.get("applications", {}) - for charm_name, val in overlay_apps.items(): - if val is None: - self.overlay_removed_charms.add(charm_name) - - self.bundle = await self._validate_bundle(self.bundle) - - if is_local_bundle(path): - self.bundle = await self._handle_local_charms(self.bundle, bundle_dir) - - self.bundle, self.overlays = self._resolve_include_file_config(bundle_dir) - - _yaml_data = [yaml.dump(self.bundle)] - for overlay in self.overlays: - _yaml_data.append(yaml.dump(overlay).replace("null", "")) # noqa: PERF401 - yaml_data = "---\n".join(_yaml_data) - - self.plan = await self.bundle_facade.GetChangesMapArgs( - bundleurl=path, yaml=yaml_data - ) - - if self.plan.errors and any(self.plan.errors): - raise JujuError(self.plan.errors) - - async def _download_bundle(self, charm_url, origin): - if self.charms_facade is None: - raise JujuError( - f"unable to download bundle for {charm_url} using the new charms facade. Upgrade controller to proceed." - ) - - id_ = origin.id_ if origin.id_ else "" - hash_ = origin.hash_ if origin.hash_ else "" - charm_origin = { - "source": origin.source, - "type": origin.type_, - "id": id_, - "hash": hash_, - "revision": origin.revision, - "risk": origin.risk, - "track": origin.track, - "architecture": origin.architecture, - } - if self.model.connection().is_using_old_client: - charm_origin["os"] = origin.os - charm_origin["series"] = origin.series - else: - charm_origin["base"] = origin.base - - resp = await self.charms_facade.GetDownloadInfos( - entities=[{"charm-url": str(charm_url), "charm-origin": charm_origin}] - ) - if len(resp.results) != 1: - raise JujuError(f"expected one result, received {resp.results}") - - result = resp.results[0] - if not result.url: - raise JujuError(f"no url found for bundle {charm_url.name}") - - bundle_resp = requests.get(result.url) # noqa: S113 - bundle_resp.raise_for_status() - - with closing(bundle_resp), zipfile.ZipFile( - io.BytesIO(bundle_resp.content) - ) as archive: - return self._get_bundle_yaml(archive) - - def _get_bundle_yaml(self, archive): - for member in archive.infolist(): - if member.filename == "bundle.yaml": - return archive.read(member) - raise JujuError("bundle.yaml not found") - - async def _resolve_charms(self): - deployed = dict() - - specs = self.applications_specs - - for name in self.applications: - spec = specs[name] - app = self.model.applications.get(name, None) - - cons = None - if app is not None: - deployed[name] = name - - if is_local_charm(spec["charm"]): - spec["charm"] = self.model.applications[name] - continue - if spec["charm"] == app.charm_url: - continue - - cons = await app.get_constraints() - - if is_local_charm(spec["charm"]): - continue - - charm_url = URL.parse(spec["charm"]) - - channel = ( - Channel.parse(spec["channel"]) - if "channel" in spec - else Channel("latest", "stable") - ) - track, risk = channel.track, channel.risk - series = spec.get("series", self.bundle.get("series", None)) - base = get_base_from_origin_or_channel(channel, series) - - if self.charms_facade is not None: - if cons is not None and cons["arch"] != "": - architecture = cons["arch"] - else: - architecture = await self.model._resolve_architecture(charm_url) - - origin = client.CharmOrigin( - source=Source.CHARM_HUB.value, - architecture=architecture, - risk=risk, - track=track, - base=base, - ) - charm_url, charm_origin = await self.model._resolve_charm( - charm_url, origin - ) - spec["charm"] = str(charm_url) - else: - charm_origin = client.CharmOrigin( - source=Source.CHARM_HUB.value, - risk=risk, - track=track, - base=base, - ) - - if str(channel) not in self.origins: - self.origins[str(charm_url)] = {} - self.origins[str(charm_url)][str(channel)] = charm_origin - - async def execute_plan(self): - await self._resolve_charms() - - changes = ChangeSet(self.plan.changes) - for step in changes.sorted(): - change_cls = self.change_types.get(step.method) - if change_cls is None: - raise NotImplementedError(f"unknown change type: {step.method}") - change = change_cls(step.id_, step.requires, step.args) - log.info(f"Applying change: {change}") - self.references[step.id_] = await change.run(self) - - @property - def applications(self): - apps_dict = self.bundle.get("applications", {}) - return set(apps_dict.keys()) - self.overlay_removed_charms - - @property - def applications_specs(self): - return self.bundle.get("applications", {}) - - def resolve_relation(self, reference): - parts = reference.split(":", maxsplit=1) - application = self.resolve(parts[0]) - if len(parts) == 1: - return application - return f"{application}:{parts[1]}" - - def resolve(self, reference): - if reference and reference.startswith("$"): - ref = self.references[reference[1:]] - if ref is not None: - reference = ref - return reference - - -def is_local_charm(charm_url: str): - return ( - charm_url.startswith(".") - or charm_url.startswith("local:") - or os.path.isabs(charm_url) - ) - - -is_local_bundle = is_local_charm - - -def read_local_bundle(path): - path = Path(path) - if os.path.isfile(path): - bundle_yaml = path.read_text() - bundle_dir = path.parent - elif os.path.isdir(path): - bundle_yaml = (path / "bundle.yaml").read_text() - bundle_dir = path - - return (bundle_yaml, bundle_dir) - - -async def get_charm_series(metadata, model): - """Inspects the given metadata and returns a default series from its - metadata.yaml (the first item in the 'series' list). - - Tries to extract the information from the given model if no - series is determined from the given metadata. - Returns None if no series can be determined. - - """ - _series = metadata.get("series") - series = _series[0] if _series else None - - if series is None: - # get the ConfigValue for the 'default-series' from the model - model_config = await model.get_config() - _default_series = model_config.get("default-series") - - if _default_series is not None: - # then update the series with its value - series = _default_series.value - - return series - - -class ChangeSet: - def __init__(self, changes): - self.changes = changes - - # sorted does a topological sort of the changes - def sorted(self): - if len(self.changes) == 0: - return [] - - changes = {} - for change in self.changes: - changes[change.id_] = set(change.requires) - sorted_changes = toposort_flatten(changes) - results = [] - for change_id in sorted_changes: - for change in self.changes: - if change_id == change.id_: - results.append(change) - break - return results - - -class ChangeInfo: - _toPy = {} - - def __init__(self, change_id, requires, params=None): - self.change_id = change_id - self.requires = requires - - type(self).from_dict(self, params) - - @classmethod - def from_dict(cls, self, data): - """from_dict converts a data bag into fields on a class instance. - If a value is missing from the data, then None is assigned to the field - instance value. - """ - d = data or {} - for k, v in cls._toPy.items(): - if k in d: - setattr(self, v, d[k]) - else: - setattr(self, v, None) - - -class AddApplicationChange(ChangeInfo): - _toPy = { - "charm": "charm", - "series": "series", - "application": "application", - "options": "options", - "constraints": "constraints", - "storage": "storage", - "devices": "devices", - "endpoint-bindings": "endpoint_bindings", - "resources": "resources", - "num-units": "num_units", - "channel": "channel", - } - - """AddApplicationChange holds a change for deploying a Juju application. - - :charm: URL of the charm to be used to deploy this application. - :series: series of the application to be deployed if the charm - default is not sufficient. - :application: application name. - :num_units: number of units required. For IAAS models, this will - be 0 and separate AddUnitChanges will be used. For Kubernetes models, - this will be used to scale the application. - :options: holds application options. - :constraints: optional application constraints. - :storage: optional storage constraints, in the form of `{label: constraint}`. - The label is a string specified by the charm, while the constraint is - either a constraints.StorageConstraintDict, or a string following - `the juju storage constraint directive format `_, - specifying the storage pool, number of volumes, and size of each volume. - :devices: optional devices constraints. - :endpoint_bindings: optional endpoint bindings - :resources: identifies the revision to use for each resource of the - application's charm. - :local_resources: identifies the path to the local resource of the - application's charm. - """ - - storage: Mapping[str, str | StorageConstraintDict] | None = None - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "deploy" - - async def run(self, context): - """Executes a AddApplicationChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - # NB: this should really be handled by the controller when generating the - # bundle change plan, and this short-term workaround may be missing some - # aspects of the logic which the CLI client contains to handle edge cases. - model = cast("Model", context.model) # pyright: ignore[reportUnknownMemberType] - if self.application in model.applications: - log.debug("Skipping %s; already in model", self.application) - return self.application - - # resolve indirect references - charm = context.resolve(self.charm) - options = {} - if self.options is not None: - options = self.options - if context.trusted: - if model.info.agent_version < client.Number.from_json("2.4.0"): - raise NotImplementedError( - f"trusted is not supported on model version {model.info.agent_version}" - ) - options["trust"] = "true" - - url = URL.parse(str(charm)) - - # set the channel to the default value if not specified - if not self.channel: - if Schema.CHARM_HUB.matches(url.schema): - self.channel = "latest/stable" - else: # for local charms - self.channel = "" - - channel = None - non_normalized_channel = None - if self.channel is not None and self.channel != "": - non_normalized_channel = Channel.parse(self.channel) - channel = non_normalized_channel.normalize() - - origin = context.origins.get(str(url), {}).get( - str(channel), - context.origins.get(str(url), {}).get(str(non_normalized_channel), None), - ) - if origin is None: - raise JujuError( - f"expected origin to be valid for application {self.application} and charm {url!s} with channel {channel!s}" - ) - - if not self.series: - self.series = context.bundle.get("series", None) - - resources = ( - context.bundle.get("applications", {}) - .get(self.application, {}) - .get("resources", {}) - ) - if Schema.CHARM_HUB.matches(url.schema): - resources = await model._add_charmhub_resources( - self.application, charm, origin, overrides=self.resources - ) - - await model._deploy( - charm_url=charm, - application=self.application, - series=self.series, - config=options, - constraints=self.constraints, - endpoint_bindings=self.endpoint_bindings, - resources=resources, - storage=self.storage, - channel=self.channel, - devices=self.devices, - num_units=self.num_units, - charm_origin=origin, - ) - return self.application - - def __str__(self): - series = "" - if self.series is not None and self.series != "": - series = f" on {self.series}" - units_info = "" - if self.num_units is not None: - plural = "" - if self.num_units > 1: - plural = "s" - units_info = f" with {self.num_units} unit{plural}" - return f"deploy application {self.application}{units_info}{series} using {self.charm}" - - -class AddCharmChange(ChangeInfo): - _toPy = { - "charm": "charm", - "series": "series", - "channel": "channel", - "revision": "revision", - "architecture": "architecture", - } - - """AddCharmChange holds a change for adding a charm to the environment. - - :change_id: id of the change that will be used to identify the current - change - :requires: is a slice of dependencies that are required to happen. - :params: holds the change parameters from the api response. Currently the - params could either be a list or a dict. The later being the newer - return results. - - Params holds the following values: - :charm: URL of the charm to be added. - :series: series of the charm to be added if the charm default is - not sufficient. - :channel: preferred channel for obtaining the charm. - :revision: specified revision of the charm to be added if specified. - """ - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "addCharm" - - async def run(self, context): - """Executes a AddCharmChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - # We don't add local charms because they've already been added - # by self._handle_local_charms - if is_local_charm(str(self.charm)): - return self.charm - - url = URL.parse(str(self.charm)) - ch = None - identifier = None - if Schema.CHARM_HUB.matches(url.schema): - ch = Channel("latest", "stable") - if self.channel: - ch = Channel.parse(self.channel).normalize() - arch = self.architecture - if not arch: - arch = await context.model._resolve_architecture(url) - base = get_base_from_origin_or_channel(ch, self.series) - origin = client.CharmOrigin( - source=Source.CHARM_HUB.value, - architecture=arch, - risk=ch.risk, - track=ch.track, - revision=self.revision, - base=base, - ) - identifier, origin = await context.model._resolve_charm(url, origin) - - if identifier is None: - raise JujuError(f"unknown charm {self.charm}") - - await context.model._add_charm(str(identifier), origin) - - if str(ch) not in context.origins: - context.origins[str(identifier)] = {} - context.origins[str(identifier)][str(ch)] = origin - - return str(identifier) if identifier is not None else url.path() - - def __str__(self): - series = "" - channel = "" - if self.series is not None and self.series != "": - series = f" for series {self.series}" - if self.channel is not None: - channel = f" from channel {self.channel}" - return f"upload charm {self.charm}{series}{channel}" - - -class AddMachineChange(ChangeInfo): - _toPy = { - "series": "series", - "constraints": "constraints", - "container-type": "container_type", - "parent-id": "parent_id", - } - - """AddMachineChange holds a change for adding a machine or container. - - :change_id: id of the change that will be used to identify the current - change - :requires: is a slice of dependencies that are required to happen. - :params: holds the change parameters from the api response. Currently the - params could either be a list or a dict. The later being the newer - return results. - - Params holds the following values: - :series: optional machine OS series. - :constraints: optional machine constraints. - :container_type: optionally type of the container (for instance - "lxc" or kvm"). It is not specified for top level machines. - :parent_id: id of the parent machine. - """ - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "addMachines" - - async def run(self, context): - """Executes a AddMachineChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - # Fix up values, as necessary. - params = {} - if self.parent_id is not None: - if self.parent_id.startswith("$addUnit"): - unit = context.resolve(self.parent_id)[0] - params["parent_id"] = unit.machine.entity_id - else: - params["parent_id"] = context.resolve(self.parent_id) - - params["constraints"] = parse_constraints(self.constraints) - params["jobs"] = params.get("jobs", ["JobHostUnits"]) - if not context.model.connection().is_using_old_client: - params["base"] = client.Base( - channel=utils.get_series_version(self.series), name="ubuntu" - ) - else: - params["series"] = self.series - - if self.container_type == "lxc": - log.warning( - "Juju 2.0 does not support lxc containers. " - "Converting containers to lxd." - ) - params["container_type"] = "lxd" - else: - params["container_type"] = self.container_type - - # Submit the request. - params = client.AddMachineParams(**params) - results = await context.machine_manager_facade.AddMachines(params=[params]) - error = results.machines[0].error - if error: - raise ValueError("Error adding machine: %s" % error.message) - machine = results.machines[0].machine - log.debug("Added new machine %s", machine) - return machine - - def __str__(self): - machine = "new machine" - if self.container_type is not None and self.container_type != "": - machine = f"{self.container_type} container on {machine}" - return f"add {machine}" - - -class AddRelationChange(ChangeInfo): - _toPy = {"endpoint1": "endpoint1", "endpoint2": "endpoint2"} - """AddRelationChange holds a change for adding a relation between two - applications. - - :change_id: id of the change that will be used to identify the current - change - :requires: is a slice of dependencies that are required to happen. - :params: holds the change parameters from the api response. Currently the - params could either be a list or a dict. The later being the newer - return results. - - Params holds the following values: - Endpoint1 and Endpoint2 hold relation endpoints in the - "application:interface" form, where the application is either a - placeholder pointing to an application change or in the case of a model - that already has this application deployed, the name of the - application, and the interface is optional. Examples are - "$deploy-42:web", "$deploy-42", "mysql:db". - """ - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "addRelation" - - async def run(self, context): - """Executes a AddRelationChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - ep1 = context.resolve_relation(self.endpoint1) - ep2 = context.resolve_relation(self.endpoint2) - - # NB: this should really be handled by the controller when generating the - # bundle change plan, and this short-term workaround may be missing some - # aspects of the logic which the CLI client contains to handle edge cases. - existing = [rel for rel in context.model.relations if rel.matches(ep1, ep2)] - if existing: - log.info("Skipping %s <-> %s; already related", ep1, ep2) - return existing[0] - - log.info("Relating %s <-> %s", ep1, ep2) - return await context.model.relate(ep1, ep2) - - def __str__(self): - return f"add relation {self.endpoint1} - {self.endpoint2}" - - -class AddUnitChange(ChangeInfo): - _toPy = {"application": "application", "to": "to"} - """AddUnitChange holds a change for adding an application unit. - - :change_id: id of the change that will be used to identify the current - change - :requires: is a slice of dependencies that are required to happen. - :params: holds the change parameters from the api response. Currently the - params could either be a list or a dict. The later being the newer - return results. - - Params holds the following values: - :application: application placeholder name for which a unit is - added. - :to: optional location where to add the unit, as a placeholder - pointing to another unit change or to a machine change. - """ - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "addUnit" - - async def run(self, context): - """Executes a AddUnitChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - application = context.resolve(self.application) - placement = context.resolve(self.to) - if context._units_by_app.get(application): - # enough units for this application already exist; - # claim one, and carry on - # NB: this should probably honor placement, but the juju client - # doesn't, so we're not bothering, either - unit_name = context._units_by_app[application].pop() - log.debug("Reusing unit %s for %s", unit_name, application) - return context.model.units[unit_name] - - log.debug( - "Adding new unit for %s%s", - application, - " to %s" % placement if placement else "", - ) - return await context.model.applications[application].add_unit( - count=1, - to=placement, - ) - - def __str__(self): - return f"add {self.application} unit to {self.to}" - - -class CreateOfferChange(ChangeInfo): - _toPy = { - "application": "application", - "endpoints": "endpoints", - "offer-name": "offer_name", - } - """CreateOfferChange holds a change for creating a new application endpoint - offer. - - :change_id: id of the change that will be used to identify the current - change - :requires: is a slice of dependencies that are required to happen. - :params: holds the change parameters from the api response. Currently the - params could either be a list or a dict. The later being the newer - return results. - - Params holds the following values: - :application: is the name of the application to create an offer for. - added. - :endpoint: is a list of application endpoint to expose as part of an - offer. - :offer_name: describes the offer name. - """ - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "createOffer" - - async def run(self, context): - """Executes a CreateOfferChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - application = context.resolve(self.application) - for ep in self.endpoints: - await context.model.create_offer( - ep, offer_name=self.offer_name, application_name=application - ) - - def __str__(self): - endpoints = "" - if self.endpoints is not None: - endpoints = ":{}".format(",".join(self.endpoints)) - return f"create offer {self.offer_name} using {self.application}{endpoints}" - - -class ConsumeOfferChange(ChangeInfo): - _toPy = {"url": "url", "application-name": "application_name"} - """CreateOfferChange holds a change for consuming a offer. - - :change_id: id of the change that will be used to identify the current - change - :requires: is a slice of dependencies that are required to happen. - :params: holds the change parameters from the api response. Currently the - params could either be a list or a dict. The later being the newer - return results. - - Params holds the following values: - :url: contains the location of the offer - :application_name: describes the application name on offer. - """ - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "consumeOffer" - - async def run(self, context): - """Executes a ConsumeOfferChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - application = context.resolve(self.application_name) - local_name = await context.model.consume( - self.url, application_alias=application - ) - return local_name - - def __str__(self): - return f"consume offer {self.application_name} at {self.url}" - - -class ExposeChange(ChangeInfo): - _toPy = { - "application": "application", - "exposed-endpoints": "exposed_endpoints", - } - """ExposeChange holds a change for exposing an application. - - :change_id: id of the change that will be used to identify the current - change - :requires: is a slice of dependencies that are required to happen. - :params: holds the change parameters from the api response. Currently the - params could either be a list or a dict. The later being the newer - return results. - - Params holds the following values: - :application: placeholder name of the application that must be - exposed. - :exposed_endpoints: a an optional dictionary where keys are endpoint - names and values are dicts that specify the space names and CIDRs - that should be able to access the port ranges that the application - has opened for each endpoint. - """ - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "expose" - - async def run(self, context): - """Executes a ExposeChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - application = context.resolve(self.application) - log.info("Exposing %s", application) - return await context.model.applications[application].expose( - self.exposed_endpoints - ) - - def __str__(self): - return f"expose {self.application}" - - -class ScaleChange(ChangeInfo): - _toPy = {"application": "application", "scale": "scale"} - """ - ScaleChange holds a change for scaling an application. - - :change_id: id of the change that will be used to identify the current - change - :requires: is a slice of dependencies that are required to happen. - :params: holds the change parameters from the api response. Currently the - params could either be a list or a dict. The later being the newer - return results. - - Params holds the following values: - :application: placeholder name of the application to be scaled. - :scale: is the new scale value to use. - """ - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "scale" - - async def run(self, context): - """Executes a ScaleChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - application = context.resolve(self.application) - return await context.model.applications[application].scale(scale=self.scale) - - def __str__(self): - return f"scale {self.application} to {self.scale} units" - - -class SetAnnotationsChange(ChangeInfo): - _toPy = {"id": "id", "entity-type": "entity_type", "annotations": "annotations"} - """SetAnnotationsChange holds a change for setting application and machine - annotations. - - :change_id: id of the change that will be used to identify the current - change - :requires: is a slice of dependencies that are required to happen. - :params: holds the change parameters from the api response. Currently the - params could either be a list or a dict. The later being the newer - return results. - - Params holds the following values: - :id: is the placeholder for the application or machine change - corresponding to the entity to be annotated. - :entity_type: type of the entity, "application" or "machine". - :ennotations: annotations as key/value pairs. - """ - - @staticmethod - def method(): - """Method returns an associated ID for the Juju API call.""" - return "setAnnotations" - - async def run(self, context): - """Executes a SetAnnotationsChange using the returned parameters from - the API server. - - :param context: is used for any methods or properties required to - perform a change. - """ - entity_id = context.resolve(self.id) - try: - entity = context.model.state.get_entity(self.entity_type, entity_id) - except KeyError: - entity = await context.model._wait_for_new(self.entity_type, entity_id) - return await entity.set_annotations(self.annotations) - - def __str__(self): - return f"set annotations for {self.id}" diff --git a/build/lib/juju/charm.py b/build/lib/juju/charm.py deleted file mode 100644 index 4d2fe2f3..00000000 --- a/build/lib/juju/charm.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import logging - -from . import model - -log = logging.getLogger(__name__) - - -class Charm(model.ModelEntity): - pass diff --git a/build/lib/juju/charmhub.py b/build/lib/juju/charmhub.py deleted file mode 100644 index 61cbfcca..00000000 --- a/build/lib/juju/charmhub.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import json - -import requests - -from juju import jasyncio - -from .client import client -from .errors import JujuError - - -class CharmHub: - def __init__(self, model): - self.model = model - - async def _charmhub_url(self): - model_conf = await self.model.get_config() - return model_conf["charmhub-url"] - - async def request_charmhub_with_retry(self, url, retries): - for _ in range(retries): - _response = requests.get(url) # noqa: S113 - if _response.status_code == 200: - return _response - await jasyncio.sleep(5) - raise JujuError(f"Got {_response.status_code} from {url}") - - async def get_charm_id(self, charm_name): - _conn, _headers, _path_prefix = self.model.connection().https_connection() - - charmhub_url = await self._charmhub_url() - url = f"{charmhub_url.value}/v2/charms/info/{charm_name}" - _response = await self.request_charmhub_with_retry(url, 5) - response = json.loads(_response.text) - return response["id"], response["name"] - - async def is_subordinate(self, charm_name): - _conn, _headers, _path_prefix = self.model.connection().https_connection() - - charmhub_url = await self._charmhub_url() - url = f"{charmhub_url.value}/v2/charms/info/{charm_name}?fields=default-release.revision.subordinate" - _response = await self.request_charmhub_with_retry(url, 5) - response = json.loads(_response.text) - rev_response = response["default-release"]["revision"] - return "subordinate" in rev_response and rev_response["subordinate"] - - # TODO (caner) : we should be able to recreate the channel-map through the - # api call without needing the CharmHub facade - - async def list_resources(self, charm_name): - _conn, _headers, __path_prefix = self.model.connection().https_connection() - - charmhub_url = await self._charmhub_url() - url = f"{charmhub_url.value}/v2/charms/info/{charm_name}?fields=default-release.resources" - _response = await self.request_charmhub_with_retry(url, 5) - response = json.loads(_response.text) - return response["default-release"]["resources"] - - async def info(self, name, channel=None): - """Info displays detailed information about a CharmHub charm. The charm - can be specified by the exact name. - - Channel is a hint for providing the metadata for a given channel. - Without the channel hint then only the default release will have the - metadata. - - """ - if not name: - raise JujuError("name expected") - - if self.model.connection().is_using_old_client: - if channel is None: - channel = "" - facade = self._facade() - res = await facade.Info(tag=f"application-{name}", channel=channel) - err_code = res.errors.error_list.code - if err_code: - raise JujuError( - f"charmhub.info - {err_code} : {res.errors.error_list.message}" - ) - result = res.result - result.channel_map = CharmHub._channel_map_to_dict( - result.channel_map, name, channel=channel - ) - result = result.serialize() - else: - charmhub_url = await self._charmhub_url() - url = f"{charmhub_url.value}/v2/charms/info/{name}?fields=channel-map" - try: - _response = await self.request_charmhub_with_retry(url, 5) - except JujuError as e: - if "404" in e.message: - raise JujuError(f"{name} not found") from e - result = json.loads(_response.text) - result["channel-map"] = CharmHub._channel_list_to_map( - result["channel-map"], name, channel=channel - ) - return result - - @staticmethod - def _channel_list_to_map(channel_list_map, name, channel=""): - """Charmhub API returns the channel map as a list of channel objects - (with risk, track, revision, download etc). This turns that into a map - that's keyed with the channel=track/risk for easy - filtering/processing. This representation is also closer to the - result of the 2.9 facade call. - - So basically, - [{'channel':{'risk':'stable', 'track':'latest'}, 'revision': 58}] - becomes: - {'latest/stable': {'channel':{'risk':'stable', 'track':'latest'}, - 'revision': 58}} - - :param channel_list_map: [map[str][any]] - :return: map[str][map[str][any]] - """ - channel_map = {} - for ch in channel_list_map: - ch_name = f"{ch['channel']['track']}/{ch['channel']['risk']}" - if channel and channel != ch_name: - # If channel is given, then filter out the rest - continue - channel_map[ch_name] = ch - if channel == ch_name: - # If we found the desired channel, no need to continue - break - # After loop is done, check for non-existent channel - if channel and channel not in channel_map: - raise JujuError(f"Charmhub.info : channel {channel} not found for {name}") - return channel_map - - @staticmethod - def _channel_map_to_dict(channel_map, name, channel=""): - """Converts the client.definitions.Channel objects into python maps - inside a channel map (for pylibjuju <3.0) - - :param channel_map: map[str][Channel] - :return: map[str][map[str][any]] - """ - channel_dict = {} - for ch_name, ch_obj in channel_map.items(): - # No need to worry about filtering channel - # Charmhub facade will take care of that - _ch = ch_obj.serialize() - _ch["platforms"] = [p.serialize() for p in _ch["platforms"]] - channel_dict[ch_name] = _ch - if channel and channel not in channel_dict: - raise JujuError(f"Charmhub.info : channel {channel} not found for {name}") - return channel_dict - - async def find( - self, - query, - category=None, - channel=None, - charm_type=None, - platforms=None, - publisher=None, - relation_requires=None, - relation_provides=None, - ): - """Find queries the CharmHub store for available charms or bundles.""" - if charm_type is not None and charm_type not in ["charm", "bundle"]: - raise JujuError("expected either charm or bundle for charm_type") - - facade = self._facade() - return await facade.Find( - query=query, - category=category, - channel=channel, - type_=charm_type, - platforms=platforms, - publisher=publisher, - relation_provides=relation_provides, - relation_requires=relation_requires, - ) - - def _facade(self): - return client.CharmHubFacade.from_connection(self.model.connection()) diff --git a/build/lib/juju/client/__init__.py b/build/lib/juju/client/__init__.py deleted file mode 100644 index 4b9e20c5..00000000 --- a/build/lib/juju/client/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright 2024 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -"""Juju client.""" diff --git a/build/lib/juju/client/_client.py b/build/lib/juju/client/_client.py deleted file mode 100644 index e72ae738..00000000 --- a/build/lib/juju/client/_client.py +++ /dev/null @@ -1,236 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client import ( - _client1, - _client2, - _client3, - _client4, - _client5, - _client6, - _client7, - _client8, - _client9, - _client10, - _client11, - _client12, - _client17, - _client19, - _client20, -) -from juju.client._definitions import * - -CLIENTS = { - "7": _client7, - "3": _client3, - "4": _client4, - "2": _client2, - "17": _client17, - "6": _client6, - "11": _client11, - "1": _client1, - "10": _client10, - "9": _client9, - "5": _client5, - "19": _client19, - "20": _client20, - "8": _client8, - "12": _client12, -} - - -def lookup_facade(name, version): - """Given a facade name and version, attempt to pull that facade out - of the correct client.py file. - - """ - for _version in range(int(version), 0, -1): - try: - facade = getattr(CLIENTS[str(_version)], name) - return facade - except (KeyError, AttributeError): - continue - else: - raise ImportError(f"No supported version for facade: {name}") - - -class TypeFactory: - @classmethod - def from_connection(cls, connection): - """Given a connected Connection object, return an initialized and - connected instance of an API Interface matching the name of - this class. - - @param connection: initialized Connection object. - - """ - facade_name = cls.__name__ - if not facade_name.endswith("Facade"): - raise TypeError(f"Unexpected class name: {facade_name}") - facade_name = facade_name[: -len("Facade")] - version = connection.facades.get(facade_name) - if version is None: - raise Exception(f"No facade {facade_name} in facades {connection.facades}") - - c = lookup_facade(cls.__name__, version) - c = c() - c.connect(connection) - - return c - - @classmethod - def best_facade_version(cls, connection): - """Returns the best facade version for a given facade. This will help with - trying to provide different functionality for different facade versions. - - @param connection: initialized Connection object. - """ - facade_name = cls.__name__ - if not facade_name.endswith("Facade"): - raise TypeError(f"Unexpected class name: {facade_name}") - facade_name = facade_name[: -len("Facade")] - return connection.facades.get(facade_name) - - -class ActionFacade(TypeFactory): - pass - - -class AdminFacade(TypeFactory): - pass - - -class AllModelWatcherFacade(TypeFactory): - pass - - -class AllWatcherFacade(TypeFactory): - pass - - -class AnnotationsFacade(TypeFactory): - pass - - -class ApplicationFacade(TypeFactory): - pass - - -class ApplicationOffersFacade(TypeFactory): - pass - - -class BackupsFacade(TypeFactory): - pass - - -class BlockFacade(TypeFactory): - pass - - -class BundleFacade(TypeFactory): - pass - - -class CharmsFacade(TypeFactory): - pass - - -class ClientFacade(TypeFactory): - pass - - -class CloudFacade(TypeFactory): - pass - - -class ControllerFacade(TypeFactory): - pass - - -class CredentialManagerFacade(TypeFactory): - pass - - -class FacadeVersions(TypeFactory): - pass - - -class FirewallRulesFacade(TypeFactory): - pass - - -class HighAvailabilityFacade(TypeFactory): - pass - - -class ImageMetadataManagerFacade(TypeFactory): - pass - - -class KeyManagerFacade(TypeFactory): - pass - - -class MachineManagerFacade(TypeFactory): - pass - - -class MetricsDebugFacade(TypeFactory): - pass - - -class ModelConfigFacade(TypeFactory): - pass - - -class ModelGenerationFacade(TypeFactory): - pass - - -class ModelManagerFacade(TypeFactory): - pass - - -class ModelUpgraderFacade(TypeFactory): - pass - - -class PayloadsFacade(TypeFactory): - pass - - -class PingerFacade(TypeFactory): - pass - - -class ResourcesFacade(TypeFactory): - pass - - -class SSHClientFacade(TypeFactory): - pass - - -class SecretBackendsFacade(TypeFactory): - pass - - -class SecretsFacade(TypeFactory): - pass - - -class SpacesFacade(TypeFactory): - pass - - -class StorageFacade(TypeFactory): - pass - - -class SubnetsFacade(TypeFactory): - pass - - -class UserManagerFacade(TypeFactory): - pass diff --git a/build/lib/juju/client/_client1.py b/build/lib/juju/client/_client1.py deleted file mode 100644 index 1c91138c..00000000 --- a/build/lib/juju/client/_client1.py +++ /dev/null @@ -1,550 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class CredentialManagerFacade(Type): - name = "CredentialManager" - version = 1 - - @ReturnMapping(ErrorResult) - async def InvalidateModelCredential(self, reason=None): - """InvalidateModelCredential marks the cloud credential for this model as invalid. - - reason : str - Returns -> ErrorResult - """ - if reason is not None and not isinstance(reason, (bytes, str)): - raise Exception(f"Expected reason to be a str, received: {type(reason)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="CredentialManager", - request="InvalidateModelCredential", - version=1, - params=_params, - ) - _params["reason"] = reason - reply = await self.rpc(msg) - return reply - - -class FirewallRulesFacade(Type): - name = "FirewallRules" - version = 1 - - @ReturnMapping(ListFirewallRulesResults) - async def ListFirewallRules(self): - """ListFirewallRules returns all the firewall rules. - - Returns -> ListFirewallRulesResults - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="FirewallRules", request="ListFirewallRules", version=1, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetFirewallRules(self, args=None): - """SetFirewallRules creates or updates the specified firewall rules. - - args : typing.Sequence[~FirewallRule] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="FirewallRules", request="SetFirewallRules", version=1, params=_params - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - -class ImageMetadataManagerFacade(Type): - name = "ImageMetadataManager" - version = 1 - - @ReturnMapping(ErrorResults) - async def Delete(self, image_ids=None): - """Delete deletes cloud image metadata for given image ids. - It supports bulk calls. - - image_ids : typing.Sequence[str] - Returns -> ErrorResults - """ - if image_ids is not None and not isinstance(image_ids, (bytes, str, list)): - raise Exception( - f"Expected image_ids to be a Sequence, received: {type(image_ids)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ImageMetadataManager", request="Delete", version=1, params=_params - ) - _params["image-ids"] = image_ids - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ListCloudImageMetadataResult) - async def List( - self, - arches=None, - region=None, - root_storage_type=None, - stream=None, - versions=None, - virt_type=None, - ): - """List returns all found cloud image metadata that satisfy - given filter. - Returned list contains metadata ordered by priority. - - arches : typing.Sequence[str] - region : str - root_storage_type : str - stream : str - versions : typing.Sequence[str] - virt_type : str - Returns -> ListCloudImageMetadataResult - """ - if arches is not None and not isinstance(arches, (bytes, str, list)): - raise Exception( - f"Expected arches to be a Sequence, received: {type(arches)}" - ) - - if region is not None and not isinstance(region, (bytes, str)): - raise Exception(f"Expected region to be a str, received: {type(region)}") - - if root_storage_type is not None and not isinstance( - root_storage_type, (bytes, str) - ): - raise Exception( - f"Expected root_storage_type to be a str, received: {type(root_storage_type)}" - ) - - if stream is not None and not isinstance(stream, (bytes, str)): - raise Exception(f"Expected stream to be a str, received: {type(stream)}") - - if versions is not None and not isinstance(versions, (bytes, str, list)): - raise Exception( - f"Expected versions to be a Sequence, received: {type(versions)}" - ) - - if virt_type is not None and not isinstance(virt_type, (bytes, str)): - raise Exception( - f"Expected virt_type to be a str, received: {type(virt_type)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ImageMetadataManager", request="List", version=1, params=_params - ) - _params["arches"] = arches - _params["region"] = region - _params["root-storage-type"] = root_storage_type - _params["stream"] = stream - _params["versions"] = versions - _params["virt-type"] = virt_type - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Save(self, metadata=None): - """Save stores given cloud image metadata. - It supports bulk calls. - - metadata : typing.Sequence[~CloudImageMetadataList] - Returns -> ErrorResults - """ - if metadata is not None and not isinstance(metadata, (bytes, str, list)): - raise Exception( - f"Expected metadata to be a Sequence, received: {type(metadata)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ImageMetadataManager", request="Save", version=1, params=_params - ) - _params["metadata"] = metadata - reply = await self.rpc(msg) - return reply - - -class KeyManagerFacade(Type): - name = "KeyManager" - version = 1 - - @ReturnMapping(ErrorResults) - async def AddKeys(self, ssh_keys=None, user=None): - """AddKeys adds new authorised ssh keys for the specified user. - - ssh_keys : typing.Sequence[str] - user : str - Returns -> ErrorResults - """ - if ssh_keys is not None and not isinstance(ssh_keys, (bytes, str, list)): - raise Exception( - f"Expected ssh_keys to be a Sequence, received: {type(ssh_keys)}" - ) - - if user is not None and not isinstance(user, (bytes, str)): - raise Exception(f"Expected user to be a str, received: {type(user)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="KeyManager", request="AddKeys", version=1, params=_params) - _params["ssh-keys"] = ssh_keys - _params["user"] = user - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DeleteKeys(self, ssh_keys=None, user=None): - """DeleteKeys deletes the authorised ssh keys for the specified user. - - ssh_keys : typing.Sequence[str] - user : str - Returns -> ErrorResults - """ - if ssh_keys is not None and not isinstance(ssh_keys, (bytes, str, list)): - raise Exception( - f"Expected ssh_keys to be a Sequence, received: {type(ssh_keys)}" - ) - - if user is not None and not isinstance(user, (bytes, str)): - raise Exception(f"Expected user to be a str, received: {type(user)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="KeyManager", request="DeleteKeys", version=1, params=_params) - _params["ssh-keys"] = ssh_keys - _params["user"] = user - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ImportKeys(self, ssh_keys=None, user=None): - """ImportKeys imports new authorised ssh keys from the specified key ids for the specified user. - - ssh_keys : typing.Sequence[str] - user : str - Returns -> ErrorResults - """ - if ssh_keys is not None and not isinstance(ssh_keys, (bytes, str, list)): - raise Exception( - f"Expected ssh_keys to be a Sequence, received: {type(ssh_keys)}" - ) - - if user is not None and not isinstance(user, (bytes, str)): - raise Exception(f"Expected user to be a str, received: {type(user)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="KeyManager", request="ImportKeys", version=1, params=_params) - _params["ssh-keys"] = ssh_keys - _params["user"] = user - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringsResults) - async def ListKeys(self, entities=None, mode=None): - """ListKeys returns the authorised ssh keys for the specified users. - - entities : Entities - mode : bool - Returns -> StringsResults - """ - if entities is not None and not isinstance(entities, (dict, Entities)): - raise Exception( - f"Expected entities to be a Entities, received: {type(entities)}" - ) - - if mode is not None and not isinstance(mode, bool): - raise Exception(f"Expected mode to be a bool, received: {type(mode)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="KeyManager", request="ListKeys", version=1, params=_params) - _params["entities"] = entities - _params["mode"] = mode - reply = await self.rpc(msg) - return reply - - -class ModelUpgraderFacade(Type): - name = "ModelUpgrader" - version = 1 - - @ReturnMapping(None) - async def AbortModelUpgrade(self, model_tag=None): - """AbortModelUpgrade aborts and archives the model upgrade - synchronisation record, if any. - - model_tag : str - Returns -> None - """ - if model_tag is not None and not isinstance(model_tag, (bytes, str)): - raise Exception( - f"Expected model_tag to be a str, received: {type(model_tag)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelUpgrader", request="AbortModelUpgrade", version=1, params=_params - ) - _params["model-tag"] = model_tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UpgradeModelResult) - async def UpgradeModel( - self, - agent_stream=None, - dry_run=None, - ignore_agent_versions=None, - model_tag=None, - target_version=None, - ): - """UpgradeModel upgrades a model. - - agent_stream : str - dry_run : bool - ignore_agent_versions : bool - model_tag : str - target_version : Number - Returns -> UpgradeModelResult - """ - if agent_stream is not None and not isinstance(agent_stream, (bytes, str)): - raise Exception( - f"Expected agent_stream to be a str, received: {type(agent_stream)}" - ) - - if dry_run is not None and not isinstance(dry_run, bool): - raise Exception(f"Expected dry_run to be a bool, received: {type(dry_run)}") - - if ignore_agent_versions is not None and not isinstance( - ignore_agent_versions, bool - ): - raise Exception( - f"Expected ignore_agent_versions to be a bool, received: {type(ignore_agent_versions)}" - ) - - if model_tag is not None and not isinstance(model_tag, (bytes, str)): - raise Exception( - f"Expected model_tag to be a str, received: {type(model_tag)}" - ) - - if target_version is not None and not isinstance( - target_version, (dict, Number) - ): - raise Exception( - f"Expected target_version to be a Number, received: {type(target_version)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelUpgrader", request="UpgradeModel", version=1, params=_params - ) - _params["agent-stream"] = agent_stream - _params["dry-run"] = dry_run - _params["ignore-agent-versions"] = ignore_agent_versions - _params["model-tag"] = model_tag - _params["target-version"] = target_version - reply = await self.rpc(msg) - return reply - - -class PayloadsFacade(Type): - name = "Payloads" - version = 1 - - @ReturnMapping(PayloadListResults) - async def List(self, patterns=None): - """List builds the list of payloads being tracked for - the given unit and IDs. If no IDs are provided then all tracked - payloads for the unit are returned. - - patterns : typing.Sequence[str] - Returns -> PayloadListResults - """ - if patterns is not None and not isinstance(patterns, (bytes, str, list)): - raise Exception( - f"Expected patterns to be a Sequence, received: {type(patterns)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Payloads", request="List", version=1, params=_params) - _params["patterns"] = patterns - reply = await self.rpc(msg) - return reply - - -class PingerFacade(Type): - name = "Pinger" - version = 1 - - @ReturnMapping(None) - async def Ping(self): - """Returns -> None""" - # map input types to rpc msg - _params = dict() - msg = dict(type="Pinger", request="Ping", version=1, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def Stop(self): - """Returns -> None""" - # map input types to rpc msg - _params = dict() - msg = dict(type="Pinger", request="Stop", version=1, params=_params) - - reply = await self.rpc(msg) - return reply - - -class SecretBackendsFacade(Type): - name = "SecretBackends" - version = 1 - - @ReturnMapping(ErrorResults) - async def AddSecretBackends(self, args=None): - """AddSecretBackends adds new secret backends. - - args : typing.Sequence[~AddSecretBackendArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="SecretBackends", - request="AddSecretBackends", - version=1, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ListSecretBackendsResults) - async def ListSecretBackends(self, names=None, reveal=None): - """ListSecretBackends lists available secret backends. - - names : typing.Sequence[str] - reveal : bool - Returns -> ListSecretBackendsResults - """ - if names is not None and not isinstance(names, (bytes, str, list)): - raise Exception(f"Expected names to be a Sequence, received: {type(names)}") - - if reveal is not None and not isinstance(reveal, bool): - raise Exception(f"Expected reveal to be a bool, received: {type(reveal)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="SecretBackends", - request="ListSecretBackends", - version=1, - params=_params, - ) - _params["names"] = names - _params["reveal"] = reveal - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def RemoveSecretBackends(self, args=None): - """RemoveSecretBackends removes secret backends. - - args : typing.Sequence[~RemoveSecretBackendArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="SecretBackends", - request="RemoveSecretBackends", - version=1, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UpdateSecretBackends(self, args=None): - """UpdateSecretBackends updates secret backends. - - args : typing.Sequence[~UpdateSecretBackendArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="SecretBackends", - request="UpdateSecretBackends", - version=1, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - -class SecretsFacade(Type): - name = "Secrets" - version = 1 - - @ReturnMapping(ListSecretResults) - async def ListSecrets(self, filter_=None, show_secrets=None): - """ListSecrets lists available secrets. - - filter_ : SecretsFilter - show_secrets : bool - Returns -> ListSecretResults - """ - if filter_ is not None and not isinstance(filter_, (dict, SecretsFilter)): - raise Exception( - f"Expected filter_ to be a SecretsFilter, received: {type(filter_)}" - ) - - if show_secrets is not None and not isinstance(show_secrets, bool): - raise Exception( - f"Expected show_secrets to be a bool, received: {type(show_secrets)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Secrets", request="ListSecrets", version=1, params=_params) - _params["filter"] = filter_ - _params["show-secrets"] = show_secrets - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client10.py b/build/lib/juju/client/_client10.py deleted file mode 100644 index 2de5c721..00000000 --- a/build/lib/juju/client/_client10.py +++ /dev/null @@ -1,674 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class MachineManagerFacade(Type): - name = "MachineManager" - version = 10 - - @ReturnMapping(AddMachinesResults) - async def AddMachines(self, params=None): - """AddMachines adds new machines with the supplied parameters. - The args will contain Base info. - - params : typing.Sequence[~AddMachineParams] - Returns -> AddMachinesResults - """ - if params is not None and not isinstance(params, (bytes, str, list)): - raise Exception( - f"Expected params to be a Sequence, received: {type(params)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", request="AddMachines", version=10, params=_params - ) - _params["params"] = params - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DestroyMachineResults) - async def DestroyMachineWithParams( - self, dry_run=None, force=None, keep=None, machine_tags=None, max_wait=None - ): - """DestroyMachineWithParams removes a set of machines from the model. - - dry_run : bool - force : bool - keep : bool - machine_tags : typing.Sequence[str] - max_wait : int - Returns -> DestroyMachineResults - """ - if dry_run is not None and not isinstance(dry_run, bool): - raise Exception(f"Expected dry_run to be a bool, received: {type(dry_run)}") - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if keep is not None and not isinstance(keep, bool): - raise Exception(f"Expected keep to be a bool, received: {type(keep)}") - - if machine_tags is not None and not isinstance( - machine_tags, (bytes, str, list) - ): - raise Exception( - f"Expected machine_tags to be a Sequence, received: {type(machine_tags)}" - ) - - if max_wait is not None and not isinstance(max_wait, int): - raise Exception( - f"Expected max_wait to be a int, received: {type(max_wait)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", - request="DestroyMachineWithParams", - version=10, - params=_params, - ) - _params["dry-run"] = dry_run - _params["force"] = force - _params["keep"] = keep - _params["machine-tags"] = machine_tags - _params["max-wait"] = max_wait - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringsResults) - async def GetUpgradeSeriesMessages(self, params=None): - """GetUpgradeSeriesMessages returns all new messages associated with upgrade - series events. Messages that have already been retrieved once are not - returned by this method. - - params : typing.Sequence[~UpgradeSeriesNotificationParam] - Returns -> StringsResults - """ - if params is not None and not isinstance(params, (bytes, str, list)): - raise Exception( - f"Expected params to be a Sequence, received: {type(params)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", - request="GetUpgradeSeriesMessages", - version=10, - params=_params, - ) - _params["params"] = params - reply = await self.rpc(msg) - return reply - - @ReturnMapping(InstanceTypesResults) - async def InstanceTypes(self, constraints=None): - """InstanceTypes returns instance type information for the cloud and region - in which the current model is deployed. - - constraints : typing.Sequence[~ModelInstanceTypesConstraint] - Returns -> InstanceTypesResults - """ - if constraints is not None and not isinstance(constraints, (bytes, str, list)): - raise Exception( - f"Expected constraints to be a Sequence, received: {type(constraints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", request="InstanceTypes", version=10, params=_params - ) - _params["constraints"] = constraints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ProvisioningScriptResult) - async def ProvisioningScript( - self, data_dir=None, disable_package_commands=None, machine_id=None, nonce=None - ): - """ProvisioningScript returns a shell script that, when run, - provisions a machine agent on the machine executing the script. - - data_dir : str - disable_package_commands : bool - machine_id : str - nonce : str - Returns -> ProvisioningScriptResult - """ - if data_dir is not None and not isinstance(data_dir, (bytes, str)): - raise Exception( - f"Expected data_dir to be a str, received: {type(data_dir)}" - ) - - if disable_package_commands is not None and not isinstance( - disable_package_commands, bool - ): - raise Exception( - f"Expected disable_package_commands to be a bool, received: {type(disable_package_commands)}" - ) - - if machine_id is not None and not isinstance(machine_id, (bytes, str)): - raise Exception( - f"Expected machine_id to be a str, received: {type(machine_id)}" - ) - - if nonce is not None and not isinstance(nonce, (bytes, str)): - raise Exception(f"Expected nonce to be a str, received: {type(nonce)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", - request="ProvisioningScript", - version=10, - params=_params, - ) - _params["data-dir"] = data_dir - _params["disable-package-commands"] = disable_package_commands - _params["machine-id"] = machine_id - _params["nonce"] = nonce - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def RetryProvisioning(self, all_=None, machines=None): - """RetryProvisioning marks a provisioning error as transient on the machines. - - all_ : bool - machines : typing.Sequence[str] - Returns -> ErrorResults - """ - if all_ is not None and not isinstance(all_, bool): - raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") - - if machines is not None and not isinstance(machines, (bytes, str, list)): - raise Exception( - f"Expected machines to be a Sequence, received: {type(machines)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", - request="RetryProvisioning", - version=10, - params=_params, - ) - _params["all"] = all_ - _params["machines"] = machines - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResult) - async def UpgradeSeriesComplete(self, channel=None, force=None, tag=None): - """UpgradeSeriesComplete marks a machine as having completed a managed series - upgrade. - - channel : str - force : bool - tag : Entity - Returns -> ErrorResult - """ - if channel is not None and not isinstance(channel, (bytes, str)): - raise Exception(f"Expected channel to be a str, received: {type(channel)}") - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if tag is not None and not isinstance(tag, (dict, Entity)): - raise Exception(f"Expected tag to be a Entity, received: {type(tag)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", - request="UpgradeSeriesComplete", - version=10, - params=_params, - ) - _params["channel"] = channel - _params["force"] = force - _params["tag"] = tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResult) - async def UpgradeSeriesPrepare(self, channel=None, force=None, tag=None): - """UpgradeSeriesPrepare prepares a machine for a OS series upgrade. - - channel : str - force : bool - tag : Entity - Returns -> ErrorResult - """ - if channel is not None and not isinstance(channel, (bytes, str)): - raise Exception(f"Expected channel to be a str, received: {type(channel)}") - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if tag is not None and not isinstance(tag, (dict, Entity)): - raise Exception(f"Expected tag to be a Entity, received: {type(tag)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", - request="UpgradeSeriesPrepare", - version=10, - params=_params, - ) - _params["channel"] = channel - _params["force"] = force - _params["tag"] = tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UpgradeSeriesUnitsResults) - async def UpgradeSeriesValidate(self, args=None): - """UpgradeSeriesValidate validates that the incoming arguments correspond to a - valid series upgrade for the target machine. - If they do, a list of the machine's current units is returned for use in - soliciting user confirmation of the command. - - args : typing.Sequence[~UpdateChannelArg] - Returns -> UpgradeSeriesUnitsResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", - request="UpgradeSeriesValidate", - version=10, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(NotifyWatchResults) - async def WatchUpgradeSeriesNotifications(self, entities=None): - """WatchUpgradeSeriesNotifications returns a watcher that fires on upgrade - series events. - - entities : typing.Sequence[~Entity] - Returns -> NotifyWatchResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MachineManager", - request="WatchUpgradeSeriesNotifications", - version=10, - params=_params, - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - -class ModelManagerFacade(Type): - name = "ModelManager" - version = 10 - - @ReturnMapping(ErrorResults) - async def ChangeModelCredential(self, model_credentials=None): - """ChangeModelCredential changes cloud credential reference for models. - These new cloud credentials must already exist on the controller. - - model_credentials : typing.Sequence[~ChangeModelCredentialParams] - Returns -> ErrorResults - """ - if model_credentials is not None and not isinstance( - model_credentials, (bytes, str, list) - ): - raise Exception( - f"Expected model_credentials to be a Sequence, received: {type(model_credentials)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", - request="ChangeModelCredential", - version=10, - params=_params, - ) - _params["model-credentials"] = model_credentials - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelInfo) - async def CreateModel( - self, - cloud_tag=None, - config=None, - credential=None, - name=None, - owner_tag=None, - region=None, - ): - """CreateModel creates a new model using the account and - model config specified in the args. - - cloud_tag : str - config : typing.Mapping[str, typing.Any] - credential : str - name : str - owner_tag : str - region : str - Returns -> ModelInfo - """ - if cloud_tag is not None and not isinstance(cloud_tag, (bytes, str)): - raise Exception( - f"Expected cloud_tag to be a str, received: {type(cloud_tag)}" - ) - - if config is not None and not isinstance(config, dict): - raise Exception( - f"Expected config to be a Mapping, received: {type(config)}" - ) - - if credential is not None and not isinstance(credential, (bytes, str)): - raise Exception( - f"Expected credential to be a str, received: {type(credential)}" - ) - - if name is not None and not isinstance(name, (bytes, str)): - raise Exception(f"Expected name to be a str, received: {type(name)}") - - if owner_tag is not None and not isinstance(owner_tag, (bytes, str)): - raise Exception( - f"Expected owner_tag to be a str, received: {type(owner_tag)}" - ) - - if region is not None and not isinstance(region, (bytes, str)): - raise Exception(f"Expected region to be a str, received: {type(region)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="CreateModel", version=10, params=_params - ) - _params["cloud-tag"] = cloud_tag - _params["config"] = config - _params["credential"] = credential - _params["name"] = name - _params["owner-tag"] = owner_tag - _params["region"] = region - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DestroyModels(self, models=None): - """DestroyModels will try to destroy the specified models. - If there is a block on destruction, this method will return an error. - From ModelManager v7 onwards, DestroyModels gains 'force' and 'max-wait' parameters. - - models : typing.Sequence[~DestroyModelParams] - Returns -> ErrorResults - """ - if models is not None and not isinstance(models, (bytes, str, list)): - raise Exception( - f"Expected models to be a Sequence, received: {type(models)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="DestroyModels", version=10, params=_params - ) - _params["models"] = models - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResults) - async def DumpModels(self, entities=None, simplified=None): - """DumpModels will export the models into the database agnostic - representation. The user needs to either be a controller admin, or have - admin privileges on the model itself. - - entities : typing.Sequence[~Entity] - simplified : bool - Returns -> StringResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - if simplified is not None and not isinstance(simplified, bool): - raise Exception( - f"Expected simplified to be a bool, received: {type(simplified)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="DumpModels", version=10, params=_params - ) - _params["entities"] = entities - _params["simplified"] = simplified - reply = await self.rpc(msg) - return reply - - @ReturnMapping(MapResults) - async def DumpModelsDB(self, entities=None): - """DumpModelsDB will gather all documents from all model collections - for the specified model. The map result contains a map of collection - names to lists of documents represented as maps. - - entities : typing.Sequence[~Entity] - Returns -> MapResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="DumpModelsDB", version=10, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelSummaryResults) - async def ListModelSummaries(self, all_=None, user_tag=None): - """ListModelSummaries returns models that the specified user - has access to in the current server. Controller admins (superuser) - can list models for any user. Other users - can only ask about their own models. - - all_ : bool - user_tag : str - Returns -> ModelSummaryResults - """ - if all_ is not None and not isinstance(all_, bool): - raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") - - if user_tag is not None and not isinstance(user_tag, (bytes, str)): - raise Exception( - f"Expected user_tag to be a str, received: {type(user_tag)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", - request="ListModelSummaries", - version=10, - params=_params, - ) - _params["all"] = all_ - _params["user-tag"] = user_tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UserModelList) - async def ListModels(self, tag=None): - """ListModels returns the models that the specified user - has access to in the current server. Controller admins (superuser) - can list models for any user. Other users - can only ask about their own models. - - tag : str - Returns -> UserModelList - """ - if tag is not None and not isinstance(tag, (bytes, str)): - raise Exception(f"Expected tag to be a str, received: {type(tag)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="ListModels", version=10, params=_params - ) - _params["tag"] = tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelDefaultsResults) - async def ModelDefaultsForClouds(self, entities=None): - """ModelDefaultsForClouds returns the default config values for the specified - clouds. - - entities : typing.Sequence[~Entity] - Returns -> ModelDefaultsResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", - request="ModelDefaultsForClouds", - version=10, - params=_params, - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelInfoResults) - async def ModelInfo(self, entities=None): - """ModelInfo returns information about the specified models. - - entities : typing.Sequence[~Entity] - Returns -> ModelInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelManager", request="ModelInfo", version=10, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelStatusResults) - async def ModelStatus(self, entities=None): - """ModelStatus returns a summary of the model. - - entities : typing.Sequence[~Entity] - Returns -> ModelStatusResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="ModelStatus", version=10, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ModifyModelAccess(self, changes=None): - """ModifyModelAccess changes the model access granted to users. - - changes : typing.Sequence[~ModifyModelAccess] - Returns -> ErrorResults - """ - if changes is not None and not isinstance(changes, (bytes, str, list)): - raise Exception( - f"Expected changes to be a Sequence, received: {type(changes)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="ModifyModelAccess", version=10, params=_params - ) - _params["changes"] = changes - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetModelDefaults(self, config=None): - """SetModelDefaults writes new values for the specified default model settings. - - config : typing.Sequence[~ModelDefaultValues] - Returns -> ErrorResults - """ - if config is not None and not isinstance(config, (bytes, str, list)): - raise Exception( - f"Expected config to be a Sequence, received: {type(config)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="SetModelDefaults", version=10, params=_params - ) - _params["config"] = config - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UnsetModelDefaults(self, keys=None): - """UnsetModelDefaults removes the specified default model settings. - - keys : typing.Sequence[~ModelUnsetKeys] - Returns -> ErrorResults - """ - if keys is not None and not isinstance(keys, (bytes, str, list)): - raise Exception(f"Expected keys to be a Sequence, received: {type(keys)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", - request="UnsetModelDefaults", - version=10, - params=_params, - ) - _params["keys"] = keys - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client11.py b/build/lib/juju/client/_client11.py deleted file mode 100644 index c1f00f6b..00000000 --- a/build/lib/juju/client/_client11.py +++ /dev/null @@ -1,481 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class ControllerFacade(Type): - name = "Controller" - version = 11 - - @ReturnMapping(UserModelList) - async def AllModels(self): - """AllModels allows controller administrators to get the list of all the - models in the controller. - - Returns -> UserModelList - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Controller", request="AllModels", version=11, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CloudSpecResults) - async def CloudSpec(self, entities=None): - """CloudSpec returns the model's cloud spec. - - entities : typing.Sequence[~Entity] - Returns -> CloudSpecResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Controller", request="CloudSpec", version=11, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def ConfigSet(self, config=None): - """ConfigSet changes the value of specified controller configuration - settings. Only some settings can be changed after bootstrap. - Settings that aren't specified in the params are left unchanged. - - config : typing.Mapping[str, typing.Any] - Returns -> None - """ - if config is not None and not isinstance(config, dict): - raise Exception( - f"Expected config to be a Mapping, received: {type(config)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Controller", request="ConfigSet", version=11, params=_params) - _params["config"] = config - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ControllerAPIInfoResults) - async def ControllerAPIInfoForModels(self, entities=None): - """ControllerAPIInfoForModels returns the controller api connection details for the specified models. - - entities : typing.Sequence[~Entity] - Returns -> ControllerAPIInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="ControllerAPIInfoForModels", - version=11, - params=_params, - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ControllerConfigResult) - async def ControllerConfig(self): - """ControllerConfig returns the controller's configuration. - - Returns -> ControllerConfigResult - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="ControllerConfig", version=11, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ControllerVersionResults) - async def ControllerVersion(self): - """ControllerVersion returns the version information associated with this - controller binary. - - NOTE: the implementation intentionally does not check for SuperuserAccess - as the Version is known even to users with login access. - - Returns -> ControllerVersionResults - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="ControllerVersion", version=11, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DashboardConnectionInfo) - async def DashboardConnectionInfo(self): - """DashboardConnectionInfo returns the connection information for a client to - connect to the Juju Dashboard including any proxying information. - - Returns -> DashboardConnectionInfo - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="DashboardConnectionInfo", - version=11, - params=_params, - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def DestroyController( - self, - destroy_models=None, - destroy_storage=None, - force=None, - max_wait=None, - model_timeout=None, - ): - """DestroyController destroys the controller. - - If the args specify the destruction of the models, this method will - attempt to do so. Otherwise, if the controller has any non-empty, - non-Dead hosted models, then an error with the code - params.CodeHasHostedModels will be transmitted. - - destroy_models : bool - destroy_storage : bool - force : bool - max_wait : int - model_timeout : int - Returns -> None - """ - if destroy_models is not None and not isinstance(destroy_models, bool): - raise Exception( - f"Expected destroy_models to be a bool, received: {type(destroy_models)}" - ) - - if destroy_storage is not None and not isinstance(destroy_storage, bool): - raise Exception( - f"Expected destroy_storage to be a bool, received: {type(destroy_storage)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if max_wait is not None and not isinstance(max_wait, int): - raise Exception( - f"Expected max_wait to be a int, received: {type(max_wait)}" - ) - - if model_timeout is not None and not isinstance(model_timeout, int): - raise Exception( - f"Expected model_timeout to be a int, received: {type(model_timeout)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="DestroyController", version=11, params=_params - ) - _params["destroy-models"] = destroy_models - _params["destroy-storage"] = destroy_storage - _params["force"] = force - _params["max-wait"] = max_wait - _params["model-timeout"] = model_timeout - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CloudSpecResult) - async def GetCloudSpec(self): - """GetCloudSpec constructs the CloudSpec for a validated and authorized model. - - Returns -> CloudSpecResult - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="GetCloudSpec", version=11, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UserAccessResults) - async def GetControllerAccess(self, entities=None): - """GetControllerAccess returns the level of access the specified users - have on the controller. - - entities : typing.Sequence[~Entity] - Returns -> UserAccessResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="GetControllerAccess", version=11, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(HostedModelConfigsResults) - async def HostedModelConfigs(self): - """HostedModelConfigs returns all the information that the client needs in - order to connect directly with the host model's provider and destroy it - directly. - - Returns -> HostedModelConfigsResults - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="HostedModelConfigs", version=11, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResult) - async def IdentityProviderURL(self): - """IdentityProviderURL returns the URL of the configured external identity - provider for this controller or an empty string if no external identity - provider has been configured when the controller was bootstrapped. - - NOTE: the implementation intentionally does not check for SuperuserAccess - as the URL is known even to users with login access. - - Returns -> StringResult - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="IdentityProviderURL", version=11, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(InitiateMigrationResults) - async def InitiateMigration(self, specs=None): - """InitiateMigration attempts to begin the migration of one or - more models to other controllers. - - specs : typing.Sequence[~MigrationSpec] - Returns -> InitiateMigrationResults - """ - if specs is not None and not isinstance(specs, (bytes, str, list)): - raise Exception(f"Expected specs to be a Sequence, received: {type(specs)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="InitiateMigration", version=11, params=_params - ) - _params["specs"] = specs - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelBlockInfoList) - async def ListBlockedModels(self): - """ListBlockedModels returns a list of all models on the controller - which have a block in place. The resulting slice is sorted by model - name, then owner. Callers must be controller administrators to retrieve the - list. - - Returns -> ModelBlockInfoList - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="ListBlockedModels", version=11, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelConfigResults) - async def ModelConfig(self): - """ModelConfig returns the model config for the controller - model. For information on the current model, use - client.ModelGet - - Returns -> ModelConfigResults - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Controller", request="ModelConfig", version=11, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelStatusResults) - async def ModelStatus(self, entities=None): - """ModelStatus returns a summary of the model. - - entities : typing.Sequence[~Entity] - Returns -> ModelStatusResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Controller", request="ModelStatus", version=11, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ModifyControllerAccess(self, changes=None): - """ModifyControllerAccess changes the model access granted to users. - - changes : typing.Sequence[~ModifyControllerAccess] - Returns -> ErrorResults - """ - if changes is not None and not isinstance(changes, (bytes, str, list)): - raise Exception( - f"Expected changes to be a Sequence, received: {type(changes)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="ModifyControllerAccess", - version=11, - params=_params, - ) - _params["changes"] = changes - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResult) - async def MongoVersion(self): - """MongoVersion allows the introspection of the mongo version per controller - - Returns -> StringResult - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="MongoVersion", version=11, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def RemoveBlocks(self, all_=None): - """RemoveBlocks removes all the blocks in the controller. - - all_ : bool - Returns -> None - """ - if all_ is not None and not isinstance(all_, bool): - raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="RemoveBlocks", version=11, params=_params - ) - _params["all"] = all_ - reply = await self.rpc(msg) - return reply - - @ReturnMapping(SummaryWatcherID) - async def WatchAllModelSummaries(self): - """WatchAllModelSummaries starts watching the summary updates from the cache. - This method is superuser access only, and watches all models in the - controller. - - Returns -> SummaryWatcherID - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="WatchAllModelSummaries", - version=11, - params=_params, - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(AllWatcherId) - async def WatchAllModels(self): - """WatchAllModels starts watching events for all models in the - controller. The returned AllWatcherId should be used with Next on the - AllModelWatcher endpoint to receive deltas. - - Returns -> AllWatcherId - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="WatchAllModels", version=11, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(NotifyWatchResults) - async def WatchCloudSpecsChanges(self, entities=None): - """WatchCloudSpecsChanges returns a watcher for cloud spec changes. - - entities : typing.Sequence[~Entity] - Returns -> NotifyWatchResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="WatchCloudSpecsChanges", - version=11, - params=_params, - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(SummaryWatcherID) - async def WatchModelSummaries(self): - """WatchModelSummaries starts watching the summary updates from the cache. - Only models that the user has access to are returned. - - Returns -> SummaryWatcherID - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="WatchModelSummaries", version=11, params=_params - ) - - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client12.py b/build/lib/juju/client/_client12.py deleted file mode 100644 index 55624321..00000000 --- a/build/lib/juju/client/_client12.py +++ /dev/null @@ -1,466 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class ControllerFacade(Type): - name = "Controller" - version = 12 - - @ReturnMapping(UserModelList) - async def AllModels(self): - """AllModels allows controller administrators to get the list of all the - models in the controller. - - Returns -> UserModelList - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Controller", request="AllModels", version=12, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CloudSpecResults) - async def CloudSpec(self, entities=None): - """CloudSpec returns the model's cloud spec. - - entities : typing.Sequence[~Entity] - Returns -> CloudSpecResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Controller", request="CloudSpec", version=12, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def ConfigSet(self, config=None): - """ConfigSet changes the value of specified controller configuration - settings. Only some settings can be changed after bootstrap. - Settings that aren't specified in the params are left unchanged. - - config : typing.Mapping[str, typing.Any] - Returns -> None - """ - if config is not None and not isinstance(config, dict): - raise Exception( - f"Expected config to be a Mapping, received: {type(config)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Controller", request="ConfigSet", version=12, params=_params) - _params["config"] = config - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ControllerAPIInfoResults) - async def ControllerAPIInfoForModels(self, entities=None): - """ControllerAPIInfoForModels returns the controller api connection details for the specified models. - - entities : typing.Sequence[~Entity] - Returns -> ControllerAPIInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="ControllerAPIInfoForModels", - version=12, - params=_params, - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ControllerConfigResult) - async def ControllerConfig(self): - """ControllerConfig returns the controller's configuration. - - Returns -> ControllerConfigResult - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="ControllerConfig", version=12, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ControllerVersionResults) - async def ControllerVersion(self): - """ControllerVersion returns the version information associated with this - controller binary. - - NOTE: the implementation intentionally does not check for SuperuserAccess - as the Version is known even to users with login access. - - Returns -> ControllerVersionResults - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="ControllerVersion", version=12, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DashboardConnectionInfo) - async def DashboardConnectionInfo(self): - """DashboardConnectionInfo returns the connection information for a client to - connect to the Juju Dashboard including any proxying information. - - Returns -> DashboardConnectionInfo - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="DashboardConnectionInfo", - version=12, - params=_params, - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def DestroyController( - self, - destroy_models=None, - destroy_storage=None, - force=None, - max_wait=None, - model_timeout=None, - ): - """DestroyController destroys the controller. - - If the args specify the destruction of the models, this method will - attempt to do so. Otherwise, if the controller has any non-empty, - non-Dead hosted models, then an error with the code - params.CodeHasHostedModels will be transmitted. - - destroy_models : bool - destroy_storage : bool - force : bool - max_wait : int - model_timeout : int - Returns -> None - """ - if destroy_models is not None and not isinstance(destroy_models, bool): - raise Exception( - f"Expected destroy_models to be a bool, received: {type(destroy_models)}" - ) - - if destroy_storage is not None and not isinstance(destroy_storage, bool): - raise Exception( - f"Expected destroy_storage to be a bool, received: {type(destroy_storage)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if max_wait is not None and not isinstance(max_wait, int): - raise Exception( - f"Expected max_wait to be a int, received: {type(max_wait)}" - ) - - if model_timeout is not None and not isinstance(model_timeout, int): - raise Exception( - f"Expected model_timeout to be a int, received: {type(model_timeout)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="DestroyController", version=12, params=_params - ) - _params["destroy-models"] = destroy_models - _params["destroy-storage"] = destroy_storage - _params["force"] = force - _params["max-wait"] = max_wait - _params["model-timeout"] = model_timeout - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CloudSpecResult) - async def GetCloudSpec(self): - """GetCloudSpec constructs the CloudSpec for a validated and authorized model. - - Returns -> CloudSpecResult - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="GetCloudSpec", version=12, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UserAccessResults) - async def GetControllerAccess(self, entities=None): - """GetControllerAccess returns the level of access the specified users - have on the controller. - - entities : typing.Sequence[~Entity] - Returns -> UserAccessResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="GetControllerAccess", version=12, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(HostedModelConfigsResults) - async def HostedModelConfigs(self): - """HostedModelConfigs returns all the information that the client needs in - order to connect directly with the host model's provider and destroy it - directly. - - Returns -> HostedModelConfigsResults - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="HostedModelConfigs", version=12, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResult) - async def IdentityProviderURL(self): - """IdentityProviderURL returns the URL of the configured external identity - provider for this controller or an empty string if no external identity - provider has been configured when the controller was bootstrapped. - - NOTE: the implementation intentionally does not check for SuperuserAccess - as the URL is known even to users with login access. - - Returns -> StringResult - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="IdentityProviderURL", version=12, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(InitiateMigrationResults) - async def InitiateMigration(self, specs=None): - """InitiateMigration attempts to begin the migration of one or - more models to other controllers. - - specs : typing.Sequence[~MigrationSpec] - Returns -> InitiateMigrationResults - """ - if specs is not None and not isinstance(specs, (bytes, str, list)): - raise Exception(f"Expected specs to be a Sequence, received: {type(specs)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="InitiateMigration", version=12, params=_params - ) - _params["specs"] = specs - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelBlockInfoList) - async def ListBlockedModels(self): - """ListBlockedModels returns a list of all models on the controller - which have a block in place. The resulting slice is sorted by model - name, then owner. Callers must be controller administrators to retrieve the - list. - - Returns -> ModelBlockInfoList - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="ListBlockedModels", version=12, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelStatusResults) - async def ModelStatus(self, entities=None): - """ModelStatus returns a summary of the model. - - entities : typing.Sequence[~Entity] - Returns -> ModelStatusResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Controller", request="ModelStatus", version=12, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ModifyControllerAccess(self, changes=None): - """ModifyControllerAccess changes the model access granted to users. - - changes : typing.Sequence[~ModifyControllerAccess] - Returns -> ErrorResults - """ - if changes is not None and not isinstance(changes, (bytes, str, list)): - raise Exception( - f"Expected changes to be a Sequence, received: {type(changes)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="ModifyControllerAccess", - version=12, - params=_params, - ) - _params["changes"] = changes - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResult) - async def MongoVersion(self): - """MongoVersion allows the introspection of the mongo version per controller - - Returns -> StringResult - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="MongoVersion", version=12, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def RemoveBlocks(self, all_=None): - """RemoveBlocks removes all the blocks in the controller. - - all_ : bool - Returns -> None - """ - if all_ is not None and not isinstance(all_, bool): - raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="RemoveBlocks", version=12, params=_params - ) - _params["all"] = all_ - reply = await self.rpc(msg) - return reply - - @ReturnMapping(SummaryWatcherID) - async def WatchAllModelSummaries(self): - """WatchAllModelSummaries starts watching the summary updates from the cache. - This method is superuser access only, and watches all models in the - controller. - - Returns -> SummaryWatcherID - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="WatchAllModelSummaries", - version=12, - params=_params, - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(AllWatcherId) - async def WatchAllModels(self): - """WatchAllModels starts watching events for all models in the - controller. The returned AllWatcherId should be used with Next on the - AllModelWatcher endpoint to receive deltas. - - Returns -> AllWatcherId - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="WatchAllModels", version=12, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(NotifyWatchResults) - async def WatchCloudSpecsChanges(self, entities=None): - """WatchCloudSpecsChanges returns a watcher for cloud spec changes. - - entities : typing.Sequence[~Entity] - Returns -> NotifyWatchResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", - request="WatchCloudSpecsChanges", - version=12, - params=_params, - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(SummaryWatcherID) - async def WatchModelSummaries(self): - """WatchModelSummaries starts watching the summary updates from the cache. - Only models that the user has access to are returned. - - Returns -> SummaryWatcherID - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Controller", request="WatchModelSummaries", version=12, params=_params - ) - - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client17.py b/build/lib/juju/client/_client17.py deleted file mode 100644 index 50ecbcf6..00000000 --- a/build/lib/juju/client/_client17.py +++ /dev/null @@ -1,817 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class ApplicationFacade(Type): - name = "Application" - version = 17 - - @ReturnMapping(AddRelationResults) - async def AddRelation(self, endpoints=None, via_cidrs=None): - """AddRelation adds a relation between the specified endpoints and returns the relation info. - - endpoints : typing.Sequence[str] - via_cidrs : typing.Sequence[str] - Returns -> AddRelationResults - """ - if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): - raise Exception( - f"Expected endpoints to be a Sequence, received: {type(endpoints)}" - ) - - if via_cidrs is not None and not isinstance(via_cidrs, (bytes, str, list)): - raise Exception( - f"Expected via_cidrs to be a Sequence, received: {type(via_cidrs)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="AddRelation", version=17, params=_params - ) - _params["endpoints"] = endpoints - _params["via-cidrs"] = via_cidrs - reply = await self.rpc(msg) - return reply - - @ReturnMapping(AddApplicationUnitsResults) - async def AddUnits( - self, - application=None, - attach_storage=None, - num_units=None, - placement=None, - policy=None, - ): - """AddUnits adds a given number of units to an application. - - application : str - attach_storage : typing.Sequence[str] - num_units : int - placement : typing.Sequence[~Placement] - policy : str - Returns -> AddApplicationUnitsResults - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if attach_storage is not None and not isinstance( - attach_storage, (bytes, str, list) - ): - raise Exception( - f"Expected attach_storage to be a Sequence, received: {type(attach_storage)}" - ) - - if num_units is not None and not isinstance(num_units, int): - raise Exception( - f"Expected num_units to be a int, received: {type(num_units)}" - ) - - if placement is not None and not isinstance(placement, (bytes, str, list)): - raise Exception( - f"Expected placement to be a Sequence, received: {type(placement)}" - ) - - if policy is not None and not isinstance(policy, (bytes, str)): - raise Exception(f"Expected policy to be a str, received: {type(policy)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="AddUnits", version=17, params=_params) - _params["application"] = application - _params["attach-storage"] = attach_storage - _params["num-units"] = num_units - _params["placement"] = placement - _params["policy"] = policy - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationInfoResults) - async def ApplicationsInfo(self, entities=None): - """ApplicationsInfo returns applications information. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="ApplicationsInfo", version=17, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetConfigResults) - async def CharmConfig(self, args=None): - """CharmConfig returns charm config for the input list of applications and - model generations. - - args : typing.Sequence[~ApplicationGet] - Returns -> ApplicationGetConfigResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="CharmConfig", version=17, params=_params - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationCharmRelationsResults) - async def CharmRelations(self, application=None): - """CharmRelations implements the server side of Application.CharmRelations. - - application : str - Returns -> ApplicationCharmRelationsResults - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="CharmRelations", version=17, params=_params - ) - _params["application"] = application - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Consume(self, args=None): - """Consume adds remote applications to the model without creating any - relations. - - args : typing.Sequence[~ConsumeApplicationArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Consume", version=17, params=_params) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Deploy(self, applications=None): - """Deploy fetches the charms from the charm store and deploys them - using the specified placement directives. - - applications : typing.Sequence[~ApplicationDeploy] - Returns -> ErrorResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Deploy", version=17, params=_params) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DestroyApplicationResults) - async def DestroyApplication(self, applications=None): - """DestroyApplication removes a given set of applications. - - applications : typing.Sequence[~DestroyApplicationParams] - Returns -> DestroyApplicationResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="DestroyApplication", version=17, params=_params - ) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DestroyConsumedApplications(self, applications=None): - """DestroyConsumedApplications removes a given set of consumed (remote) applications. - - applications : typing.Sequence[~DestroyConsumedApplicationParams] - Returns -> ErrorResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="DestroyConsumedApplications", - version=17, - params=_params, - ) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def DestroyRelation( - self, endpoints=None, force=None, max_wait=None, relation_id=None - ): - """DestroyRelation removes the relation between the - specified endpoints or an id. - - endpoints : typing.Sequence[str] - force : bool - max_wait : int - relation_id : int - Returns -> None - """ - if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): - raise Exception( - f"Expected endpoints to be a Sequence, received: {type(endpoints)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if max_wait is not None and not isinstance(max_wait, int): - raise Exception( - f"Expected max_wait to be a int, received: {type(max_wait)}" - ) - - if relation_id is not None and not isinstance(relation_id, int): - raise Exception( - f"Expected relation_id to be a int, received: {type(relation_id)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="DestroyRelation", version=17, params=_params - ) - _params["endpoints"] = endpoints - _params["force"] = force - _params["max-wait"] = max_wait - _params["relation-id"] = relation_id - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DestroyUnitResults) - async def DestroyUnit(self, units=None): - """DestroyUnit removes a given set of application units. - - units : typing.Sequence[~DestroyUnitParams] - Returns -> DestroyUnitResults - """ - if units is not None and not isinstance(units, (bytes, str, list)): - raise Exception(f"Expected units to be a Sequence, received: {type(units)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="DestroyUnit", version=17, params=_params - ) - _params["units"] = units - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def Expose(self, application=None, exposed_endpoints=None): - """Expose changes the juju-managed firewall to expose any ports that - were also explicitly marked by units as open. - - application : str - exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if exposed_endpoints is not None and not isinstance(exposed_endpoints, dict): - raise Exception( - f"Expected exposed_endpoints to be a Mapping, received: {type(exposed_endpoints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Expose", version=17, params=_params) - _params["application"] = application - _params["exposed-endpoints"] = exposed_endpoints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetResults) - async def Get(self, application=None, branch=None): - """Get returns the charm configuration for an application. - - application : str - branch : str - Returns -> ApplicationGetResults - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Get", version=17, params=_params) - _params["application"] = application - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CharmURLOriginResult) - async def GetCharmURLOrigin(self, application=None, branch=None): - """GetCharmURLOrigin returns the charm URL and charm origin the given - application is running at present. - - application : str - branch : str - Returns -> CharmURLOriginResult - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="GetCharmURLOrigin", version=17, params=_params - ) - _params["application"] = application - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetConfigResults) - async def GetConfig(self, entities=None): - """GetConfig returns the charm config for each of the input applications. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationGetConfigResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="GetConfig", version=17, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetConstraintsResults) - async def GetConstraints(self, entities=None): - """GetConstraints returns the constraints for a given application. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationGetConstraintsResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="GetConstraints", version=17, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResult) - async def Leader(self, tag=None): - """Leader returns the unit name of the leader for the given application. - - tag : str - Returns -> StringResult - """ - if tag is not None and not isinstance(tag, (bytes, str)): - raise Exception(f"Expected tag to be a str, received: {type(tag)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Leader", version=17, params=_params) - _params["tag"] = tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def MergeBindings(self, args=None): - """MergeBindings merges operator-defined bindings with the current bindings for - one or more applications. - - args : typing.Sequence[~ApplicationMergeBindings] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="MergeBindings", version=17, params=_params - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ResolveUnitErrors(self, all_=None, retry=None, tags=None): - """ResolveUnitErrors marks errors on the specified units as resolved. - - all_ : bool - retry : bool - tags : Entities - Returns -> ErrorResults - """ - if all_ is not None and not isinstance(all_, bool): - raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") - - if retry is not None and not isinstance(retry, bool): - raise Exception(f"Expected retry to be a bool, received: {type(retry)}") - - if tags is not None and not isinstance(tags, (dict, Entities)): - raise Exception(f"Expected tags to be a Entities, received: {type(tags)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="ResolveUnitErrors", version=17, params=_params - ) - _params["all"] = all_ - _params["retry"] = retry - _params["tags"] = tags - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ScaleApplicationResults) - async def ScaleApplications(self, applications=None): - """ScaleApplications scales the specified application to the requested number of units. - - applications : typing.Sequence[~ScaleApplicationParams] - Returns -> ScaleApplicationResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="ScaleApplications", version=17, params=_params - ) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def SetCharm( - self, - application=None, - channel=None, - charm_origin=None, - charm_url=None, - config_settings=None, - config_settings_yaml=None, - endpoint_bindings=None, - force=None, - force_base=None, - force_units=None, - generation=None, - resource_ids=None, - storage_constraints=None, - ): - """SetCharm sets the charm for a given for the application. - - application : str - channel : str - charm_origin : CharmOrigin - charm_url : str - config_settings : typing.Mapping[str, str] - config_settings_yaml : str - endpoint_bindings : typing.Mapping[str, str] - force : bool - force_base : bool - force_units : bool - generation : str - resource_ids : typing.Mapping[str, str] - storage_constraints : typing.Mapping[str, ~StorageConstraints] - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if channel is not None and not isinstance(channel, (bytes, str)): - raise Exception(f"Expected channel to be a str, received: {type(channel)}") - - if charm_origin is not None and not isinstance( - charm_origin, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" - ) - - if charm_url is not None and not isinstance(charm_url, (bytes, str)): - raise Exception( - f"Expected charm_url to be a str, received: {type(charm_url)}" - ) - - if config_settings is not None and not isinstance(config_settings, dict): - raise Exception( - f"Expected config_settings to be a Mapping, received: {type(config_settings)}" - ) - - if config_settings_yaml is not None and not isinstance( - config_settings_yaml, (bytes, str) - ): - raise Exception( - f"Expected config_settings_yaml to be a str, received: {type(config_settings_yaml)}" - ) - - if endpoint_bindings is not None and not isinstance(endpoint_bindings, dict): - raise Exception( - f"Expected endpoint_bindings to be a Mapping, received: {type(endpoint_bindings)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if force_base is not None and not isinstance(force_base, bool): - raise Exception( - f"Expected force_base to be a bool, received: {type(force_base)}" - ) - - if force_units is not None and not isinstance(force_units, bool): - raise Exception( - f"Expected force_units to be a bool, received: {type(force_units)}" - ) - - if generation is not None and not isinstance(generation, (bytes, str)): - raise Exception( - f"Expected generation to be a str, received: {type(generation)}" - ) - - if resource_ids is not None and not isinstance(resource_ids, dict): - raise Exception( - f"Expected resource_ids to be a Mapping, received: {type(resource_ids)}" - ) - - if storage_constraints is not None and not isinstance( - storage_constraints, dict - ): - raise Exception( - f"Expected storage_constraints to be a Mapping, received: {type(storage_constraints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="SetCharm", version=17, params=_params) - _params["application"] = application - _params["channel"] = channel - _params["charm-origin"] = charm_origin - _params["charm-url"] = charm_url - _params["config-settings"] = config_settings - _params["config-settings-yaml"] = config_settings_yaml - _params["endpoint-bindings"] = endpoint_bindings - _params["force"] = force - _params["force-base"] = force_base - _params["force-units"] = force_units - _params["generation"] = generation - _params["resource-ids"] = resource_ids - _params["storage-constraints"] = storage_constraints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetConfigs(self, args=None): - """SetConfigs implements the server side of Application.SetConfig. Both - application and charm config are set. It does not unset values in - Config map that are set to an empty string. Unset should be used for that. - - args : typing.Sequence[~ConfigSet] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="SetConfigs", version=17, params=_params) - _params["Args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def SetConstraints(self, application=None, constraints=None): - """SetConstraints sets the constraints for a given application. - - application : str - constraints : Value - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if constraints is not None and not isinstance(constraints, (dict, Value)): - raise Exception( - f"Expected constraints to be a Value, received: {type(constraints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="SetConstraints", version=17, params=_params - ) - _params["application"] = application - _params["constraints"] = constraints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetMetricCredentials(self, creds=None): - """SetMetricCredentials sets credentials on the application. - - creds : typing.Sequence[~ApplicationMetricCredential] - Returns -> ErrorResults - """ - if creds is not None and not isinstance(creds, (bytes, str, list)): - raise Exception(f"Expected creds to be a Sequence, received: {type(creds)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="SetMetricCredentials", - version=17, - params=_params, - ) - _params["creds"] = creds - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetRelationsSuspended(self, args=None): - """SetRelationsSuspended sets the suspended status of the specified relations. - - args : typing.Sequence[~RelationSuspendedArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="SetRelationsSuspended", - version=17, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def Unexpose(self, application=None, exposed_endpoints=None): - """Unexpose changes the juju-managed firewall to unexpose any ports that - were also explicitly marked by units as open. - - application : str - exposed_endpoints : typing.Sequence[str] - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if exposed_endpoints is not None and not isinstance( - exposed_endpoints, (bytes, str, list) - ): - raise Exception( - f"Expected exposed_endpoints to be a Sequence, received: {type(exposed_endpoints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Unexpose", version=17, params=_params) - _params["application"] = application - _params["exposed-endpoints"] = exposed_endpoints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UnitInfoResults) - async def UnitsInfo(self, entities=None): - """UnitsInfo returns unit information for the given entities (units or - applications). - - entities : typing.Sequence[~Entity] - Returns -> UnitInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="UnitsInfo", version=17, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UnsetApplicationsConfig(self, args=None): - """UnsetApplicationsConfig implements the server side of Application.UnsetApplicationsConfig. - - args : typing.Sequence[~ApplicationUnset] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="UnsetApplicationsConfig", - version=17, - params=_params, - ) - _params["Args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UpdateApplicationBase(self, args=None): - """UpdateApplicationBase updates the application base. - Base for subordinates is updated too. - - args : typing.Sequence[~UpdateChannelArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="UpdateApplicationBase", - version=17, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client19.py b/build/lib/juju/client/_client19.py deleted file mode 100644 index 6437cccd..00000000 --- a/build/lib/juju/client/_client19.py +++ /dev/null @@ -1,845 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class ApplicationFacade(Type): - name = "Application" - version = 19 - - @ReturnMapping(AddRelationResults) - async def AddRelation(self, endpoints=None, via_cidrs=None): - """AddRelation adds a relation between the specified endpoints and returns the relation info. - - endpoints : typing.Sequence[str] - via_cidrs : typing.Sequence[str] - Returns -> AddRelationResults - """ - if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): - raise Exception( - f"Expected endpoints to be a Sequence, received: {type(endpoints)}" - ) - - if via_cidrs is not None and not isinstance(via_cidrs, (bytes, str, list)): - raise Exception( - f"Expected via_cidrs to be a Sequence, received: {type(via_cidrs)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="AddRelation", version=19, params=_params - ) - _params["endpoints"] = endpoints - _params["via-cidrs"] = via_cidrs - reply = await self.rpc(msg) - return reply - - @ReturnMapping(AddApplicationUnitsResults) - async def AddUnits( - self, - application=None, - attach_storage=None, - num_units=None, - placement=None, - policy=None, - ): - """AddUnits adds a given number of units to an application. - - application : str - attach_storage : typing.Sequence[str] - num_units : int - placement : typing.Sequence[~Placement] - policy : str - Returns -> AddApplicationUnitsResults - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if attach_storage is not None and not isinstance( - attach_storage, (bytes, str, list) - ): - raise Exception( - f"Expected attach_storage to be a Sequence, received: {type(attach_storage)}" - ) - - if num_units is not None and not isinstance(num_units, int): - raise Exception( - f"Expected num_units to be a int, received: {type(num_units)}" - ) - - if placement is not None and not isinstance(placement, (bytes, str, list)): - raise Exception( - f"Expected placement to be a Sequence, received: {type(placement)}" - ) - - if policy is not None and not isinstance(policy, (bytes, str)): - raise Exception(f"Expected policy to be a str, received: {type(policy)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="AddUnits", version=19, params=_params) - _params["application"] = application - _params["attach-storage"] = attach_storage - _params["num-units"] = num_units - _params["placement"] = placement - _params["policy"] = policy - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationInfoResults) - async def ApplicationsInfo(self, entities=None): - """ApplicationsInfo returns applications information. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="ApplicationsInfo", version=19, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetConfigResults) - async def CharmConfig(self, args=None): - """CharmConfig returns charm config for the input list of applications and - model generations. - - args : typing.Sequence[~ApplicationGet] - Returns -> ApplicationGetConfigResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="CharmConfig", version=19, params=_params - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationCharmRelationsResults) - async def CharmRelations(self, application=None): - """CharmRelations implements the server side of Application.CharmRelations. - - application : str - Returns -> ApplicationCharmRelationsResults - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="CharmRelations", version=19, params=_params - ) - _params["application"] = application - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Consume(self, args=None): - """Consume adds remote applications to the model without creating any - relations. - - args : typing.Sequence[~ConsumeApplicationArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Consume", version=19, params=_params) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Deploy(self, applications=None): - """Deploy fetches the charms from the charm store and deploys them - using the specified placement directives. - - applications : typing.Sequence[~ApplicationDeploy] - Returns -> ErrorResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Deploy", version=19, params=_params) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DeployFromRepositoryResults) - async def DeployFromRepository(self, args=None): - """DeployFromRepository is a one-stop deployment method for repository - charms. Only a charm name is required to deploy. If argument validation - fails, a list of all errors found in validation will be returned. If a - local resource is provided, details required for uploading the validated - resource will be returned. - - args : typing.Sequence[~DeployFromRepositoryArg] - Returns -> DeployFromRepositoryResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="DeployFromRepository", - version=19, - params=_params, - ) - _params["Args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DestroyApplicationResults) - async def DestroyApplication(self, applications=None): - """DestroyApplication removes a given set of applications. - - applications : typing.Sequence[~DestroyApplicationParams] - Returns -> DestroyApplicationResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="DestroyApplication", version=19, params=_params - ) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DestroyConsumedApplications(self, applications=None): - """DestroyConsumedApplications removes a given set of consumed (remote) applications. - - applications : typing.Sequence[~DestroyConsumedApplicationParams] - Returns -> ErrorResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="DestroyConsumedApplications", - version=19, - params=_params, - ) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def DestroyRelation( - self, endpoints=None, force=None, max_wait=None, relation_id=None - ): - """DestroyRelation removes the relation between the - specified endpoints or an id. - - endpoints : typing.Sequence[str] - force : bool - max_wait : int - relation_id : int - Returns -> None - """ - if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): - raise Exception( - f"Expected endpoints to be a Sequence, received: {type(endpoints)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if max_wait is not None and not isinstance(max_wait, int): - raise Exception( - f"Expected max_wait to be a int, received: {type(max_wait)}" - ) - - if relation_id is not None and not isinstance(relation_id, int): - raise Exception( - f"Expected relation_id to be a int, received: {type(relation_id)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="DestroyRelation", version=19, params=_params - ) - _params["endpoints"] = endpoints - _params["force"] = force - _params["max-wait"] = max_wait - _params["relation-id"] = relation_id - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DestroyUnitResults) - async def DestroyUnit(self, units=None): - """DestroyUnit removes a given set of application units. - - units : typing.Sequence[~DestroyUnitParams] - Returns -> DestroyUnitResults - """ - if units is not None and not isinstance(units, (bytes, str, list)): - raise Exception(f"Expected units to be a Sequence, received: {type(units)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="DestroyUnit", version=19, params=_params - ) - _params["units"] = units - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def Expose(self, application=None, exposed_endpoints=None): - """Expose changes the juju-managed firewall to expose any ports that - were also explicitly marked by units as open. - - application : str - exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if exposed_endpoints is not None and not isinstance(exposed_endpoints, dict): - raise Exception( - f"Expected exposed_endpoints to be a Mapping, received: {type(exposed_endpoints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Expose", version=19, params=_params) - _params["application"] = application - _params["exposed-endpoints"] = exposed_endpoints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetResults) - async def Get(self, application=None, branch=None): - """Get returns the charm configuration for an application. - - application : str - branch : str - Returns -> ApplicationGetResults - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Get", version=19, params=_params) - _params["application"] = application - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CharmURLOriginResult) - async def GetCharmURLOrigin(self, application=None, branch=None): - """GetCharmURLOrigin returns the charm URL and charm origin the given - application is running at present. - - application : str - branch : str - Returns -> CharmURLOriginResult - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="GetCharmURLOrigin", version=19, params=_params - ) - _params["application"] = application - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetConfigResults) - async def GetConfig(self, entities=None): - """GetConfig returns the charm config for each of the input applications. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationGetConfigResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="GetConfig", version=19, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetConstraintsResults) - async def GetConstraints(self, entities=None): - """GetConstraints returns the constraints for a given application. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationGetConstraintsResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="GetConstraints", version=19, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResult) - async def Leader(self, tag=None): - """Leader returns the unit name of the leader for the given application. - - tag : str - Returns -> StringResult - """ - if tag is not None and not isinstance(tag, (bytes, str)): - raise Exception(f"Expected tag to be a str, received: {type(tag)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Leader", version=19, params=_params) - _params["tag"] = tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def MergeBindings(self, args=None): - """MergeBindings merges operator-defined bindings with the current bindings for - one or more applications. - - args : typing.Sequence[~ApplicationMergeBindings] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="MergeBindings", version=19, params=_params - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ResolveUnitErrors(self, all_=None, retry=None, tags=None): - """ResolveUnitErrors marks errors on the specified units as resolved. - - all_ : bool - retry : bool - tags : Entities - Returns -> ErrorResults - """ - if all_ is not None and not isinstance(all_, bool): - raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") - - if retry is not None and not isinstance(retry, bool): - raise Exception(f"Expected retry to be a bool, received: {type(retry)}") - - if tags is not None and not isinstance(tags, (dict, Entities)): - raise Exception(f"Expected tags to be a Entities, received: {type(tags)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="ResolveUnitErrors", version=19, params=_params - ) - _params["all"] = all_ - _params["retry"] = retry - _params["tags"] = tags - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ScaleApplicationResults) - async def ScaleApplications(self, applications=None): - """ScaleApplications scales the specified application to the requested number of units. - - applications : typing.Sequence[~ScaleApplicationParams] - Returns -> ScaleApplicationResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="ScaleApplications", version=19, params=_params - ) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def SetCharm( - self, - application=None, - channel=None, - charm_origin=None, - charm_url=None, - config_settings=None, - config_settings_yaml=None, - endpoint_bindings=None, - force=None, - force_base=None, - force_units=None, - generation=None, - resource_ids=None, - storage_constraints=None, - ): - """SetCharm sets the charm for a given for the application. - - application : str - channel : str - charm_origin : CharmOrigin - charm_url : str - config_settings : typing.Mapping[str, str] - config_settings_yaml : str - endpoint_bindings : typing.Mapping[str, str] - force : bool - force_base : bool - force_units : bool - generation : str - resource_ids : typing.Mapping[str, str] - storage_constraints : typing.Mapping[str, ~StorageConstraints] - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if channel is not None and not isinstance(channel, (bytes, str)): - raise Exception(f"Expected channel to be a str, received: {type(channel)}") - - if charm_origin is not None and not isinstance( - charm_origin, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" - ) - - if charm_url is not None and not isinstance(charm_url, (bytes, str)): - raise Exception( - f"Expected charm_url to be a str, received: {type(charm_url)}" - ) - - if config_settings is not None and not isinstance(config_settings, dict): - raise Exception( - f"Expected config_settings to be a Mapping, received: {type(config_settings)}" - ) - - if config_settings_yaml is not None and not isinstance( - config_settings_yaml, (bytes, str) - ): - raise Exception( - f"Expected config_settings_yaml to be a str, received: {type(config_settings_yaml)}" - ) - - if endpoint_bindings is not None and not isinstance(endpoint_bindings, dict): - raise Exception( - f"Expected endpoint_bindings to be a Mapping, received: {type(endpoint_bindings)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if force_base is not None and not isinstance(force_base, bool): - raise Exception( - f"Expected force_base to be a bool, received: {type(force_base)}" - ) - - if force_units is not None and not isinstance(force_units, bool): - raise Exception( - f"Expected force_units to be a bool, received: {type(force_units)}" - ) - - if generation is not None and not isinstance(generation, (bytes, str)): - raise Exception( - f"Expected generation to be a str, received: {type(generation)}" - ) - - if resource_ids is not None and not isinstance(resource_ids, dict): - raise Exception( - f"Expected resource_ids to be a Mapping, received: {type(resource_ids)}" - ) - - if storage_constraints is not None and not isinstance( - storage_constraints, dict - ): - raise Exception( - f"Expected storage_constraints to be a Mapping, received: {type(storage_constraints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="SetCharm", version=19, params=_params) - _params["application"] = application - _params["channel"] = channel - _params["charm-origin"] = charm_origin - _params["charm-url"] = charm_url - _params["config-settings"] = config_settings - _params["config-settings-yaml"] = config_settings_yaml - _params["endpoint-bindings"] = endpoint_bindings - _params["force"] = force - _params["force-base"] = force_base - _params["force-units"] = force_units - _params["generation"] = generation - _params["resource-ids"] = resource_ids - _params["storage-constraints"] = storage_constraints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetConfigs(self, args=None): - """SetConfigs implements the server side of Application.SetConfig. Both - application and charm config are set. It does not unset values in - Config map that are set to an empty string. Unset should be used for that. - - args : typing.Sequence[~ConfigSet] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="SetConfigs", version=19, params=_params) - _params["Args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def SetConstraints(self, application=None, constraints=None): - """SetConstraints sets the constraints for a given application. - - application : str - constraints : Value - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if constraints is not None and not isinstance(constraints, (dict, Value)): - raise Exception( - f"Expected constraints to be a Value, received: {type(constraints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="SetConstraints", version=19, params=_params - ) - _params["application"] = application - _params["constraints"] = constraints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetMetricCredentials(self, creds=None): - """SetMetricCredentials sets credentials on the application. - TODO (cderici) only used for metered charms in cmd MeteredDeployAPI, - kept for client compatibility, remove in juju 4.0 - - creds : typing.Sequence[~ApplicationMetricCredential] - Returns -> ErrorResults - """ - if creds is not None and not isinstance(creds, (bytes, str, list)): - raise Exception(f"Expected creds to be a Sequence, received: {type(creds)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="SetMetricCredentials", - version=19, - params=_params, - ) - _params["creds"] = creds - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetRelationsSuspended(self, args=None): - """SetRelationsSuspended sets the suspended status of the specified relations. - - args : typing.Sequence[~RelationSuspendedArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="SetRelationsSuspended", - version=19, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def Unexpose(self, application=None, exposed_endpoints=None): - """Unexpose changes the juju-managed firewall to unexpose any ports that - were also explicitly marked by units as open. - - application : str - exposed_endpoints : typing.Sequence[str] - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if exposed_endpoints is not None and not isinstance( - exposed_endpoints, (bytes, str, list) - ): - raise Exception( - f"Expected exposed_endpoints to be a Sequence, received: {type(exposed_endpoints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Unexpose", version=19, params=_params) - _params["application"] = application - _params["exposed-endpoints"] = exposed_endpoints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UnitInfoResults) - async def UnitsInfo(self, entities=None): - """UnitsInfo returns unit information for the given entities (units or - applications). - - entities : typing.Sequence[~Entity] - Returns -> UnitInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="UnitsInfo", version=19, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UnsetApplicationsConfig(self, args=None): - """UnsetApplicationsConfig implements the server side of Application.UnsetApplicationsConfig. - - args : typing.Sequence[~ApplicationUnset] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="UnsetApplicationsConfig", - version=19, - params=_params, - ) - _params["Args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UpdateApplicationBase(self, args=None): - """UpdateApplicationBase updates the application base. - Base for subordinates is updated too. - - args : typing.Sequence[~UpdateChannelArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="UpdateApplicationBase", - version=19, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client2.py b/build/lib/juju/client/_client2.py deleted file mode 100644 index 91cf26f2..00000000 --- a/build/lib/juju/client/_client2.py +++ /dev/null @@ -1,326 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class AnnotationsFacade(Type): - name = "Annotations" - version = 2 - - @ReturnMapping(AnnotationsGetResults) - async def Get(self, entities=None): - """Get returns annotations for given entities. - If annotations cannot be retrieved for a given entity, an error is returned. - Each entity is treated independently and, hence, will fail or succeed independently. - - entities : typing.Sequence[~Entity] - Returns -> AnnotationsGetResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Annotations", request="Get", version=2, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Set(self, annotations=None): - """Set stores annotations for given entities - - annotations : typing.Sequence[~EntityAnnotations] - Returns -> ErrorResults - """ - if annotations is not None and not isinstance(annotations, (bytes, str, list)): - raise Exception( - f"Expected annotations to be a Sequence, received: {type(annotations)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Annotations", request="Set", version=2, params=_params) - _params["annotations"] = annotations - reply = await self.rpc(msg) - return reply - - -class BlockFacade(Type): - name = "Block" - version = 2 - - @ReturnMapping(BlockResults) - async def List(self): - """List implements Block.List(). - - Returns -> BlockResults - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Block", request="List", version=2, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResult) - async def SwitchBlockOff(self, message=None, type_=None): - """SwitchBlockOff implements Block.SwitchBlockOff(). - - message : str - type_ : str - Returns -> ErrorResult - """ - if message is not None and not isinstance(message, (bytes, str)): - raise Exception(f"Expected message to be a str, received: {type(message)}") - - if type_ is not None and not isinstance(type_, (bytes, str)): - raise Exception(f"Expected type_ to be a str, received: {type(type_)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Block", request="SwitchBlockOff", version=2, params=_params) - _params["message"] = message - _params["type"] = type_ - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResult) - async def SwitchBlockOn(self, message=None, type_=None): - """SwitchBlockOn implements Block.SwitchBlockOn(). - - message : str - type_ : str - Returns -> ErrorResult - """ - if message is not None and not isinstance(message, (bytes, str)): - raise Exception(f"Expected message to be a str, received: {type(message)}") - - if type_ is not None and not isinstance(type_, (bytes, str)): - raise Exception(f"Expected type_ to be a str, received: {type(type_)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Block", request="SwitchBlockOn", version=2, params=_params) - _params["message"] = message - _params["type"] = type_ - reply = await self.rpc(msg) - return reply - - -class HighAvailabilityFacade(Type): - name = "HighAvailability" - version = 2 - - @ReturnMapping(ControllersChangeResults) - async def EnableHA(self, specs=None): - """EnableHA adds controller machines as necessary to ensure the - controller has the number of machines specified. - - specs : typing.Sequence[~ControllersSpec] - Returns -> ControllersChangeResults - """ - if specs is not None and not isinstance(specs, (bytes, str, list)): - raise Exception(f"Expected specs to be a Sequence, received: {type(specs)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="HighAvailability", request="EnableHA", version=2, params=_params - ) - _params["specs"] = specs - reply = await self.rpc(msg) - return reply - - -class MetricsDebugFacade(Type): - name = "MetricsDebug" - version = 2 - - @ReturnMapping(MetricResults) - async def GetMetrics(self, entities=None): - """GetMetrics returns all metrics stored by the state server. - - entities : typing.Sequence[~Entity] - Returns -> MetricResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="MetricsDebug", request="GetMetrics", version=2, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetMeterStatus(self, statues=None): - """SetMeterStatus sets meter statuses for entities. - - statues : typing.Sequence[~MeterStatusParam] - Returns -> ErrorResults - """ - if statues is not None and not isinstance(statues, (bytes, str, list)): - raise Exception( - f"Expected statues to be a Sequence, received: {type(statues)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="MetricsDebug", request="SetMeterStatus", version=2, params=_params - ) - _params["statues"] = statues - reply = await self.rpc(msg) - return reply - - -class SecretsFacade(Type): - name = "Secrets" - version = 2 - - @ReturnMapping(StringResults) - async def CreateSecrets(self, args=None): - """CreateSecrets creates new secrets. - - args : typing.Sequence[~CreateSecretArg] - Returns -> StringResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Secrets", request="CreateSecrets", version=2, params=_params) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def GrantSecret(self, applications=None, label=None, uri=None): - """GrantSecret grants access to a user secret. - - applications : typing.Sequence[str] - label : str - uri : str - Returns -> ErrorResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - if label is not None and not isinstance(label, (bytes, str)): - raise Exception(f"Expected label to be a str, received: {type(label)}") - - if uri is not None and not isinstance(uri, (bytes, str)): - raise Exception(f"Expected uri to be a str, received: {type(uri)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Secrets", request="GrantSecret", version=2, params=_params) - _params["applications"] = applications - _params["label"] = label - _params["uri"] = uri - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ListSecretResults) - async def ListSecrets(self, filter_=None, show_secrets=None): - """ListSecrets lists available secrets. - - filter_ : SecretsFilter - show_secrets : bool - Returns -> ListSecretResults - """ - if filter_ is not None and not isinstance(filter_, (dict, SecretsFilter)): - raise Exception( - f"Expected filter_ to be a SecretsFilter, received: {type(filter_)}" - ) - - if show_secrets is not None and not isinstance(show_secrets, bool): - raise Exception( - f"Expected show_secrets to be a bool, received: {type(show_secrets)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Secrets", request="ListSecrets", version=2, params=_params) - _params["filter"] = filter_ - _params["show-secrets"] = show_secrets - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def RemoveSecrets(self, args=None): - """RemoveSecrets remove user secret. - - args : typing.Sequence[~DeleteSecretArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Secrets", request="RemoveSecrets", version=2, params=_params) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def RevokeSecret(self, applications=None, label=None, uri=None): - """RevokeSecret revokes access to a user secret. - - applications : typing.Sequence[str] - label : str - uri : str - Returns -> ErrorResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - if label is not None and not isinstance(label, (bytes, str)): - raise Exception(f"Expected label to be a str, received: {type(label)}") - - if uri is not None and not isinstance(uri, (bytes, str)): - raise Exception(f"Expected uri to be a str, received: {type(uri)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Secrets", request="RevokeSecret", version=2, params=_params) - _params["applications"] = applications - _params["label"] = label - _params["uri"] = uri - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UpdateSecrets(self, args=None): - """UpdateSecrets creates new secrets. - - args : typing.Sequence[~UpdateUserSecretArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Secrets", request="UpdateSecrets", version=2, params=_params) - _params["args"] = args - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client20.py b/build/lib/juju/client/_client20.py deleted file mode 100644 index 846ab264..00000000 --- a/build/lib/juju/client/_client20.py +++ /dev/null @@ -1,845 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class ApplicationFacade(Type): - name = "Application" - version = 20 - - @ReturnMapping(AddRelationResults) - async def AddRelation(self, endpoints=None, via_cidrs=None): - """AddRelation adds a relation between the specified endpoints and returns the relation info. - - endpoints : typing.Sequence[str] - via_cidrs : typing.Sequence[str] - Returns -> AddRelationResults - """ - if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): - raise Exception( - f"Expected endpoints to be a Sequence, received: {type(endpoints)}" - ) - - if via_cidrs is not None and not isinstance(via_cidrs, (bytes, str, list)): - raise Exception( - f"Expected via_cidrs to be a Sequence, received: {type(via_cidrs)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="AddRelation", version=20, params=_params - ) - _params["endpoints"] = endpoints - _params["via-cidrs"] = via_cidrs - reply = await self.rpc(msg) - return reply - - @ReturnMapping(AddApplicationUnitsResults) - async def AddUnits( - self, - application=None, - attach_storage=None, - num_units=None, - placement=None, - policy=None, - ): - """AddUnits adds a given number of units to an application. - - application : str - attach_storage : typing.Sequence[str] - num_units : int - placement : typing.Sequence[~Placement] - policy : str - Returns -> AddApplicationUnitsResults - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if attach_storage is not None and not isinstance( - attach_storage, (bytes, str, list) - ): - raise Exception( - f"Expected attach_storage to be a Sequence, received: {type(attach_storage)}" - ) - - if num_units is not None and not isinstance(num_units, int): - raise Exception( - f"Expected num_units to be a int, received: {type(num_units)}" - ) - - if placement is not None and not isinstance(placement, (bytes, str, list)): - raise Exception( - f"Expected placement to be a Sequence, received: {type(placement)}" - ) - - if policy is not None and not isinstance(policy, (bytes, str)): - raise Exception(f"Expected policy to be a str, received: {type(policy)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="AddUnits", version=20, params=_params) - _params["application"] = application - _params["attach-storage"] = attach_storage - _params["num-units"] = num_units - _params["placement"] = placement - _params["policy"] = policy - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationInfoResults) - async def ApplicationsInfo(self, entities=None): - """ApplicationsInfo returns applications information. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="ApplicationsInfo", version=20, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetConfigResults) - async def CharmConfig(self, args=None): - """CharmConfig returns charm config for the input list of applications and - model generations. - - args : typing.Sequence[~ApplicationGet] - Returns -> ApplicationGetConfigResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="CharmConfig", version=20, params=_params - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationCharmRelationsResults) - async def CharmRelations(self, application=None): - """CharmRelations implements the server side of Application.CharmRelations. - - application : str - Returns -> ApplicationCharmRelationsResults - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="CharmRelations", version=20, params=_params - ) - _params["application"] = application - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Consume(self, args=None): - """Consume adds remote applications to the model without creating any - relations. - - args : typing.Sequence[~ConsumeApplicationArgV5] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Consume", version=20, params=_params) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Deploy(self, applications=None): - """Deploy fetches the charms from the charm store and deploys them - using the specified placement directives. - - applications : typing.Sequence[~ApplicationDeploy] - Returns -> ErrorResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Deploy", version=20, params=_params) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DeployFromRepositoryResults) - async def DeployFromRepository(self, args=None): - """DeployFromRepository is a one-stop deployment method for repository - charms. Only a charm name is required to deploy. If argument validation - fails, a list of all errors found in validation will be returned. If a - local resource is provided, details required for uploading the validated - resource will be returned. - - args : typing.Sequence[~DeployFromRepositoryArg] - Returns -> DeployFromRepositoryResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="DeployFromRepository", - version=20, - params=_params, - ) - _params["Args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DestroyApplicationResults) - async def DestroyApplication(self, applications=None): - """DestroyApplication removes a given set of applications. - - applications : typing.Sequence[~DestroyApplicationParams] - Returns -> DestroyApplicationResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="DestroyApplication", version=20, params=_params - ) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DestroyConsumedApplications(self, applications=None): - """DestroyConsumedApplications removes a given set of consumed (remote) applications. - - applications : typing.Sequence[~DestroyConsumedApplicationParams] - Returns -> ErrorResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="DestroyConsumedApplications", - version=20, - params=_params, - ) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def DestroyRelation( - self, endpoints=None, force=None, max_wait=None, relation_id=None - ): - """DestroyRelation removes the relation between the - specified endpoints or an id. - - endpoints : typing.Sequence[str] - force : bool - max_wait : int - relation_id : int - Returns -> None - """ - if endpoints is not None and not isinstance(endpoints, (bytes, str, list)): - raise Exception( - f"Expected endpoints to be a Sequence, received: {type(endpoints)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if max_wait is not None and not isinstance(max_wait, int): - raise Exception( - f"Expected max_wait to be a int, received: {type(max_wait)}" - ) - - if relation_id is not None and not isinstance(relation_id, int): - raise Exception( - f"Expected relation_id to be a int, received: {type(relation_id)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="DestroyRelation", version=20, params=_params - ) - _params["endpoints"] = endpoints - _params["force"] = force - _params["max-wait"] = max_wait - _params["relation-id"] = relation_id - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DestroyUnitResults) - async def DestroyUnit(self, units=None): - """DestroyUnit removes a given set of application units. - - units : typing.Sequence[~DestroyUnitParams] - Returns -> DestroyUnitResults - """ - if units is not None and not isinstance(units, (bytes, str, list)): - raise Exception(f"Expected units to be a Sequence, received: {type(units)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="DestroyUnit", version=20, params=_params - ) - _params["units"] = units - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def Expose(self, application=None, exposed_endpoints=None): - """Expose changes the juju-managed firewall to expose any ports that - were also explicitly marked by units as open. - - application : str - exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if exposed_endpoints is not None and not isinstance(exposed_endpoints, dict): - raise Exception( - f"Expected exposed_endpoints to be a Mapping, received: {type(exposed_endpoints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Expose", version=20, params=_params) - _params["application"] = application - _params["exposed-endpoints"] = exposed_endpoints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetResults) - async def Get(self, application=None, branch=None): - """Get returns the charm configuration for an application. - - application : str - branch : str - Returns -> ApplicationGetResults - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Get", version=20, params=_params) - _params["application"] = application - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CharmURLOriginResult) - async def GetCharmURLOrigin(self, application=None, branch=None): - """GetCharmURLOrigin returns the charm URL and charm origin the given - application is running at present. - - application : str - branch : str - Returns -> CharmURLOriginResult - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="GetCharmURLOrigin", version=20, params=_params - ) - _params["application"] = application - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetConfigResults) - async def GetConfig(self, entities=None): - """GetConfig returns the charm config for each of the input applications. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationGetConfigResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="GetConfig", version=20, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationGetConstraintsResults) - async def GetConstraints(self, entities=None): - """GetConstraints returns the constraints for a given application. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationGetConstraintsResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="GetConstraints", version=20, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResult) - async def Leader(self, tag=None): - """Leader returns the unit name of the leader for the given application. - - tag : str - Returns -> StringResult - """ - if tag is not None and not isinstance(tag, (bytes, str)): - raise Exception(f"Expected tag to be a str, received: {type(tag)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Leader", version=20, params=_params) - _params["tag"] = tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def MergeBindings(self, args=None): - """MergeBindings merges operator-defined bindings with the current bindings for - one or more applications. - - args : typing.Sequence[~ApplicationMergeBindings] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="MergeBindings", version=20, params=_params - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ResolveUnitErrors(self, all_=None, retry=None, tags=None): - """ResolveUnitErrors marks errors on the specified units as resolved. - - all_ : bool - retry : bool - tags : Entities - Returns -> ErrorResults - """ - if all_ is not None and not isinstance(all_, bool): - raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") - - if retry is not None and not isinstance(retry, bool): - raise Exception(f"Expected retry to be a bool, received: {type(retry)}") - - if tags is not None and not isinstance(tags, (dict, Entities)): - raise Exception(f"Expected tags to be a Entities, received: {type(tags)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="ResolveUnitErrors", version=20, params=_params - ) - _params["all"] = all_ - _params["retry"] = retry - _params["tags"] = tags - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ScaleApplicationResults) - async def ScaleApplications(self, applications=None): - """ScaleApplications scales the specified application to the requested number of units. - - applications : typing.Sequence[~ScaleApplicationParams] - Returns -> ScaleApplicationResults - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="ScaleApplications", version=20, params=_params - ) - _params["applications"] = applications - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def SetCharm( - self, - application=None, - channel=None, - charm_origin=None, - charm_url=None, - config_settings=None, - config_settings_yaml=None, - endpoint_bindings=None, - force=None, - force_base=None, - force_units=None, - generation=None, - resource_ids=None, - storage_constraints=None, - ): - """SetCharm sets the charm for a given for the application. - - application : str - channel : str - charm_origin : CharmOrigin - charm_url : str - config_settings : typing.Mapping[str, str] - config_settings_yaml : str - endpoint_bindings : typing.Mapping[str, str] - force : bool - force_base : bool - force_units : bool - generation : str - resource_ids : typing.Mapping[str, str] - storage_constraints : typing.Mapping[str, ~StorageConstraints] - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if channel is not None and not isinstance(channel, (bytes, str)): - raise Exception(f"Expected channel to be a str, received: {type(channel)}") - - if charm_origin is not None and not isinstance( - charm_origin, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" - ) - - if charm_url is not None and not isinstance(charm_url, (bytes, str)): - raise Exception( - f"Expected charm_url to be a str, received: {type(charm_url)}" - ) - - if config_settings is not None and not isinstance(config_settings, dict): - raise Exception( - f"Expected config_settings to be a Mapping, received: {type(config_settings)}" - ) - - if config_settings_yaml is not None and not isinstance( - config_settings_yaml, (bytes, str) - ): - raise Exception( - f"Expected config_settings_yaml to be a str, received: {type(config_settings_yaml)}" - ) - - if endpoint_bindings is not None and not isinstance(endpoint_bindings, dict): - raise Exception( - f"Expected endpoint_bindings to be a Mapping, received: {type(endpoint_bindings)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if force_base is not None and not isinstance(force_base, bool): - raise Exception( - f"Expected force_base to be a bool, received: {type(force_base)}" - ) - - if force_units is not None and not isinstance(force_units, bool): - raise Exception( - f"Expected force_units to be a bool, received: {type(force_units)}" - ) - - if generation is not None and not isinstance(generation, (bytes, str)): - raise Exception( - f"Expected generation to be a str, received: {type(generation)}" - ) - - if resource_ids is not None and not isinstance(resource_ids, dict): - raise Exception( - f"Expected resource_ids to be a Mapping, received: {type(resource_ids)}" - ) - - if storage_constraints is not None and not isinstance( - storage_constraints, dict - ): - raise Exception( - f"Expected storage_constraints to be a Mapping, received: {type(storage_constraints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="SetCharm", version=20, params=_params) - _params["application"] = application - _params["channel"] = channel - _params["charm-origin"] = charm_origin - _params["charm-url"] = charm_url - _params["config-settings"] = config_settings - _params["config-settings-yaml"] = config_settings_yaml - _params["endpoint-bindings"] = endpoint_bindings - _params["force"] = force - _params["force-base"] = force_base - _params["force-units"] = force_units - _params["generation"] = generation - _params["resource-ids"] = resource_ids - _params["storage-constraints"] = storage_constraints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetConfigs(self, args=None): - """SetConfigs implements the server side of Application.SetConfig. Both - application and charm config are set. It does not unset values in - Config map that are set to an empty string. Unset should be used for that. - - args : typing.Sequence[~ConfigSet] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="SetConfigs", version=20, params=_params) - _params["Args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def SetConstraints(self, application=None, constraints=None): - """SetConstraints sets the constraints for a given application. - - application : str - constraints : Value - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if constraints is not None and not isinstance(constraints, (dict, Value)): - raise Exception( - f"Expected constraints to be a Value, received: {type(constraints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", request="SetConstraints", version=20, params=_params - ) - _params["application"] = application - _params["constraints"] = constraints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetMetricCredentials(self, creds=None): - """SetMetricCredentials sets credentials on the application. - TODO (cderici) only used for metered charms in cmd MeteredDeployAPI, - kept for client compatibility, remove in juju 4.0 - - creds : typing.Sequence[~ApplicationMetricCredential] - Returns -> ErrorResults - """ - if creds is not None and not isinstance(creds, (bytes, str, list)): - raise Exception(f"Expected creds to be a Sequence, received: {type(creds)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="SetMetricCredentials", - version=20, - params=_params, - ) - _params["creds"] = creds - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetRelationsSuspended(self, args=None): - """SetRelationsSuspended sets the suspended status of the specified relations. - - args : typing.Sequence[~RelationSuspendedArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="SetRelationsSuspended", - version=20, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def Unexpose(self, application=None, exposed_endpoints=None): - """Unexpose changes the juju-managed firewall to unexpose any ports that - were also explicitly marked by units as open. - - application : str - exposed_endpoints : typing.Sequence[str] - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if exposed_endpoints is not None and not isinstance( - exposed_endpoints, (bytes, str, list) - ): - raise Exception( - f"Expected exposed_endpoints to be a Sequence, received: {type(exposed_endpoints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="Unexpose", version=20, params=_params) - _params["application"] = application - _params["exposed-endpoints"] = exposed_endpoints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UnitInfoResults) - async def UnitsInfo(self, entities=None): - """UnitsInfo returns unit information for the given entities (units or - applications). - - entities : typing.Sequence[~Entity] - Returns -> UnitInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Application", request="UnitsInfo", version=20, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UnsetApplicationsConfig(self, args=None): - """UnsetApplicationsConfig implements the server side of Application.UnsetApplicationsConfig. - - args : typing.Sequence[~ApplicationUnset] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="UnsetApplicationsConfig", - version=20, - params=_params, - ) - _params["Args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UpdateApplicationBase(self, args=None): - """UpdateApplicationBase updates the application base. - Base for subordinates is updated too. - - args : typing.Sequence[~UpdateChannelArg] - Returns -> ErrorResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Application", - request="UpdateApplicationBase", - version=20, - params=_params, - ) - _params["args"] = args - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client3.py b/build/lib/juju/client/_client3.py deleted file mode 100644 index b119ce03..00000000 --- a/build/lib/juju/client/_client3.py +++ /dev/null @@ -1,605 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class AdminFacade(Type): - name = "Admin" - version = 3 - - @ReturnMapping(LoginResult) - async def Login( - self, - auth_tag=None, - bakery_version=None, - cli_args=None, - client_version=None, - credentials=None, - macaroons=None, - nonce=None, - token=None, - user_data=None, - ): - """Login logs in with the provided credentials. All subsequent requests on the - connection will act as the authenticated user. - - auth_tag : str - bakery_version : int - cli_args : str - client_version : str - credentials : str - macaroons : typing.Sequence[~Macaroon] - nonce : str - token : str - user_data : str - Returns -> LoginResult - """ - if auth_tag is not None and not isinstance(auth_tag, (bytes, str)): - raise Exception( - f"Expected auth_tag to be a str, received: {type(auth_tag)}" - ) - - if bakery_version is not None and not isinstance(bakery_version, int): - raise Exception( - f"Expected bakery_version to be a int, received: {type(bakery_version)}" - ) - - if cli_args is not None and not isinstance(cli_args, (bytes, str)): - raise Exception( - f"Expected cli_args to be a str, received: {type(cli_args)}" - ) - - if client_version is not None and not isinstance(client_version, (bytes, str)): - raise Exception( - f"Expected client_version to be a str, received: {type(client_version)}" - ) - - if credentials is not None and not isinstance(credentials, (bytes, str)): - raise Exception( - f"Expected credentials to be a str, received: {type(credentials)}" - ) - - if macaroons is not None and not isinstance(macaroons, (bytes, str, list)): - raise Exception( - f"Expected macaroons to be a Sequence, received: {type(macaroons)}" - ) - - if nonce is not None and not isinstance(nonce, (bytes, str)): - raise Exception(f"Expected nonce to be a str, received: {type(nonce)}") - - if token is not None and not isinstance(token, (bytes, str)): - raise Exception(f"Expected token to be a str, received: {type(token)}") - - if user_data is not None and not isinstance(user_data, (bytes, str)): - raise Exception( - f"Expected user_data to be a str, received: {type(user_data)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Admin", request="Login", version=3, params=_params) - _params["auth-tag"] = auth_tag - _params["bakery-version"] = bakery_version - _params["cli-args"] = cli_args - _params["client-version"] = client_version - _params["credentials"] = credentials - _params["macaroons"] = macaroons - _params["nonce"] = nonce - _params["token"] = token - _params["user-data"] = user_data - reply = await self.rpc(msg) - return reply - - @ReturnMapping(RedirectInfoResult) - async def RedirectInfo(self): - """RedirectInfo returns redirected host information for the model. - In Juju it always returns an error because the Juju controller - does not multiplex controllers. - - Returns -> RedirectInfoResult - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Admin", request="RedirectInfo", version=3, params=_params) - - reply = await self.rpc(msg) - return reply - - -class AllWatcherFacade(Type): - name = "AllWatcher" - version = 3 - - @ReturnMapping(AllWatcherNextResults) - async def Next(self): - """Next will return the current state of everything on the first call - and subsequent calls will - - Returns -> AllWatcherNextResults - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="AllWatcher", request="Next", version=3, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def Stop(self): - """Stop stops the watcher. - - Returns -> None - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="AllWatcher", request="Stop", version=3, params=_params) - - reply = await self.rpc(msg) - return reply - - async def rpc(self, msg): - """Patch rpc method to add Id.""" - if not hasattr(self, "Id"): - raise RuntimeError('Missing "Id" field') - msg["Id"] = id - - from .facade import TypeEncoder - - reply = await self.connection.rpc(msg, encoder=TypeEncoder) - return reply - - -class BackupsFacade(Type): - name = "Backups" - version = 3 - - @ReturnMapping(BackupsMetadataResult) - async def Create(self, no_download=None, notes=None): - """Create is the API method that requests juju to create a new backup - of its state. - - no_download : bool - notes : str - Returns -> BackupsMetadataResult - """ - if no_download is not None and not isinstance(no_download, bool): - raise Exception( - f"Expected no_download to be a bool, received: {type(no_download)}" - ) - - if notes is not None and not isinstance(notes, (bytes, str)): - raise Exception(f"Expected notes to be a str, received: {type(notes)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Backups", request="Create", version=3, params=_params) - _params["no-download"] = no_download - _params["notes"] = notes - reply = await self.rpc(msg) - return reply - - -class ModelConfigFacade(Type): - name = "ModelConfig" - version = 3 - - @ReturnMapping(GetConstraintsResults) - async def GetModelConstraints(self): - """GetModelConstraints returns the constraints for the model. - - Returns -> GetConstraintsResults - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelConfig", request="GetModelConstraints", version=3, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelConfigResults) - async def ModelGet(self): - """ModelGet implements the server-side part of the - model-config CLI command. - - Returns -> ModelConfigResults - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelConfig", request="ModelGet", version=3, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def ModelSet(self, config=None): - """ModelSet implements the server-side part of the - set-model-config CLI command. - - config : typing.Mapping[str, typing.Any] - Returns -> None - """ - if config is not None and not isinstance(config, dict): - raise Exception( - f"Expected config to be a Mapping, received: {type(config)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelConfig", request="ModelSet", version=3, params=_params) - _params["config"] = config - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def ModelUnset(self, keys=None): - """ModelUnset implements the server-side part of the - set-model-config CLI command. - - keys : typing.Sequence[str] - Returns -> None - """ - if keys is not None and not isinstance(keys, (bytes, str, list)): - raise Exception(f"Expected keys to be a Sequence, received: {type(keys)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelConfig", request="ModelUnset", version=3, params=_params) - _params["keys"] = keys - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResult) - async def SLALevel(self): - """SLALevel returns the current sla level for the model. - - Returns -> StringResult - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelConfig", request="SLALevel", version=3, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelSequencesResult) - async def Sequences(self): - """Sequences returns the model's sequence names and next values. - - Returns -> ModelSequencesResult - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelConfig", request="Sequences", version=3, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def SetModelConstraints(self, application=None, constraints=None): - """SetModelConstraints sets the constraints for the model. - - application : str - constraints : Value - Returns -> None - """ - if application is not None and not isinstance(application, (bytes, str)): - raise Exception( - f"Expected application to be a str, received: {type(application)}" - ) - - if constraints is not None and not isinstance(constraints, (dict, Value)): - raise Exception( - f"Expected constraints to be a Value, received: {type(constraints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelConfig", request="SetModelConstraints", version=3, params=_params - ) - _params["application"] = application - _params["constraints"] = constraints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def SetSLALevel(self, modelslainfo=None, creds=None, level=None, owner=None): - """SetSLALevel sets the sla level on the model. - - modelslainfo : ModelSLAInfo - creds : typing.Sequence[int] - level : str - owner : str - Returns -> None - """ - if modelslainfo is not None and not isinstance( - modelslainfo, (dict, ModelSLAInfo) - ): - raise Exception( - f"Expected modelslainfo to be a ModelSLAInfo, received: {type(modelslainfo)}" - ) - - if creds is not None and not isinstance(creds, (bytes, str, list)): - raise Exception(f"Expected creds to be a Sequence, received: {type(creds)}") - - if level is not None and not isinstance(level, (bytes, str)): - raise Exception(f"Expected level to be a str, received: {type(level)}") - - if owner is not None and not isinstance(owner, (bytes, str)): - raise Exception(f"Expected owner to be a str, received: {type(owner)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelConfig", request="SetSLALevel", version=3, params=_params) - _params["ModelSLAInfo"] = modelslainfo - _params["creds"] = creds - _params["level"] = level - _params["owner"] = owner - reply = await self.rpc(msg) - return reply - - -class ResourcesFacade(Type): - name = "Resources" - version = 3 - - @ReturnMapping(AddPendingResourcesResult) - async def AddPendingResources( - self, - entity=None, - charm_origin=None, - macaroon=None, - resources=None, - tag=None, - url=None, - ): - """AddPendingResources adds the provided resources (info) to the Juju - model in a pending state, meaning they are not available until - resolved. Handles CharmHub and Local charms. - - entity : Entity - charm_origin : CharmOrigin - macaroon : Macaroon - resources : typing.Sequence[~CharmResource] - tag : str - url : str - Returns -> AddPendingResourcesResult - """ - if entity is not None and not isinstance(entity, (dict, Entity)): - raise Exception(f"Expected entity to be a Entity, received: {type(entity)}") - - if charm_origin is not None and not isinstance( - charm_origin, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" - ) - - if macaroon is not None and not isinstance(macaroon, (dict, Macaroon)): - raise Exception( - f"Expected macaroon to be a Macaroon, received: {type(macaroon)}" - ) - - if resources is not None and not isinstance(resources, (bytes, str, list)): - raise Exception( - f"Expected resources to be a Sequence, received: {type(resources)}" - ) - - if tag is not None and not isinstance(tag, (bytes, str)): - raise Exception(f"Expected tag to be a str, received: {type(tag)}") - - if url is not None and not isinstance(url, (bytes, str)): - raise Exception(f"Expected url to be a str, received: {type(url)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Resources", request="AddPendingResources", version=3, params=_params - ) - _params["Entity"] = entity - _params["charm-origin"] = charm_origin - _params["macaroon"] = macaroon - _params["resources"] = resources - _params["tag"] = tag - _params["url"] = url - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ResourcesResults) - async def ListResources(self, entities=None): - """ListResources returns the list of resources for the given application. - - entities : typing.Sequence[~Entity] - Returns -> ResourcesResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Resources", request="ListResources", version=3, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - -class UserManagerFacade(Type): - name = "UserManager" - version = 3 - - @ReturnMapping(AddUserResults) - async def AddUser(self, users=None): - """AddUser adds a user with a username, and either a password or - a randomly generated secret key which will be returned. - - users : typing.Sequence[~AddUser] - Returns -> AddUserResults - """ - if users is not None and not isinstance(users, (bytes, str, list)): - raise Exception(f"Expected users to be a Sequence, received: {type(users)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="UserManager", request="AddUser", version=3, params=_params) - _params["users"] = users - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DisableUser(self, entities=None): - """DisableUser disables one or more users. If the user is already disabled, - the action is considered a success. - - entities : typing.Sequence[~Entity] - Returns -> ErrorResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="UserManager", request="DisableUser", version=3, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def EnableUser(self, entities=None): - """EnableUser enables one or more users. If the user is already enabled, - the action is considered a success. - - entities : typing.Sequence[~Entity] - Returns -> ErrorResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="UserManager", request="EnableUser", version=3, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelUserInfoResults) - async def ModelUserInfo(self, entities=None): - """ModelUserInfo returns information on all users in the model. - - entities : typing.Sequence[~Entity] - Returns -> ModelUserInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="UserManager", request="ModelUserInfo", version=3, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def RemoveUser(self, entities=None): - """RemoveUser permanently removes a user from the current controller for each - entity provided. While the user is permanently removed we keep it's - information around for auditing purposes. - TODO(redir): Add information about getting deleted user information when we - add that capability. - - entities : typing.Sequence[~Entity] - Returns -> ErrorResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="UserManager", request="RemoveUser", version=3, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(AddUserResults) - async def ResetPassword(self, entities=None): - """ResetPassword resets password for supplied users by - invalidating current passwords (if any) and generating - new random secret keys which will be returned. - Users cannot reset their own password. - - entities : typing.Sequence[~Entity] - Returns -> AddUserResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="UserManager", request="ResetPassword", version=3, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetPassword(self, changes=None): - """SetPassword changes the stored password for the specified users. - - changes : typing.Sequence[~EntityPassword] - Returns -> ErrorResults - """ - if changes is not None and not isinstance(changes, (bytes, str, list)): - raise Exception( - f"Expected changes to be a Sequence, received: {type(changes)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="UserManager", request="SetPassword", version=3, params=_params) - _params["changes"] = changes - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UserInfoResults) - async def UserInfo(self, entities=None, include_disabled=None): - """UserInfo returns information on a user. - - entities : typing.Sequence[~Entity] - include_disabled : bool - Returns -> UserInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - if include_disabled is not None and not isinstance(include_disabled, bool): - raise Exception( - f"Expected include_disabled to be a bool, received: {type(include_disabled)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="UserManager", request="UserInfo", version=3, params=_params) - _params["entities"] = entities - _params["include-disabled"] = include_disabled - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client4.py b/build/lib/juju/client/_client4.py deleted file mode 100644 index 0e8c6890..00000000 --- a/build/lib/juju/client/_client4.py +++ /dev/null @@ -1,572 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class AllModelWatcherFacade(Type): - name = "AllModelWatcher" - version = 4 - - @ReturnMapping(AllWatcherNextResults) - async def Next(self): - """Next will return the current state of everything on the first call - and subsequent calls will - - Returns -> AllWatcherNextResults - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="AllModelWatcher", request="Next", version=4, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def Stop(self): - """Stop stops the watcher. - - Returns -> None - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="AllModelWatcher", request="Stop", version=4, params=_params) - - reply = await self.rpc(msg) - return reply - - async def rpc(self, msg): - """Patch rpc method to add Id.""" - if not hasattr(self, "Id"): - raise RuntimeError('Missing "Id" field') - msg["Id"] = id - - from .facade import TypeEncoder - - reply = await self.connection.rpc(msg, encoder=TypeEncoder) - return reply - - -class ApplicationOffersFacade(Type): - name = "ApplicationOffers" - version = 4 - - @ReturnMapping(ApplicationOffersResults) - async def ApplicationOffers(self, bakery_version=None, offer_urls=None): - """ApplicationOffers gets details about remote applications that match given URLs. - - bakery_version : int - offer_urls : typing.Sequence[str] - Returns -> ApplicationOffersResults - """ - if bakery_version is not None and not isinstance(bakery_version, int): - raise Exception( - f"Expected bakery_version to be a int, received: {type(bakery_version)}" - ) - - if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): - raise Exception( - f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="ApplicationOffers", - version=4, - params=_params, - ) - _params["bakery-version"] = bakery_version - _params["offer-urls"] = offer_urls - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DestroyOffers(self, force=None, offer_urls=None): - """DestroyOffers removes the offers specified by the given URLs, forcing if necessary. - - force : bool - offer_urls : typing.Sequence[str] - Returns -> ErrorResults - """ - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): - raise Exception( - f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", request="DestroyOffers", version=4, params=_params - ) - _params["force"] = force - _params["offer-urls"] = offer_urls - reply = await self.rpc(msg) - return reply - - @ReturnMapping(QueryApplicationOffersResults) - async def FindApplicationOffers(self, filters=None): - """FindApplicationOffers gets details about remote applications that match given filter. - - filters : typing.Sequence[~OfferFilter] - Returns -> QueryApplicationOffersResults - """ - if filters is not None and not isinstance(filters, (bytes, str, list)): - raise Exception( - f"Expected filters to be a Sequence, received: {type(filters)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="FindApplicationOffers", - version=4, - params=_params, - ) - _params["Filters"] = filters - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ConsumeOfferDetailsResults) - async def GetConsumeDetails(self, offer_urls=None, user_tag=None): - """GetConsumeDetails returns the details necessary to pass to another model - to allow the specified args user to consume the offers represented by the args URLs. - - offer_urls : OfferURLs - user_tag : str - Returns -> ConsumeOfferDetailsResults - """ - if offer_urls is not None and not isinstance(offer_urls, (dict, OfferURLs)): - raise Exception( - f"Expected offer_urls to be a OfferURLs, received: {type(offer_urls)}" - ) - - if user_tag is not None and not isinstance(user_tag, (bytes, str)): - raise Exception( - f"Expected user_tag to be a str, received: {type(user_tag)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="GetConsumeDetails", - version=4, - params=_params, - ) - _params["offer-urls"] = offer_urls - _params["user-tag"] = user_tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(QueryApplicationOffersResults) - async def ListApplicationOffers(self, filters=None): - """ListApplicationOffers gets deployed details about application offers that match given filter. - The results contain details about the deployed applications such as connection count. - - filters : typing.Sequence[~OfferFilter] - Returns -> QueryApplicationOffersResults - """ - if filters is not None and not isinstance(filters, (bytes, str, list)): - raise Exception( - f"Expected filters to be a Sequence, received: {type(filters)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="ListApplicationOffers", - version=4, - params=_params, - ) - _params["Filters"] = filters - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ModifyOfferAccess(self, changes=None): - """ModifyOfferAccess changes the application offer access granted to users. - - changes : typing.Sequence[~ModifyOfferAccess] - Returns -> ErrorResults - """ - if changes is not None and not isinstance(changes, (bytes, str, list)): - raise Exception( - f"Expected changes to be a Sequence, received: {type(changes)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="ModifyOfferAccess", - version=4, - params=_params, - ) - _params["changes"] = changes - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Offer(self, offers=None): - """Offer makes application endpoints available for consumption at a specified URL. - - offers : typing.Sequence[~AddApplicationOffer] - Returns -> ErrorResults - """ - if offers is not None and not isinstance(offers, (bytes, str, list)): - raise Exception( - f"Expected offers to be a Sequence, received: {type(offers)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="ApplicationOffers", request="Offer", version=4, params=_params) - _params["Offers"] = offers - reply = await self.rpc(msg) - return reply - - @ReturnMapping(RemoteApplicationInfoResults) - async def RemoteApplicationInfo(self, bakery_version=None, offer_urls=None): - """RemoteApplicationInfo returns information about the requested remote application. - This call currently has no client side API, only there for the Dashboard at this stage. - - bakery_version : int - offer_urls : typing.Sequence[str] - Returns -> RemoteApplicationInfoResults - """ - if bakery_version is not None and not isinstance(bakery_version, int): - raise Exception( - f"Expected bakery_version to be a int, received: {type(bakery_version)}" - ) - - if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): - raise Exception( - f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="RemoteApplicationInfo", - version=4, - params=_params, - ) - _params["bakery-version"] = bakery_version - _params["offer-urls"] = offer_urls - reply = await self.rpc(msg) - return reply - - -class ModelGenerationFacade(Type): - name = "ModelGeneration" - version = 4 - - @ReturnMapping(ErrorResult) - async def AbortBranch(self, branch=None): - """AbortBranch aborts the input branch, marking it complete. However no - changes are made applicable to the whole model. No units may be assigned - to the branch when aborting. - - branch : str - Returns -> ErrorResult - """ - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelGeneration", request="AbortBranch", version=4, params=_params - ) - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResult) - async def AddBranch(self, branch=None): - """AddBranch adds a new branch with the input name to the model. - - branch : str - Returns -> ErrorResult - """ - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelGeneration", request="AddBranch", version=4, params=_params - ) - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(BranchResults) - async def BranchInfo(self, branches=None, detailed=None): - """BranchInfo will return details of branch identified by the input argument, - including units on the branch and the configuration disjoint with the - master generation. - An error is returned if no in-flight branch matching in input is found. - - branches : typing.Sequence[str] - detailed : bool - Returns -> BranchResults - """ - if branches is not None and not isinstance(branches, (bytes, str, list)): - raise Exception( - f"Expected branches to be a Sequence, received: {type(branches)}" - ) - - if detailed is not None and not isinstance(detailed, bool): - raise Exception( - f"Expected detailed to be a bool, received: {type(detailed)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelGeneration", request="BranchInfo", version=4, params=_params - ) - _params["branches"] = branches - _params["detailed"] = detailed - reply = await self.rpc(msg) - return reply - - @ReturnMapping(IntResult) - async def CommitBranch(self, branch=None): - """CommitBranch commits the input branch, making its changes applicable to - the whole model and marking it complete. - - branch : str - Returns -> IntResult - """ - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelGeneration", request="CommitBranch", version=4, params=_params - ) - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(BoolResult) - async def HasActiveBranch(self, branch=None): - """HasActiveBranch returns a true result if the input model has an "in-flight" - branch matching the input name. - - branch : str - Returns -> BoolResult - """ - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelGeneration", request="HasActiveBranch", version=4, params=_params - ) - _params["branch"] = branch - reply = await self.rpc(msg) - return reply - - @ReturnMapping(BranchResults) - async def ListCommits(self): - """ListCommits will return the commits, hence only branches with generation_id higher than 0 - - Returns -> BranchResults - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelGeneration", request="ListCommits", version=4, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(GenerationResult) - async def ShowCommit(self, generation_id=None): - """ShowCommit will return details a commit given by its generationId - An error is returned if either no branch can be found corresponding to the generation id. - Or the generation id given is below 1. - - generation_id : int - Returns -> GenerationResult - """ - if generation_id is not None and not isinstance(generation_id, int): - raise Exception( - f"Expected generation_id to be a int, received: {type(generation_id)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelGeneration", request="ShowCommit", version=4, params=_params - ) - _params["generation-id"] = generation_id - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def TrackBranch(self, branch=None, entities=None, num_units=None): - """TrackBranch marks the input units and/or applications as tracking the input - branch, causing them to realise changes made under that branch. - - branch : str - entities : typing.Sequence[~Entity] - num_units : int - Returns -> ErrorResults - """ - if branch is not None and not isinstance(branch, (bytes, str)): - raise Exception(f"Expected branch to be a str, received: {type(branch)}") - - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - if num_units is not None and not isinstance(num_units, int): - raise Exception( - f"Expected num_units to be a int, received: {type(num_units)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelGeneration", request="TrackBranch", version=4, params=_params - ) - _params["branch"] = branch - _params["entities"] = entities - _params["num-units"] = num_units - reply = await self.rpc(msg) - return reply - - -class SSHClientFacade(Type): - name = "SSHClient" - version = 4 - - @ReturnMapping(SSHAddressesResults) - async def AllAddresses(self, entities=None): - """AllAddresses reports all addresses that might have SSH listening for each - entity in args. The result is sorted with public addresses first. - Machines and units are supported as entity types. - - entities : typing.Sequence[~Entity] - Returns -> SSHAddressesResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="SSHClient", request="AllAddresses", version=4, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CloudSpecResult) - async def ModelCredentialForSSH(self): - """ModelCredentialForSSH returns a cloud spec for ssh purpose. - This facade call is only used for k8s model. - - Returns -> CloudSpecResult - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="SSHClient", request="ModelCredentialForSSH", version=4, params=_params - ) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(SSHAddressResults) - async def PrivateAddress(self, entities=None): - """PrivateAddress reports the preferred private network address for one or - more entities. Machines and units are supported. - - entities : typing.Sequence[~Entity] - Returns -> SSHAddressResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="SSHClient", request="PrivateAddress", version=4, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(SSHProxyResult) - async def Proxy(self): - """Proxy returns whether SSH connections should be proxied through the - controller hosts for the model associated with the API connection. - - Returns -> SSHProxyResult - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="SSHClient", request="Proxy", version=4, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(SSHAddressResults) - async def PublicAddress(self, entities=None): - """PublicAddress reports the preferred public network address for one - or more entities. Machines and units are supported. - - entities : typing.Sequence[~Entity] - Returns -> SSHAddressResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="SSHClient", request="PublicAddress", version=4, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(SSHPublicKeysResults) - async def PublicKeys(self, entities=None): - """PublicKeys returns the public SSH hosts for one or more - entities. Machines and units are supported. - - entities : typing.Sequence[~Entity] - Returns -> SSHPublicKeysResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="SSHClient", request="PublicKeys", version=4, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client5.py b/build/lib/juju/client/_client5.py deleted file mode 100644 index ee64edd4..00000000 --- a/build/lib/juju/client/_client5.py +++ /dev/null @@ -1,285 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class ApplicationOffersFacade(Type): - name = "ApplicationOffers" - version = 5 - - @ReturnMapping(ApplicationOffersResults) - async def ApplicationOffers(self, bakery_version=None, offer_urls=None): - """ApplicationOffers gets details about remote applications that match given URLs. - - bakery_version : int - offer_urls : typing.Sequence[str] - Returns -> ApplicationOffersResults - """ - if bakery_version is not None and not isinstance(bakery_version, int): - raise Exception( - f"Expected bakery_version to be a int, received: {type(bakery_version)}" - ) - - if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): - raise Exception( - f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="ApplicationOffers", - version=5, - params=_params, - ) - _params["bakery-version"] = bakery_version - _params["offer-urls"] = offer_urls - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DestroyOffers(self, force=None, offer_urls=None): - """DestroyOffers removes the offers specified by the given URLs, forcing if necessary. - - force : bool - offer_urls : typing.Sequence[str] - Returns -> ErrorResults - """ - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): - raise Exception( - f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", request="DestroyOffers", version=5, params=_params - ) - _params["force"] = force - _params["offer-urls"] = offer_urls - reply = await self.rpc(msg) - return reply - - @ReturnMapping(QueryApplicationOffersResultsV5) - async def FindApplicationOffers(self, filters=None): - """FindApplicationOffers gets details about remote applications that match given filter. - - filters : typing.Sequence[~OfferFilter] - Returns -> QueryApplicationOffersResultsV5 - """ - if filters is not None and not isinstance(filters, (bytes, str, list)): - raise Exception( - f"Expected filters to be a Sequence, received: {type(filters)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="FindApplicationOffers", - version=5, - params=_params, - ) - _params["Filters"] = filters - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ConsumeOfferDetailsResults) - async def GetConsumeDetails(self, offer_urls=None, user_tag=None): - """GetConsumeDetails returns the details necessary to pass to another model - to allow the specified args user to consume the offers represented by the args URLs. - - offer_urls : OfferURLs - user_tag : str - Returns -> ConsumeOfferDetailsResults - """ - if offer_urls is not None and not isinstance(offer_urls, (dict, OfferURLs)): - raise Exception( - f"Expected offer_urls to be a OfferURLs, received: {type(offer_urls)}" - ) - - if user_tag is not None and not isinstance(user_tag, (bytes, str)): - raise Exception( - f"Expected user_tag to be a str, received: {type(user_tag)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="GetConsumeDetails", - version=5, - params=_params, - ) - _params["offer-urls"] = offer_urls - _params["user-tag"] = user_tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(QueryApplicationOffersResultsV5) - async def ListApplicationOffers(self, filters=None): - """ListApplicationOffers gets deployed details about application offers that match given filter. - The results contain details about the deployed applications such as connection count. - - filters : typing.Sequence[~OfferFilter] - Returns -> QueryApplicationOffersResultsV5 - """ - if filters is not None and not isinstance(filters, (bytes, str, list)): - raise Exception( - f"Expected filters to be a Sequence, received: {type(filters)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="ListApplicationOffers", - version=5, - params=_params, - ) - _params["Filters"] = filters - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ModifyOfferAccess(self, changes=None): - """ModifyOfferAccess changes the application offer access granted to users. - - changes : typing.Sequence[~ModifyOfferAccess] - Returns -> ErrorResults - """ - if changes is not None and not isinstance(changes, (bytes, str, list)): - raise Exception( - f"Expected changes to be a Sequence, received: {type(changes)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="ModifyOfferAccess", - version=5, - params=_params, - ) - _params["changes"] = changes - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Offer(self, offers=None): - """Offer makes application endpoints available for consumption at a specified URL. - - offers : typing.Sequence[~AddApplicationOffer] - Returns -> ErrorResults - """ - if offers is not None and not isinstance(offers, (bytes, str, list)): - raise Exception( - f"Expected offers to be a Sequence, received: {type(offers)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="ApplicationOffers", request="Offer", version=5, params=_params) - _params["Offers"] = offers - reply = await self.rpc(msg) - return reply - - @ReturnMapping(RemoteApplicationInfoResults) - async def RemoteApplicationInfo(self, bakery_version=None, offer_urls=None): - """RemoteApplicationInfo returns information about the requested remote application. - This call currently has no client side API, only there for the Dashboard at this stage. - - bakery_version : int - offer_urls : typing.Sequence[str] - Returns -> RemoteApplicationInfoResults - """ - if bakery_version is not None and not isinstance(bakery_version, int): - raise Exception( - f"Expected bakery_version to be a int, received: {type(bakery_version)}" - ) - - if offer_urls is not None and not isinstance(offer_urls, (bytes, str, list)): - raise Exception( - f"Expected offer_urls to be a Sequence, received: {type(offer_urls)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ApplicationOffers", - request="RemoteApplicationInfo", - version=5, - params=_params, - ) - _params["bakery-version"] = bakery_version - _params["offer-urls"] = offer_urls - reply = await self.rpc(msg) - return reply - - -class SubnetsFacade(Type): - name = "Subnets" - version = 5 - - @ReturnMapping(ZoneResults) - async def AllZones(self): - """AllZones returns all availability zones known to Juju. If a - zone is unusable, unavailable, or deprecated the Available - field will be false. - - Returns -> ZoneResults - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Subnets", request="AllZones", version=5, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ListSubnetsResults) - async def ListSubnets(self, space_tag=None, zone=None): - """ListSubnets returns the matching subnets after applying - optional filters. - - space_tag : str - zone : str - Returns -> ListSubnetsResults - """ - if space_tag is not None and not isinstance(space_tag, (bytes, str)): - raise Exception( - f"Expected space_tag to be a str, received: {type(space_tag)}" - ) - - if zone is not None and not isinstance(zone, (bytes, str)): - raise Exception(f"Expected zone to be a str, received: {type(zone)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Subnets", request="ListSubnets", version=5, params=_params) - _params["space-tag"] = space_tag - _params["zone"] = zone - reply = await self.rpc(msg) - return reply - - @ReturnMapping(SubnetsResults) - async def SubnetsByCIDR(self, cidrs=None): - """SubnetsByCIDR returns the collection of subnets matching each CIDR in the input. - - cidrs : typing.Sequence[str] - Returns -> SubnetsResults - """ - if cidrs is not None and not isinstance(cidrs, (bytes, str, list)): - raise Exception(f"Expected cidrs to be a Sequence, received: {type(cidrs)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Subnets", request="SubnetsByCIDR", version=5, params=_params) - _params["cidrs"] = cidrs - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client6.py b/build/lib/juju/client/_client6.py deleted file mode 100644 index 29c2cb93..00000000 --- a/build/lib/juju/client/_client6.py +++ /dev/null @@ -1,781 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class BundleFacade(Type): - name = "Bundle" - version = 6 - - @ReturnMapping(StringResult) - async def ExportBundle(self, include_charm_defaults=None, include_series=None): - """ExportBundle exports the current model configuration as bundle. - - include_charm_defaults : bool - include_series : bool - Returns -> StringResult - """ - if include_charm_defaults is not None and not isinstance( - include_charm_defaults, bool - ): - raise Exception( - f"Expected include_charm_defaults to be a bool, received: {type(include_charm_defaults)}" - ) - - if include_series is not None and not isinstance(include_series, bool): - raise Exception( - f"Expected include_series to be a bool, received: {type(include_series)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Bundle", request="ExportBundle", version=6, params=_params) - _params["include-charm-defaults"] = include_charm_defaults - _params["include-series"] = include_series - reply = await self.rpc(msg) - return reply - - @ReturnMapping(BundleChangesResults) - async def GetChanges(self, bundleurl=None, yaml=None): - """GetChanges returns the list of changes required to deploy the given bundle - data. The changes are sorted by requirements, so that they can be applied in - order. - GetChanges has been superseded in favour of GetChangesMapArgs. It's - preferable to use that new method to add new functionality and move clients - away from this one. - - bundleurl : str - yaml : str - Returns -> BundleChangesResults - """ - if bundleurl is not None and not isinstance(bundleurl, (bytes, str)): - raise Exception( - f"Expected bundleurl to be a str, received: {type(bundleurl)}" - ) - - if yaml is not None and not isinstance(yaml, (bytes, str)): - raise Exception(f"Expected yaml to be a str, received: {type(yaml)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Bundle", request="GetChanges", version=6, params=_params) - _params["bundleURL"] = bundleurl - _params["yaml"] = yaml - reply = await self.rpc(msg) - return reply - - @ReturnMapping(BundleChangesMapArgsResults) - async def GetChangesMapArgs(self, bundleurl=None, yaml=None): - """GetChangesMapArgs returns the list of changes required to deploy the given - bundle data. The changes are sorted by requirements, so that they can be - applied in order. - V4 GetChangesMapArgs is not supported on anything less than v4 - - bundleurl : str - yaml : str - Returns -> BundleChangesMapArgsResults - """ - if bundleurl is not None and not isinstance(bundleurl, (bytes, str)): - raise Exception( - f"Expected bundleurl to be a str, received: {type(bundleurl)}" - ) - - if yaml is not None and not isinstance(yaml, (bytes, str)): - raise Exception(f"Expected yaml to be a str, received: {type(yaml)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Bundle", request="GetChangesMapArgs", version=6, params=_params - ) - _params["bundleURL"] = bundleurl - _params["yaml"] = yaml - reply = await self.rpc(msg) - return reply - - -class CharmsFacade(Type): - name = "Charms" - version = 6 - - @ReturnMapping(CharmOriginResult) - async def AddCharm(self, charm_origin=None, force=None, url=None): - """AddCharm adds the given charm URL (which must include revision) to the - environment, if it does not exist yet. Local charms are not supported, - only charm store and charm hub URLs. See also AddLocalCharm(). - - charm_origin : CharmOrigin - force : bool - url : str - Returns -> CharmOriginResult - """ - if charm_origin is not None and not isinstance( - charm_origin, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if url is not None and not isinstance(url, (bytes, str)): - raise Exception(f"Expected url to be a str, received: {type(url)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="AddCharm", version=6, params=_params) - _params["charm-origin"] = charm_origin - _params["force"] = force - _params["url"] = url - reply = await self.rpc(msg) - return reply - - @ReturnMapping(Charm) - async def CharmInfo(self, url=None): - """CharmInfo returns information about the requested charm. - - url : str - Returns -> Charm - """ - if url is not None and not isinstance(url, (bytes, str)): - raise Exception(f"Expected url to be a str, received: {type(url)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="CharmInfo", version=6, params=_params) - _params["url"] = url - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def CheckCharmPlacement(self, placements=None): - """CheckCharmPlacement checks if a charm is allowed to be placed with in a - given application. - - placements : typing.Sequence[~ApplicationCharmPlacement] - Returns -> ErrorResults - """ - if placements is not None and not isinstance(placements, (bytes, str, list)): - raise Exception( - f"Expected placements to be a Sequence, received: {type(placements)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Charms", request="CheckCharmPlacement", version=6, params=_params - ) - _params["placements"] = placements - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DownloadInfoResults) - async def GetDownloadInfos(self, entities=None): - """GetDownloadInfos attempts to get the bundle corresponding to the charm url - and origin. - - entities : typing.Sequence[~CharmURLAndOrigin] - Returns -> DownloadInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="GetDownloadInfos", version=6, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(IsMeteredResult) - async def IsMetered(self, url=None): - """IsMetered returns whether or not the charm is metered. - - url : str - Returns -> IsMeteredResult - """ - if url is not None and not isinstance(url, (bytes, str)): - raise Exception(f"Expected url to be a str, received: {type(url)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="IsMetered", version=6, params=_params) - _params["url"] = url - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CharmsListResult) - async def List(self, names=None): - """List returns a list of charm URLs currently in the state. - If supplied parameter contains any names, the result will - be filtered to return only the charms with supplied names. - - names : typing.Sequence[str] - Returns -> CharmsListResult - """ - if names is not None and not isinstance(names, (bytes, str, list)): - raise Exception(f"Expected names to be a Sequence, received: {type(names)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="List", version=6, params=_params) - _params["names"] = names - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CharmResourcesResults) - async def ListCharmResources(self, entities=None): - """ListCharmResources returns a series of resources for a given charm. - - entities : typing.Sequence[~CharmURLAndOrigin] - Returns -> CharmResourcesResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Charms", request="ListCharmResources", version=6, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ResolveCharmWithChannelResults) - async def ResolveCharms(self, macaroon=None, resolve=None): - """ResolveCharms resolves the given charm URLs with an optionally specified - preferred channel. Channel provided via CharmOrigin. - - macaroon : Macaroon - resolve : typing.Sequence[~ResolveCharmWithChannel] - Returns -> ResolveCharmWithChannelResults - """ - if macaroon is not None and not isinstance(macaroon, (dict, Macaroon)): - raise Exception( - f"Expected macaroon to be a Macaroon, received: {type(macaroon)}" - ) - - if resolve is not None and not isinstance(resolve, (bytes, str, list)): - raise Exception( - f"Expected resolve to be a Sequence, received: {type(resolve)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="ResolveCharms", version=6, params=_params) - _params["macaroon"] = macaroon - _params["resolve"] = resolve - reply = await self.rpc(msg) - return reply - - -class ClientFacade(Type): - name = "Client" - version = 6 - - @ReturnMapping(FindToolsResult) - async def FindTools( - self, agentstream=None, arch=None, major=None, number=None, os_type=None - ): - """FindTools returns a List containing all tools matching the given parameters. - TODO(juju 3.1) - remove, used by 2.9 client only - - agentstream : str - arch : str - major : int - number : Number - os_type : str - Returns -> FindToolsResult - """ - if agentstream is not None and not isinstance(agentstream, (bytes, str)): - raise Exception( - f"Expected agentstream to be a str, received: {type(agentstream)}" - ) - - if arch is not None and not isinstance(arch, (bytes, str)): - raise Exception(f"Expected arch to be a str, received: {type(arch)}") - - if major is not None and not isinstance(major, int): - raise Exception(f"Expected major to be a int, received: {type(major)}") - - if number is not None and not isinstance(number, (dict, Number)): - raise Exception(f"Expected number to be a Number, received: {type(number)}") - - if os_type is not None and not isinstance(os_type, (bytes, str)): - raise Exception(f"Expected os_type to be a str, received: {type(os_type)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="FindTools", version=6, params=_params) - _params["agentstream"] = agentstream - _params["arch"] = arch - _params["major"] = major - _params["number"] = number - _params["os-type"] = os_type - reply = await self.rpc(msg) - return reply - - @ReturnMapping(FullStatus) - async def FullStatus(self, patterns=None): - """FullStatus gives the information needed for juju status over the api - - patterns : typing.Sequence[str] - Returns -> FullStatus - """ - if patterns is not None and not isinstance(patterns, (bytes, str, list)): - raise Exception( - f"Expected patterns to be a Sequence, received: {type(patterns)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="FullStatus", version=6, params=_params) - _params["patterns"] = patterns - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StatusHistoryResults) - async def StatusHistory(self, requests=None): - """StatusHistory returns a slice of past statuses for several entities. - - requests : typing.Sequence[~StatusHistoryRequest] - Returns -> StatusHistoryResults - """ - if requests is not None and not isinstance(requests, (bytes, str, list)): - raise Exception( - f"Expected requests to be a Sequence, received: {type(requests)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="StatusHistory", version=6, params=_params) - _params["requests"] = requests - reply = await self.rpc(msg) - return reply - - @ReturnMapping(AllWatcherId) - async def WatchAll(self): - """WatchAll initiates a watcher for entities in the connected model. - - Returns -> AllWatcherId - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="WatchAll", version=6, params=_params) - - reply = await self.rpc(msg) - return reply - - -class SpacesFacade(Type): - name = "Spaces" - version = 6 - - @ReturnMapping(ErrorResults) - async def CreateSpaces(self, spaces=None): - """CreateSpaces creates a new Juju network space, associating the - specified subnets with it (optional; can be empty). - - spaces : typing.Sequence[~CreateSpaceParams] - Returns -> ErrorResults - """ - if spaces is not None and not isinstance(spaces, (bytes, str, list)): - raise Exception( - f"Expected spaces to be a Sequence, received: {type(spaces)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Spaces", request="CreateSpaces", version=6, params=_params) - _params["spaces"] = spaces - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ListSpacesResults) - async def ListSpaces(self): - """ListSpaces lists all the available spaces and their associated subnets. - - Returns -> ListSpacesResults - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Spaces", request="ListSpaces", version=6, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(MoveSubnetsResults) - async def MoveSubnets(self, args=None): - """MoveSubnets ensures that the input subnets are in the input space. - - args : typing.Sequence[~MoveSubnetsParam] - Returns -> MoveSubnetsResults - """ - if args is not None and not isinstance(args, (bytes, str, list)): - raise Exception(f"Expected args to be a Sequence, received: {type(args)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Spaces", request="MoveSubnets", version=6, params=_params) - _params["args"] = args - reply = await self.rpc(msg) - return reply - - @ReturnMapping(None) - async def ReloadSpaces(self): - """ReloadSpaces refreshes spaces from substrate - - Returns -> None - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Spaces", request="ReloadSpaces", version=6, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(RemoveSpaceResults) - async def RemoveSpace(self, space_param=None): - """RemoveSpace removes a space. - Returns SpaceResults if entities/settings are found which makes the deletion not possible. - - space_param : typing.Sequence[~RemoveSpaceParam] - Returns -> RemoveSpaceResults - """ - if space_param is not None and not isinstance(space_param, (bytes, str, list)): - raise Exception( - f"Expected space_param to be a Sequence, received: {type(space_param)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Spaces", request="RemoveSpace", version=6, params=_params) - _params["space-param"] = space_param - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def RenameSpace(self, changes=None): - """RenameSpace renames a space. - - changes : typing.Sequence[~RenameSpaceParams] - Returns -> ErrorResults - """ - if changes is not None and not isinstance(changes, (bytes, str, list)): - raise Exception( - f"Expected changes to be a Sequence, received: {type(changes)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Spaces", request="RenameSpace", version=6, params=_params) - _params["changes"] = changes - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ShowSpaceResults) - async def ShowSpace(self, entities=None): - """ShowSpace shows the spaces for a set of given entities. - - entities : typing.Sequence[~Entity] - Returns -> ShowSpaceResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Spaces", request="ShowSpace", version=6, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - -class StorageFacade(Type): - name = "Storage" - version = 6 - - @ReturnMapping(AddStorageResults) - async def AddToUnit(self, storages=None): - """AddToUnit validates and creates additional storage instances for units. - A "CHANGE" block can block this operation. - - storages : typing.Sequence[~StorageAddParams] - Returns -> AddStorageResults - """ - if storages is not None and not isinstance(storages, (bytes, str, list)): - raise Exception( - f"Expected storages to be a Sequence, received: {type(storages)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="AddToUnit", version=6, params=_params) - _params["storages"] = storages - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Attach(self, ids=None): - """Attach attaches existing storage instances to units. - A "CHANGE" block can block this operation. - - ids : typing.Sequence[~StorageAttachmentId] - Returns -> ErrorResults - """ - if ids is not None and not isinstance(ids, (bytes, str, list)): - raise Exception(f"Expected ids to be a Sequence, received: {type(ids)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="Attach", version=6, params=_params) - _params["ids"] = ids - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def CreatePool(self, pools=None): - """CreatePool creates a new pool with specified parameters. - - pools : typing.Sequence[~StoragePool] - Returns -> ErrorResults - """ - if pools is not None and not isinstance(pools, (bytes, str, list)): - raise Exception(f"Expected pools to be a Sequence, received: {type(pools)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="CreatePool", version=6, params=_params) - _params["pools"] = pools - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DetachStorage(self, force=None, ids=None, max_wait=None): - """DetachStorage sets the specified storage attachments to Dying, unless they are - already Dying or Dead. Any associated, persistent storage will remain - alive. This call can be forced. - - force : bool - ids : StorageAttachmentIds - max_wait : int - Returns -> ErrorResults - """ - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if ids is not None and not isinstance(ids, (dict, StorageAttachmentIds)): - raise Exception( - f"Expected ids to be a StorageAttachmentIds, received: {type(ids)}" - ) - - if max_wait is not None and not isinstance(max_wait, int): - raise Exception( - f"Expected max_wait to be a int, received: {type(max_wait)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="DetachStorage", version=6, params=_params) - _params["force"] = force - _params["ids"] = ids - _params["max-wait"] = max_wait - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ImportStorageResults) - async def Import(self, storage=None): - """Import imports existing storage into the model. - A "CHANGE" block can block this operation. - - storage : typing.Sequence[~ImportStorageParams] - Returns -> ImportStorageResults - """ - if storage is not None and not isinstance(storage, (bytes, str, list)): - raise Exception( - f"Expected storage to be a Sequence, received: {type(storage)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="Import", version=6, params=_params) - _params["storage"] = storage - reply = await self.rpc(msg) - return reply - - @ReturnMapping(FilesystemDetailsListResults) - async def ListFilesystems(self, filters=None): - """ListFilesystems returns a list of filesystems in the environment matching - the provided filter. Each result describes a filesystem in detail, including - the filesystem's attachments. - - filters : typing.Sequence[~FilesystemFilter] - Returns -> FilesystemDetailsListResults - """ - if filters is not None and not isinstance(filters, (bytes, str, list)): - raise Exception( - f"Expected filters to be a Sequence, received: {type(filters)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="ListFilesystems", version=6, params=_params) - _params["filters"] = filters - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StoragePoolsResults) - async def ListPools(self, filters=None): - """ListPools returns a list of pools. - If filter is provided, returned list only contains pools that match - the filter. - Pools can be filtered on names and provider types. - If both names and types are provided as filter, - pools that match either are returned. - This method lists union of pools and environment provider types. - If no filter is provided, all pools are returned. - - filters : typing.Sequence[~StoragePoolFilter] - Returns -> StoragePoolsResults - """ - if filters is not None and not isinstance(filters, (bytes, str, list)): - raise Exception( - f"Expected filters to be a Sequence, received: {type(filters)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="ListPools", version=6, params=_params) - _params["filters"] = filters - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StorageDetailsListResults) - async def ListStorageDetails(self, filters=None): - """ListStorageDetails returns storage matching a filter. - - filters : typing.Sequence[~StorageFilter] - Returns -> StorageDetailsListResults - """ - if filters is not None and not isinstance(filters, (bytes, str, list)): - raise Exception( - f"Expected filters to be a Sequence, received: {type(filters)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Storage", request="ListStorageDetails", version=6, params=_params - ) - _params["filters"] = filters - reply = await self.rpc(msg) - return reply - - @ReturnMapping(VolumeDetailsListResults) - async def ListVolumes(self, filters=None): - """ListVolumes lists volumes with the given filters. Each filter produces - an independent list of volumes, or an error if the filter is invalid - or the volumes could not be listed. - - filters : typing.Sequence[~VolumeFilter] - Returns -> VolumeDetailsListResults - """ - if filters is not None and not isinstance(filters, (bytes, str, list)): - raise Exception( - f"Expected filters to be a Sequence, received: {type(filters)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="ListVolumes", version=6, params=_params) - _params["filters"] = filters - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def Remove(self, storage=None): - """Remove sets the specified storage entities to Dying, unless they are - already Dying or Dead, such that the storage will eventually be removed - from the model. If the arguments specify that the storage should be - destroyed, then the associated cloud storage will be destroyed first; - otherwise it will only be released from Juju's control. - - storage : typing.Sequence[~RemoveStorageInstance] - Returns -> ErrorResults - """ - if storage is not None and not isinstance(storage, (bytes, str, list)): - raise Exception( - f"Expected storage to be a Sequence, received: {type(storage)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="Remove", version=6, params=_params) - _params["storage"] = storage - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def RemovePool(self, pools=None): - """RemovePool deletes the named pool - - pools : typing.Sequence[~StoragePoolDeleteArg] - Returns -> ErrorResults - """ - if pools is not None and not isinstance(pools, (bytes, str, list)): - raise Exception(f"Expected pools to be a Sequence, received: {type(pools)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="RemovePool", version=6, params=_params) - _params["pools"] = pools - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StorageDetailsResults) - async def StorageDetails(self, entities=None): - """StorageDetails retrieves and returns detailed information about desired - storage identified by supplied tags. If specified storage cannot be - retrieved, individual error is returned instead of storage information. - - entities : typing.Sequence[~Entity] - Returns -> StorageDetailsResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="StorageDetails", version=6, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UpdatePool(self, pools=None): - """UpdatePool deletes the named pool - - pools : typing.Sequence[~StoragePool] - Returns -> ErrorResults - """ - if pools is not None and not isinstance(pools, (bytes, str, list)): - raise Exception(f"Expected pools to be a Sequence, received: {type(pools)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Storage", request="UpdatePool", version=6, params=_params) - _params["pools"] = pools - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client7.py b/build/lib/juju/client/_client7.py deleted file mode 100644 index 09efa972..00000000 --- a/build/lib/juju/client/_client7.py +++ /dev/null @@ -1,1014 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class ActionFacade(Type): - name = "Action" - version = 7 - - @ReturnMapping(ActionResults) - async def Actions(self, entities=None): - """Actions takes a list of ActionTags, and returns the full Action for - each ID. - - entities : typing.Sequence[~Entity] - Returns -> ActionResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Action", request="Actions", version=7, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ApplicationsCharmActionsResults) - async def ApplicationsCharmsActions(self, entities=None): - """ApplicationsCharmsActions returns a slice of charm Actions for a slice of - services. - - entities : typing.Sequence[~Entity] - Returns -> ApplicationsCharmActionsResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Action", - request="ApplicationsCharmsActions", - version=7, - params=_params, - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ActionResults) - async def Cancel(self, entities=None): - """Cancel attempts to cancel enqueued Actions from running. - - entities : typing.Sequence[~Entity] - Returns -> ActionResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Action", request="Cancel", version=7, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(EnqueuedActions) - async def EnqueueOperation(self, actions=None): - """EnqueueOperation takes a list of Actions and queues them up to be executed as - an operation, each action running as a task on the designated ActionReceiver. - We return the ID of the overall operation and each individual task. - - actions : typing.Sequence[~Action] - Returns -> EnqueuedActions - """ - if actions is not None and not isinstance(actions, (bytes, str, list)): - raise Exception( - f"Expected actions to be a Sequence, received: {type(actions)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Action", request="EnqueueOperation", version=7, params=_params) - _params["actions"] = actions - reply = await self.rpc(msg) - return reply - - @ReturnMapping(OperationResults) - async def ListOperations( - self, - actions=None, - applications=None, - limit=None, - machines=None, - offset=None, - status=None, - units=None, - ): - """ListOperations fetches the called actions for specified apps/units. - - actions : typing.Sequence[str] - applications : typing.Sequence[str] - limit : int - machines : typing.Sequence[str] - offset : int - status : typing.Sequence[str] - units : typing.Sequence[str] - Returns -> OperationResults - """ - if actions is not None and not isinstance(actions, (bytes, str, list)): - raise Exception( - f"Expected actions to be a Sequence, received: {type(actions)}" - ) - - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - if limit is not None and not isinstance(limit, int): - raise Exception(f"Expected limit to be a int, received: {type(limit)}") - - if machines is not None and not isinstance(machines, (bytes, str, list)): - raise Exception( - f"Expected machines to be a Sequence, received: {type(machines)}" - ) - - if offset is not None and not isinstance(offset, int): - raise Exception(f"Expected offset to be a int, received: {type(offset)}") - - if status is not None and not isinstance(status, (bytes, str, list)): - raise Exception( - f"Expected status to be a Sequence, received: {type(status)}" - ) - - if units is not None and not isinstance(units, (bytes, str, list)): - raise Exception(f"Expected units to be a Sequence, received: {type(units)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Action", request="ListOperations", version=7, params=_params) - _params["actions"] = actions - _params["applications"] = applications - _params["limit"] = limit - _params["machines"] = machines - _params["offset"] = offset - _params["status"] = status - _params["units"] = units - reply = await self.rpc(msg) - return reply - - @ReturnMapping(OperationResults) - async def Operations(self, entities=None): - """Operations fetches the specified operation ids. - - entities : typing.Sequence[~Entity] - Returns -> OperationResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Action", request="Operations", version=7, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(EnqueuedActions) - async def Run( - self, - applications=None, - commands=None, - execution_group=None, - machines=None, - parallel=None, - timeout=None, - units=None, - workload_context=None, - ): - """Run the commands specified on the machines identified through the - list of machines, units and services. - - applications : typing.Sequence[str] - commands : str - execution_group : str - machines : typing.Sequence[str] - parallel : bool - timeout : int - units : typing.Sequence[str] - workload_context : bool - Returns -> EnqueuedActions - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - if commands is not None and not isinstance(commands, (bytes, str)): - raise Exception( - f"Expected commands to be a str, received: {type(commands)}" - ) - - if execution_group is not None and not isinstance( - execution_group, (bytes, str) - ): - raise Exception( - f"Expected execution_group to be a str, received: {type(execution_group)}" - ) - - if machines is not None and not isinstance(machines, (bytes, str, list)): - raise Exception( - f"Expected machines to be a Sequence, received: {type(machines)}" - ) - - if parallel is not None and not isinstance(parallel, bool): - raise Exception( - f"Expected parallel to be a bool, received: {type(parallel)}" - ) - - if timeout is not None and not isinstance(timeout, int): - raise Exception(f"Expected timeout to be a int, received: {type(timeout)}") - - if units is not None and not isinstance(units, (bytes, str, list)): - raise Exception(f"Expected units to be a Sequence, received: {type(units)}") - - if workload_context is not None and not isinstance(workload_context, bool): - raise Exception( - f"Expected workload_context to be a bool, received: {type(workload_context)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Action", request="Run", version=7, params=_params) - _params["applications"] = applications - _params["commands"] = commands - _params["execution-group"] = execution_group - _params["machines"] = machines - _params["parallel"] = parallel - _params["timeout"] = timeout - _params["units"] = units - _params["workload-context"] = workload_context - reply = await self.rpc(msg) - return reply - - @ReturnMapping(EnqueuedActions) - async def RunOnAllMachines( - self, - applications=None, - commands=None, - execution_group=None, - machines=None, - parallel=None, - timeout=None, - units=None, - workload_context=None, - ): - """RunOnAllMachines attempts to run the specified command on all the machines. - - applications : typing.Sequence[str] - commands : str - execution_group : str - machines : typing.Sequence[str] - parallel : bool - timeout : int - units : typing.Sequence[str] - workload_context : bool - Returns -> EnqueuedActions - """ - if applications is not None and not isinstance( - applications, (bytes, str, list) - ): - raise Exception( - f"Expected applications to be a Sequence, received: {type(applications)}" - ) - - if commands is not None and not isinstance(commands, (bytes, str)): - raise Exception( - f"Expected commands to be a str, received: {type(commands)}" - ) - - if execution_group is not None and not isinstance( - execution_group, (bytes, str) - ): - raise Exception( - f"Expected execution_group to be a str, received: {type(execution_group)}" - ) - - if machines is not None and not isinstance(machines, (bytes, str, list)): - raise Exception( - f"Expected machines to be a Sequence, received: {type(machines)}" - ) - - if parallel is not None and not isinstance(parallel, bool): - raise Exception( - f"Expected parallel to be a bool, received: {type(parallel)}" - ) - - if timeout is not None and not isinstance(timeout, int): - raise Exception(f"Expected timeout to be a int, received: {type(timeout)}") - - if units is not None and not isinstance(units, (bytes, str, list)): - raise Exception(f"Expected units to be a Sequence, received: {type(units)}") - - if workload_context is not None and not isinstance(workload_context, bool): - raise Exception( - f"Expected workload_context to be a bool, received: {type(workload_context)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Action", request="RunOnAllMachines", version=7, params=_params) - _params["applications"] = applications - _params["commands"] = commands - _params["execution-group"] = execution_group - _params["machines"] = machines - _params["parallel"] = parallel - _params["timeout"] = timeout - _params["units"] = units - _params["workload-context"] = workload_context - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringsWatchResults) - async def WatchActionsProgress(self, entities=None): - """WatchActionsProgress creates a watcher that reports on action log messages. - - entities : typing.Sequence[~Entity] - Returns -> StringsWatchResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Action", request="WatchActionsProgress", version=7, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - -class CharmsFacade(Type): - name = "Charms" - version = 7 - - @ReturnMapping(CharmOriginResult) - async def AddCharm(self, charm_origin=None, force=None, url=None): - """AddCharm adds the given charm URL (which must include revision) to the - environment, if it does not exist yet. Local charms are not supported, - only charm store and charm hub URLs. See also AddLocalCharm(). - - charm_origin : CharmOrigin - force : bool - url : str - Returns -> CharmOriginResult - """ - if charm_origin is not None and not isinstance( - charm_origin, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin to be a CharmOrigin, received: {type(charm_origin)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if url is not None and not isinstance(url, (bytes, str)): - raise Exception(f"Expected url to be a str, received: {type(url)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="AddCharm", version=7, params=_params) - _params["charm-origin"] = charm_origin - _params["force"] = force - _params["url"] = url - reply = await self.rpc(msg) - return reply - - @ReturnMapping(Charm) - async def CharmInfo(self, url=None): - """CharmInfo returns information about the requested charm. - - url : str - Returns -> Charm - """ - if url is not None and not isinstance(url, (bytes, str)): - raise Exception(f"Expected url to be a str, received: {type(url)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="CharmInfo", version=7, params=_params) - _params["url"] = url - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def CheckCharmPlacement(self, placements=None): - """CheckCharmPlacement checks if a charm is allowed to be placed with in a - given application. - - placements : typing.Sequence[~ApplicationCharmPlacement] - Returns -> ErrorResults - """ - if placements is not None and not isinstance(placements, (bytes, str, list)): - raise Exception( - f"Expected placements to be a Sequence, received: {type(placements)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Charms", request="CheckCharmPlacement", version=7, params=_params - ) - _params["placements"] = placements - reply = await self.rpc(msg) - return reply - - @ReturnMapping(DownloadInfoResults) - async def GetDownloadInfos(self, entities=None): - """GetDownloadInfos attempts to get the bundle corresponding to the charm url - and origin. - - entities : typing.Sequence[~CharmURLAndOrigin] - Returns -> DownloadInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="GetDownloadInfos", version=7, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(IsMeteredResult) - async def IsMetered(self, url=None): - """IsMetered returns whether or not the charm is metered. - TODO (cderici) only used for metered charms in cmd MeteredDeployAPI, - kept for client compatibility, remove in juju 4.0 - - url : str - Returns -> IsMeteredResult - """ - if url is not None and not isinstance(url, (bytes, str)): - raise Exception(f"Expected url to be a str, received: {type(url)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="IsMetered", version=7, params=_params) - _params["url"] = url - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CharmsListResult) - async def List(self, names=None): - """List returns a list of charm URLs currently in the state. - If supplied parameter contains any names, the result will - be filtered to return only the charms with supplied names. - - names : typing.Sequence[str] - Returns -> CharmsListResult - """ - if names is not None and not isinstance(names, (bytes, str, list)): - raise Exception(f"Expected names to be a Sequence, received: {type(names)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="List", version=7, params=_params) - _params["names"] = names - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CharmResourcesResults) - async def ListCharmResources(self, entities=None): - """ListCharmResources returns a series of resources for a given charm. - - entities : typing.Sequence[~CharmURLAndOrigin] - Returns -> CharmResourcesResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Charms", request="ListCharmResources", version=7, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ResolveCharmWithChannelResults) - async def ResolveCharms(self, macaroon=None, resolve=None): - """ResolveCharms resolves the given charm URLs with an optionally specified - preferred channel. Channel provided via CharmOrigin. - - macaroon : Macaroon - resolve : typing.Sequence[~ResolveCharmWithChannel] - Returns -> ResolveCharmWithChannelResults - """ - if macaroon is not None and not isinstance(macaroon, (dict, Macaroon)): - raise Exception( - f"Expected macaroon to be a Macaroon, received: {type(macaroon)}" - ) - - if resolve is not None and not isinstance(resolve, (bytes, str, list)): - raise Exception( - f"Expected resolve to be a Sequence, received: {type(resolve)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Charms", request="ResolveCharms", version=7, params=_params) - _params["macaroon"] = macaroon - _params["resolve"] = resolve - reply = await self.rpc(msg) - return reply - - -class ClientFacade(Type): - name = "Client" - version = 7 - - @ReturnMapping(FindToolsResult) - async def FindTools( - self, agentstream=None, arch=None, major=None, number=None, os_type=None - ): - """FindTools returns a List containing all tools matching the given parameters. - TODO(juju 3.1) - remove, used by 2.9 client only - - agentstream : str - arch : str - major : int - number : Number - os_type : str - Returns -> FindToolsResult - """ - if agentstream is not None and not isinstance(agentstream, (bytes, str)): - raise Exception( - f"Expected agentstream to be a str, received: {type(agentstream)}" - ) - - if arch is not None and not isinstance(arch, (bytes, str)): - raise Exception(f"Expected arch to be a str, received: {type(arch)}") - - if major is not None and not isinstance(major, int): - raise Exception(f"Expected major to be a int, received: {type(major)}") - - if number is not None and not isinstance(number, (dict, Number)): - raise Exception(f"Expected number to be a Number, received: {type(number)}") - - if os_type is not None and not isinstance(os_type, (bytes, str)): - raise Exception(f"Expected os_type to be a str, received: {type(os_type)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="FindTools", version=7, params=_params) - _params["agentstream"] = agentstream - _params["arch"] = arch - _params["major"] = major - _params["number"] = number - _params["os-type"] = os_type - reply = await self.rpc(msg) - return reply - - @ReturnMapping(FullStatus) - async def FullStatus(self, include_storage=None, patterns=None): - """FullStatus gives the information needed for juju status over the api - - include_storage : bool - patterns : typing.Sequence[str] - Returns -> FullStatus - """ - if include_storage is not None and not isinstance(include_storage, bool): - raise Exception( - f"Expected include_storage to be a bool, received: {type(include_storage)}" - ) - - if patterns is not None and not isinstance(patterns, (bytes, str, list)): - raise Exception( - f"Expected patterns to be a Sequence, received: {type(patterns)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="FullStatus", version=7, params=_params) - _params["include-storage"] = include_storage - _params["patterns"] = patterns - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StatusHistoryResults) - async def StatusHistory(self, requests=None): - """StatusHistory returns a slice of past statuses for several entities. - - requests : typing.Sequence[~StatusHistoryRequest] - Returns -> StatusHistoryResults - """ - if requests is not None and not isinstance(requests, (bytes, str, list)): - raise Exception( - f"Expected requests to be a Sequence, received: {type(requests)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="StatusHistory", version=7, params=_params) - _params["requests"] = requests - reply = await self.rpc(msg) - return reply - - @ReturnMapping(AllWatcherId) - async def WatchAll(self): - """WatchAll initiates a watcher for entities in the connected model. - - Returns -> AllWatcherId - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="WatchAll", version=7, params=_params) - - reply = await self.rpc(msg) - return reply - - -class CloudFacade(Type): - name = "Cloud" - version = 7 - - @ReturnMapping(None) - async def AddCloud(self, cloud=None, force=None, name=None): - """AddCloud adds a new cloud, different from the one managed by the controller. - - cloud : Cloud - force : bool - name : str - Returns -> None - """ - if cloud is not None and not isinstance(cloud, (dict, Cloud)): - raise Exception(f"Expected cloud to be a Cloud, received: {type(cloud)}") - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - if name is not None and not isinstance(name, (bytes, str)): - raise Exception(f"Expected name to be a str, received: {type(name)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="AddCloud", version=7, params=_params) - _params["cloud"] = cloud - _params["force"] = force - _params["name"] = name - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def AddCredentials(self, credentials=None): - """AddCredentials adds new credentials. - In contrast to UpdateCredentials() below, the new credentials can be - for a cloud that the controller does not manage (this is required - for CAAS models) - - credentials : typing.Sequence[~TaggedCredential] - Returns -> ErrorResults - """ - if credentials is not None and not isinstance(credentials, (bytes, str, list)): - raise Exception( - f"Expected credentials to be a Sequence, received: {type(credentials)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="AddCredentials", version=7, params=_params) - _params["credentials"] = credentials - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UpdateCredentialResults) - async def CheckCredentialsModels(self, credentials=None): - """CheckCredentialsModels validates supplied cloud credentials' content against - models that currently use these credentials. - If there are any models that are using a credential and these models or their - cloud instances are not going to be accessible with corresponding credential, - there will be detailed validation errors per model. - There's no Juju API client which uses this, but JAAS does, - - credentials : typing.Sequence[~TaggedCredential] - Returns -> UpdateCredentialResults - """ - if credentials is not None and not isinstance(credentials, (bytes, str, list)): - raise Exception( - f"Expected credentials to be a Sequence, received: {type(credentials)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Cloud", request="CheckCredentialsModels", version=7, params=_params - ) - _params["credentials"] = credentials - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CloudResults) - async def Cloud(self, entities=None): - """Cloud returns the cloud definitions for the specified clouds. - - entities : typing.Sequence[~Entity] - Returns -> CloudResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="Cloud", version=7, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CloudInfoResults) - async def CloudInfo(self, entities=None): - """CloudInfo returns information about the specified clouds. - - entities : typing.Sequence[~Entity] - Returns -> CloudInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="CloudInfo", version=7, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CloudsResult) - async def Clouds(self): - """Clouds returns the definitions of all clouds supported by the controller - that the logged in user can see. - - Returns -> CloudsResult - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="Clouds", version=7, params=_params) - - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CloudCredentialResults) - async def Credential(self, entities=None): - """Credential returns the specified cloud credential for each tag, minus secrets. - - entities : typing.Sequence[~Entity] - Returns -> CloudCredentialResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="Credential", version=7, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(CredentialContentResults) - async def CredentialContents(self, credentials=None, include_secrets=None): - """CredentialContents returns the specified cloud credentials, - including the secrets if requested. - If no specific credential name/cloud was passed in, all credentials for this user - are returned. - Only credential owner can see its contents as well as what models use it. - Controller admin has no special superpowers here and is treated the same as all other users. - - credentials : typing.Sequence[~CloudCredentialArg] - include_secrets : bool - Returns -> CredentialContentResults - """ - if credentials is not None and not isinstance(credentials, (bytes, str, list)): - raise Exception( - f"Expected credentials to be a Sequence, received: {type(credentials)}" - ) - - if include_secrets is not None and not isinstance(include_secrets, bool): - raise Exception( - f"Expected include_secrets to be a bool, received: {type(include_secrets)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Cloud", request="CredentialContents", version=7, params=_params - ) - _params["credentials"] = credentials - _params["include-secrets"] = include_secrets - reply = await self.rpc(msg) - return reply - - @ReturnMapping(InstanceTypesResults) - async def InstanceTypes(self, constraints=None): - """InstanceTypes returns instance type information for the cloud and region - in which the current model is deployed. - - constraints : typing.Sequence[~CloudInstanceTypesConstraint] - Returns -> InstanceTypesResults - """ - if constraints is not None and not isinstance(constraints, (bytes, str, list)): - raise Exception( - f"Expected constraints to be a Sequence, received: {type(constraints)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="InstanceTypes", version=7, params=_params) - _params["constraints"] = constraints - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ListCloudInfoResults) - async def ListCloudInfo(self, all_=None, user_tag=None): - """ListCloudInfo returns clouds that the specified user has access to. - Controller admins (superuser) can list clouds for any user. - Other users can only ask about their own clouds. - - all_ : bool - user_tag : str - Returns -> ListCloudInfoResults - """ - if all_ is not None and not isinstance(all_, bool): - raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") - - if user_tag is not None and not isinstance(user_tag, (bytes, str)): - raise Exception( - f"Expected user_tag to be a str, received: {type(user_tag)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="ListCloudInfo", version=7, params=_params) - _params["all"] = all_ - _params["user-tag"] = user_tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ModifyCloudAccess(self, changes=None): - """ModifyCloudAccess changes the model access granted to users. - - changes : typing.Sequence[~ModifyCloudAccess] - Returns -> ErrorResults - """ - if changes is not None and not isinstance(changes, (bytes, str, list)): - raise Exception( - f"Expected changes to be a Sequence, received: {type(changes)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="ModifyCloudAccess", version=7, params=_params) - _params["changes"] = changes - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def RemoveClouds(self, entities=None): - """RemoveClouds removes the specified clouds from the controller. - If a cloud is in use (has models deployed to it), the removal will fail. - - entities : typing.Sequence[~Entity] - Returns -> ErrorResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="RemoveClouds", version=7, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def RevokeCredentialsCheckModels(self, credentials=None): - """RevokeCredentialsCheckModels revokes a set of cloud credentials. - If the credentials are used by any of the models, the credential deletion will be aborted. - If credential-in-use needs to be revoked nonetheless, this method allows the use of force. - - credentials : typing.Sequence[~RevokeCredentialArg] - Returns -> ErrorResults - """ - if credentials is not None and not isinstance(credentials, (bytes, str, list)): - raise Exception( - f"Expected credentials to be a Sequence, received: {type(credentials)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Cloud", - request="RevokeCredentialsCheckModels", - version=7, - params=_params, - ) - _params["credentials"] = credentials - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UpdateCloud(self, clouds=None): - """UpdateCloud updates an existing cloud that the controller knows about. - - clouds : typing.Sequence[~AddCloudArgs] - Returns -> ErrorResults - """ - if clouds is not None and not isinstance(clouds, (bytes, str, list)): - raise Exception( - f"Expected clouds to be a Sequence, received: {type(clouds)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="UpdateCloud", version=7, params=_params) - _params["clouds"] = clouds - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UpdateCredentialResults) - async def UpdateCredentialsCheckModels(self, credentials=None, force=None): - """UpdateCredentialsCheckModels updates a set of cloud credentials' content. - If there are any models that are using a credential and these models - are not going to be visible with updated credential content, - there will be detailed validation errors per model. Such model errors are returned - separately and do not contribute to the overall method error status. - Controller admins can 'force' an update of the credential - regardless of whether it is deemed valid or not. - - credentials : typing.Sequence[~TaggedCredential] - force : bool - Returns -> UpdateCredentialResults - """ - if credentials is not None and not isinstance(credentials, (bytes, str, list)): - raise Exception( - f"Expected credentials to be a Sequence, received: {type(credentials)}" - ) - - if force is not None and not isinstance(force, bool): - raise Exception(f"Expected force to be a bool, received: {type(force)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="Cloud", - request="UpdateCredentialsCheckModels", - version=7, - params=_params, - ) - _params["credentials"] = credentials - _params["force"] = force - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringsResults) - async def UserCredentials(self, user_clouds=None): - """UserCredentials returns the cloud credentials for a set of users. - - user_clouds : typing.Sequence[~UserCloud] - Returns -> StringsResults - """ - if user_clouds is not None and not isinstance(user_clouds, (bytes, str, list)): - raise Exception( - f"Expected user_clouds to be a Sequence, received: {type(user_clouds)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Cloud", request="UserCredentials", version=7, params=_params) - _params["user-clouds"] = user_clouds - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client8.py b/build/lib/juju/client/_client8.py deleted file mode 100644 index e1f061c4..00000000 --- a/build/lib/juju/client/_client8.py +++ /dev/null @@ -1,68 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class ClientFacade(Type): - name = "Client" - version = 8 - - @ReturnMapping(FullStatus) - async def FullStatus(self, include_storage=None, patterns=None): - """FullStatus gives the information needed for juju status over the api - - include_storage : bool - patterns : typing.Sequence[str] - Returns -> FullStatus - """ - if include_storage is not None and not isinstance(include_storage, bool): - raise Exception( - f"Expected include_storage to be a bool, received: {type(include_storage)}" - ) - - if patterns is not None and not isinstance(patterns, (bytes, str, list)): - raise Exception( - f"Expected patterns to be a Sequence, received: {type(patterns)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="FullStatus", version=8, params=_params) - _params["include-storage"] = include_storage - _params["patterns"] = patterns - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StatusHistoryResults) - async def StatusHistory(self, requests=None): - """StatusHistory returns a slice of past statuses for several entities. - - requests : typing.Sequence[~StatusHistoryRequest] - Returns -> StatusHistoryResults - """ - if requests is not None and not isinstance(requests, (bytes, str, list)): - raise Exception( - f"Expected requests to be a Sequence, received: {type(requests)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="StatusHistory", version=8, params=_params) - _params["requests"] = requests - reply = await self.rpc(msg) - return reply - - @ReturnMapping(AllWatcherId) - async def WatchAll(self): - """WatchAll initiates a watcher for entities in the connected model. - - Returns -> AllWatcherId - """ - # map input types to rpc msg - _params = dict() - msg = dict(type="Client", request="WatchAll", version=8, params=_params) - - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_client9.py b/build/lib/juju/client/_client9.py deleted file mode 100644 index 7709e7e3..00000000 --- a/build/lib/juju/client/_client9.py +++ /dev/null @@ -1,347 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client._definitions import * -from juju.client.facade import ReturnMapping, Type - - -class ModelManagerFacade(Type): - name = "ModelManager" - version = 9 - - @ReturnMapping(ErrorResults) - async def ChangeModelCredential(self, model_credentials=None): - """ChangeModelCredential changes cloud credential reference for models. - These new cloud credentials must already exist on the controller. - - model_credentials : typing.Sequence[~ChangeModelCredentialParams] - Returns -> ErrorResults - """ - if model_credentials is not None and not isinstance( - model_credentials, (bytes, str, list) - ): - raise Exception( - f"Expected model_credentials to be a Sequence, received: {type(model_credentials)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", - request="ChangeModelCredential", - version=9, - params=_params, - ) - _params["model-credentials"] = model_credentials - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelInfo) - async def CreateModel( - self, - cloud_tag=None, - config=None, - credential=None, - name=None, - owner_tag=None, - region=None, - ): - """CreateModel creates a new model using the account and - model config specified in the args. - - cloud_tag : str - config : typing.Mapping[str, typing.Any] - credential : str - name : str - owner_tag : str - region : str - Returns -> ModelInfo - """ - if cloud_tag is not None and not isinstance(cloud_tag, (bytes, str)): - raise Exception( - f"Expected cloud_tag to be a str, received: {type(cloud_tag)}" - ) - - if config is not None and not isinstance(config, dict): - raise Exception( - f"Expected config to be a Mapping, received: {type(config)}" - ) - - if credential is not None and not isinstance(credential, (bytes, str)): - raise Exception( - f"Expected credential to be a str, received: {type(credential)}" - ) - - if name is not None and not isinstance(name, (bytes, str)): - raise Exception(f"Expected name to be a str, received: {type(name)}") - - if owner_tag is not None and not isinstance(owner_tag, (bytes, str)): - raise Exception( - f"Expected owner_tag to be a str, received: {type(owner_tag)}" - ) - - if region is not None and not isinstance(region, (bytes, str)): - raise Exception(f"Expected region to be a str, received: {type(region)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="CreateModel", version=9, params=_params - ) - _params["cloud-tag"] = cloud_tag - _params["config"] = config - _params["credential"] = credential - _params["name"] = name - _params["owner-tag"] = owner_tag - _params["region"] = region - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def DestroyModels(self, models=None): - """DestroyModels will try to destroy the specified models. - If there is a block on destruction, this method will return an error. - From ModelManager v7 onwards, DestroyModels gains 'force' and 'max-wait' parameters. - - models : typing.Sequence[~DestroyModelParams] - Returns -> ErrorResults - """ - if models is not None and not isinstance(models, (bytes, str, list)): - raise Exception( - f"Expected models to be a Sequence, received: {type(models)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="DestroyModels", version=9, params=_params - ) - _params["models"] = models - reply = await self.rpc(msg) - return reply - - @ReturnMapping(StringResults) - async def DumpModels(self, entities=None, simplified=None): - """DumpModels will export the models into the database agnostic - representation. The user needs to either be a controller admin, or have - admin privileges on the model itself. - - entities : typing.Sequence[~Entity] - simplified : bool - Returns -> StringResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - if simplified is not None and not isinstance(simplified, bool): - raise Exception( - f"Expected simplified to be a bool, received: {type(simplified)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelManager", request="DumpModels", version=9, params=_params) - _params["entities"] = entities - _params["simplified"] = simplified - reply = await self.rpc(msg) - return reply - - @ReturnMapping(MapResults) - async def DumpModelsDB(self, entities=None): - """DumpModelsDB will gather all documents from all model collections - for the specified model. The map result contains a map of collection - names to lists of documents represented as maps. - - entities : typing.Sequence[~Entity] - Returns -> MapResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="DumpModelsDB", version=9, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelSummaryResults) - async def ListModelSummaries(self, all_=None, user_tag=None): - """ListModelSummaries returns models that the specified user - has access to in the current server. Controller admins (superuser) - can list models for any user. Other users - can only ask about their own models. - - all_ : bool - user_tag : str - Returns -> ModelSummaryResults - """ - if all_ is not None and not isinstance(all_, bool): - raise Exception(f"Expected all_ to be a bool, received: {type(all_)}") - - if user_tag is not None and not isinstance(user_tag, (bytes, str)): - raise Exception( - f"Expected user_tag to be a str, received: {type(user_tag)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="ListModelSummaries", version=9, params=_params - ) - _params["all"] = all_ - _params["user-tag"] = user_tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(UserModelList) - async def ListModels(self, tag=None): - """ListModels returns the models that the specified user - has access to in the current server. Controller admins (superuser) - can list models for any user. Other users - can only ask about their own models. - - tag : str - Returns -> UserModelList - """ - if tag is not None and not isinstance(tag, (bytes, str)): - raise Exception(f"Expected tag to be a str, received: {type(tag)}") - - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelManager", request="ListModels", version=9, params=_params) - _params["tag"] = tag - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelDefaultsResults) - async def ModelDefaultsForClouds(self, entities=None): - """ModelDefaultsForClouds returns the default config values for the specified - clouds. - - entities : typing.Sequence[~Entity] - Returns -> ModelDefaultsResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", - request="ModelDefaultsForClouds", - version=9, - params=_params, - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelInfoResults) - async def ModelInfo(self, entities=None): - """ModelInfo returns information about the specified models. - - entities : typing.Sequence[~Entity] - Returns -> ModelInfoResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict(type="ModelManager", request="ModelInfo", version=9, params=_params) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ModelStatusResults) - async def ModelStatus(self, entities=None): - """ModelStatus returns a summary of the model. - - entities : typing.Sequence[~Entity] - Returns -> ModelStatusResults - """ - if entities is not None and not isinstance(entities, (bytes, str, list)): - raise Exception( - f"Expected entities to be a Sequence, received: {type(entities)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="ModelStatus", version=9, params=_params - ) - _params["entities"] = entities - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def ModifyModelAccess(self, changes=None): - """ModifyModelAccess changes the model access granted to users. - - changes : typing.Sequence[~ModifyModelAccess] - Returns -> ErrorResults - """ - if changes is not None and not isinstance(changes, (bytes, str, list)): - raise Exception( - f"Expected changes to be a Sequence, received: {type(changes)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="ModifyModelAccess", version=9, params=_params - ) - _params["changes"] = changes - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def SetModelDefaults(self, config=None): - """SetModelDefaults writes new values for the specified default model settings. - - config : typing.Sequence[~ModelDefaultValues] - Returns -> ErrorResults - """ - if config is not None and not isinstance(config, (bytes, str, list)): - raise Exception( - f"Expected config to be a Sequence, received: {type(config)}" - ) - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="SetModelDefaults", version=9, params=_params - ) - _params["config"] = config - reply = await self.rpc(msg) - return reply - - @ReturnMapping(ErrorResults) - async def UnsetModelDefaults(self, keys=None): - """UnsetModelDefaults removes the specified default model settings. - - keys : typing.Sequence[~ModelUnsetKeys] - Returns -> ErrorResults - """ - if keys is not None and not isinstance(keys, (bytes, str, list)): - raise Exception(f"Expected keys to be a Sequence, received: {type(keys)}") - - # map input types to rpc msg - _params = dict() - msg = dict( - type="ModelManager", request="UnsetModelDefaults", version=9, params=_params - ) - _params["keys"] = keys - reply = await self.rpc(msg) - return reply diff --git a/build/lib/juju/client/_definitions.py b/build/lib/juju/client/_definitions.py deleted file mode 100644 index b01afc5e..00000000 --- a/build/lib/juju/client/_definitions.py +++ /dev/null @@ -1,22723 +0,0 @@ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -from juju.client.facade import Type - - -class AccessInfo(Type): - _toSchema = {"role": "role", "scope_tag": "scope-tag", "target_tag": "target-tag"} - _toPy = {"role": "role", "scope-tag": "scope_tag", "target-tag": "target_tag"} - - def __init__(self, role=None, scope_tag=None, target_tag=None, **unknown_fields): - """Role : str - scope_tag : str - target_tag : str - """ - role_ = role - scope_tag_ = scope_tag - target_tag_ = target_tag - - # Validate arguments against known Juju API types. - if role_ is not None and not isinstance(role_, (bytes, str)): - raise Exception(f"Expected role_ to be a str, received: {type(role_)}") - - if scope_tag_ is not None and not isinstance(scope_tag_, (bytes, str)): - raise Exception( - f"Expected scope_tag_ to be a str, received: {type(scope_tag_)}" - ) - - if target_tag_ is not None and not isinstance(target_tag_, (bytes, str)): - raise Exception( - f"Expected target_tag_ to be a str, received: {type(target_tag_)}" - ) - - self.role = role_ - self.scope_tag = scope_tag_ - self.target_tag = target_tag_ - self.unknown_fields = unknown_fields - - -class Action(Type): - _toSchema = { - "execution_group": "execution-group", - "name": "name", - "parallel": "parallel", - "parameters": "parameters", - "receiver": "receiver", - "tag": "tag", - } - _toPy = { - "execution-group": "execution_group", - "name": "name", - "parallel": "parallel", - "parameters": "parameters", - "receiver": "receiver", - "tag": "tag", - } - - def __init__( - self, - execution_group=None, - name=None, - parallel=None, - parameters=None, - receiver=None, - tag=None, - **unknown_fields, - ): - """execution_group : str - name : str - parallel : bool - parameters : typing.Mapping[str, typing.Any] - receiver : str - tag : str - """ - execution_group_ = execution_group - name_ = name - parallel_ = parallel - parameters_ = parameters - receiver_ = receiver - tag_ = tag - - # Validate arguments against known Juju API types. - if execution_group_ is not None and not isinstance( - execution_group_, (bytes, str) - ): - raise Exception( - f"Expected execution_group_ to be a str, received: {type(execution_group_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if parallel_ is not None and not isinstance(parallel_, bool): - raise Exception( - f"Expected parallel_ to be a bool, received: {type(parallel_)}" - ) - - if parameters_ is not None and not isinstance(parameters_, dict): - raise Exception( - f"Expected parameters_ to be a Mapping, received: {type(parameters_)}" - ) - - if receiver_ is not None and not isinstance(receiver_, (bytes, str)): - raise Exception( - f"Expected receiver_ to be a str, received: {type(receiver_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.execution_group = execution_group_ - self.name = name_ - self.parallel = parallel_ - self.parameters = parameters_ - self.receiver = receiver_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class ActionMessage(Type): - _toSchema = {"message": "message", "timestamp": "timestamp"} - _toPy = {"message": "message", "timestamp": "timestamp"} - - def __init__(self, message=None, timestamp=None, **unknown_fields): - """Message : str - timestamp : str - """ - message_ = message - timestamp_ = timestamp - - # Validate arguments against known Juju API types. - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - if timestamp_ is not None and not isinstance(timestamp_, (bytes, str)): - raise Exception( - f"Expected timestamp_ to be a str, received: {type(timestamp_)}" - ) - - self.message = message_ - self.timestamp = timestamp_ - self.unknown_fields = unknown_fields - - -class ActionResult(Type): - _toSchema = { - "action": "action", - "completed": "completed", - "enqueued": "enqueued", - "error": "error", - "log": "log", - "message": "message", - "output": "output", - "started": "started", - "status": "status", - } - _toPy = { - "action": "action", - "completed": "completed", - "enqueued": "enqueued", - "error": "error", - "log": "log", - "message": "message", - "output": "output", - "started": "started", - "status": "status", - } - - def __init__( - self, - action=None, - completed=None, - enqueued=None, - error=None, - log=None, - message=None, - output=None, - started=None, - status=None, - **unknown_fields, - ): - """Action : Action - completed : str - enqueued : str - error : Error - log : typing.Sequence[~ActionMessage] - message : str - output : typing.Mapping[str, typing.Any] - started : str - status : str - """ - action_ = Action.from_json(action) if action else None - completed_ = completed - enqueued_ = enqueued - error_ = Error.from_json(error) if error else None - log_ = [ActionMessage.from_json(o) for o in log or []] - message_ = message - output_ = output - started_ = started - status_ = status - - # Validate arguments against known Juju API types. - if action_ is not None and not isinstance(action_, (dict, Action)): - raise Exception( - f"Expected action_ to be a Action, received: {type(action_)}" - ) - - if completed_ is not None and not isinstance(completed_, (bytes, str)): - raise Exception( - f"Expected completed_ to be a str, received: {type(completed_)}" - ) - - if enqueued_ is not None and not isinstance(enqueued_, (bytes, str)): - raise Exception( - f"Expected enqueued_ to be a str, received: {type(enqueued_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if log_ is not None and not isinstance(log_, (bytes, str, list)): - raise Exception(f"Expected log_ to be a Sequence, received: {type(log_)}") - - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - if output_ is not None and not isinstance(output_, dict): - raise Exception( - f"Expected output_ to be a Mapping, received: {type(output_)}" - ) - - if started_ is not None and not isinstance(started_, (bytes, str)): - raise Exception( - f"Expected started_ to be a str, received: {type(started_)}" - ) - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - self.action = action_ - self.completed = completed_ - self.enqueued = enqueued_ - self.error = error_ - self.log = log_ - self.message = message_ - self.output = output_ - self.started = started_ - self.status = status_ - self.unknown_fields = unknown_fields - - -class ActionResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ActionResult]""" - results_ = [ActionResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ActionSpec(Type): - _toSchema = {"description": "description", "params": "params"} - _toPy = {"description": "description", "params": "params"} - - def __init__(self, description=None, params=None, **unknown_fields): - """Description : str - params : typing.Mapping[str, typing.Any] - """ - description_ = description - params_ = params - - # Validate arguments against known Juju API types. - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if params_ is not None and not isinstance(params_, dict): - raise Exception( - f"Expected params_ to be a Mapping, received: {type(params_)}" - ) - - self.description = description_ - self.params = params_ - self.unknown_fields = unknown_fields - - -class Actions(Type): - _toSchema = {"actions": "actions"} - _toPy = {"actions": "actions"} - - def __init__(self, actions=None, **unknown_fields): - """Actions : typing.Sequence[~Action]""" - actions_ = [Action.from_json(o) for o in actions or []] - - # Validate arguments against known Juju API types. - if actions_ is not None and not isinstance(actions_, (bytes, str, list)): - raise Exception( - f"Expected actions_ to be a Sequence, received: {type(actions_)}" - ) - - self.actions = actions_ - self.unknown_fields = unknown_fields - - -class AddApplicationOffer(Type): - _toSchema = { - "application_description": "application-description", - "application_name": "application-name", - "endpoints": "endpoints", - "model_tag": "model-tag", - "offer_name": "offer-name", - "owner_tag": "owner-tag", - } - _toPy = { - "application-description": "application_description", - "application-name": "application_name", - "endpoints": "endpoints", - "model-tag": "model_tag", - "offer-name": "offer_name", - "owner-tag": "owner_tag", - } - - def __init__( - self, - application_description=None, - application_name=None, - endpoints=None, - model_tag=None, - offer_name=None, - owner_tag=None, - **unknown_fields, - ): - """application_description : str - application_name : str - endpoints : typing.Mapping[str, str] - model_tag : str - offer_name : str - owner_tag : str - """ - application_description_ = application_description - application_name_ = application_name - endpoints_ = endpoints - model_tag_ = model_tag - offer_name_ = offer_name - owner_tag_ = owner_tag - - # Validate arguments against known Juju API types. - if application_description_ is not None and not isinstance( - application_description_, (bytes, str) - ): - raise Exception( - f"Expected application_description_ to be a str, received: {type(application_description_)}" - ) - - if application_name_ is not None and not isinstance( - application_name_, (bytes, str) - ): - raise Exception( - f"Expected application_name_ to be a str, received: {type(application_name_)}" - ) - - if endpoints_ is not None and not isinstance(endpoints_, dict): - raise Exception( - f"Expected endpoints_ to be a Mapping, received: {type(endpoints_)}" - ) - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - self.application_description = application_description_ - self.application_name = application_name_ - self.endpoints = endpoints_ - self.model_tag = model_tag_ - self.offer_name = offer_name_ - self.owner_tag = owner_tag_ - self.unknown_fields = unknown_fields - - -class AddApplicationOffers(Type): - _toSchema = {"offers": "Offers"} - _toPy = {"Offers": "offers"} - - def __init__(self, offers=None, **unknown_fields): - """Offers : typing.Sequence[~AddApplicationOffer]""" - offers_ = [AddApplicationOffer.from_json(o) for o in offers or []] - - # Validate arguments against known Juju API types. - if offers_ is not None and not isinstance(offers_, (bytes, str, list)): - raise Exception( - f"Expected offers_ to be a Sequence, received: {type(offers_)}" - ) - - self.offers = offers_ - self.unknown_fields = unknown_fields - - -class AddApplicationUnits(Type): - _toSchema = { - "application": "application", - "attach_storage": "attach-storage", - "num_units": "num-units", - "placement": "placement", - "policy": "policy", - } - _toPy = { - "application": "application", - "attach-storage": "attach_storage", - "num-units": "num_units", - "placement": "placement", - "policy": "policy", - } - - def __init__( - self, - application=None, - attach_storage=None, - num_units=None, - placement=None, - policy=None, - **unknown_fields, - ): - """Application : str - attach_storage : typing.Sequence[str] - num_units : int - placement : typing.Sequence[~Placement] - policy : str - """ - application_ = application - attach_storage_ = attach_storage - num_units_ = num_units - placement_ = [Placement.from_json(o) for o in placement or []] - policy_ = policy - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if attach_storage_ is not None and not isinstance( - attach_storage_, (bytes, str, list) - ): - raise Exception( - f"Expected attach_storage_ to be a Sequence, received: {type(attach_storage_)}" - ) - - if num_units_ is not None and not isinstance(num_units_, int): - raise Exception( - f"Expected num_units_ to be a int, received: {type(num_units_)}" - ) - - if placement_ is not None and not isinstance(placement_, (bytes, str, list)): - raise Exception( - f"Expected placement_ to be a Sequence, received: {type(placement_)}" - ) - - if policy_ is not None and not isinstance(policy_, (bytes, str)): - raise Exception(f"Expected policy_ to be a str, received: {type(policy_)}") - - self.application = application_ - self.attach_storage = attach_storage_ - self.num_units = num_units_ - self.placement = placement_ - self.policy = policy_ - self.unknown_fields = unknown_fields - - -class AddApplicationUnitsResults(Type): - _toSchema = {"units": "units"} - _toPy = {"units": "units"} - - def __init__(self, units=None, **unknown_fields): - """Units : typing.Sequence[str]""" - units_ = units - - # Validate arguments against known Juju API types. - if units_ is not None and not isinstance(units_, (bytes, str, list)): - raise Exception( - f"Expected units_ to be a Sequence, received: {type(units_)}" - ) - - self.units = units_ - self.unknown_fields = unknown_fields - - -class AddCharmWithOrigin(Type): - _toSchema = {"charm_origin": "charm-origin", "force": "force", "url": "url"} - _toPy = {"charm-origin": "charm_origin", "force": "force", "url": "url"} - - def __init__(self, charm_origin=None, force=None, url=None, **unknown_fields): - """charm_origin : CharmOrigin - force : bool - url : str - """ - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - force_ = force - url_ = url - - # Validate arguments against known Juju API types. - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if url_ is not None and not isinstance(url_, (bytes, str)): - raise Exception(f"Expected url_ to be a str, received: {type(url_)}") - - self.charm_origin = charm_origin_ - self.force = force_ - self.url = url_ - self.unknown_fields = unknown_fields - - -class AddCloudArgs(Type): - _toSchema = {"cloud": "cloud", "force": "force", "name": "name"} - _toPy = {"cloud": "cloud", "force": "force", "name": "name"} - - def __init__(self, cloud=None, force=None, name=None, **unknown_fields): - """Cloud : Cloud - force : bool - name : str - """ - cloud_ = Cloud.from_json(cloud) if cloud else None - force_ = force - name_ = name - - # Validate arguments against known Juju API types. - if cloud_ is not None and not isinstance(cloud_, (dict, Cloud)): - raise Exception(f"Expected cloud_ to be a Cloud, received: {type(cloud_)}") - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - self.cloud = cloud_ - self.force = force_ - self.name = name_ - self.unknown_fields = unknown_fields - - -class AddMachineParams(Type): - _toSchema = { - "addresses": "addresses", - "base": "base", - "constraints": "constraints", - "container_type": "container-type", - "disks": "disks", - "hardware_characteristics": "hardware-characteristics", - "instance_id": "instance-id", - "jobs": "jobs", - "nonce": "nonce", - "parent_id": "parent-id", - "placement": "placement", - } - _toPy = { - "addresses": "addresses", - "base": "base", - "constraints": "constraints", - "container-type": "container_type", - "disks": "disks", - "hardware-characteristics": "hardware_characteristics", - "instance-id": "instance_id", - "jobs": "jobs", - "nonce": "nonce", - "parent-id": "parent_id", - "placement": "placement", - } - - def __init__( - self, - addresses=None, - base=None, - constraints=None, - container_type=None, - disks=None, - hardware_characteristics=None, - instance_id=None, - jobs=None, - nonce=None, - parent_id=None, - placement=None, - **unknown_fields, - ): - """Addresses : typing.Sequence[~Address] - base : Base - constraints : Value - container_type : str - disks : typing.Sequence[~Constraints] - hardware_characteristics : HardwareCharacteristics - instance_id : str - jobs : typing.Sequence[str] - nonce : str - parent_id : str - placement : Placement - """ - addresses_ = [Address.from_json(o) for o in addresses or []] - base_ = Base.from_json(base) if base else None - constraints_ = Value.from_json(constraints) if constraints else None - container_type_ = container_type - disks_ = [Constraints.from_json(o) for o in disks or []] - hardware_characteristics_ = ( - HardwareCharacteristics.from_json(hardware_characteristics) - if hardware_characteristics - else None - ) - instance_id_ = instance_id - jobs_ = jobs - nonce_ = nonce - parent_id_ = parent_id - placement_ = Placement.from_json(placement) if placement else None - - # Validate arguments against known Juju API types. - if addresses_ is not None and not isinstance(addresses_, (bytes, str, list)): - raise Exception( - f"Expected addresses_ to be a Sequence, received: {type(addresses_)}" - ) - - if base_ is not None and not isinstance(base_, (dict, Base)): - raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") - - if constraints_ is not None and not isinstance(constraints_, (dict, Value)): - raise Exception( - f"Expected constraints_ to be a Value, received: {type(constraints_)}" - ) - - if container_type_ is not None and not isinstance( - container_type_, (bytes, str) - ): - raise Exception( - f"Expected container_type_ to be a str, received: {type(container_type_)}" - ) - - if disks_ is not None and not isinstance(disks_, (bytes, str, list)): - raise Exception( - f"Expected disks_ to be a Sequence, received: {type(disks_)}" - ) - - if hardware_characteristics_ is not None and not isinstance( - hardware_characteristics_, (dict, HardwareCharacteristics) - ): - raise Exception( - f"Expected hardware_characteristics_ to be a HardwareCharacteristics, received: {type(hardware_characteristics_)}" - ) - - if instance_id_ is not None and not isinstance(instance_id_, (bytes, str)): - raise Exception( - f"Expected instance_id_ to be a str, received: {type(instance_id_)}" - ) - - if jobs_ is not None and not isinstance(jobs_, (bytes, str, list)): - raise Exception(f"Expected jobs_ to be a Sequence, received: {type(jobs_)}") - - if nonce_ is not None and not isinstance(nonce_, (bytes, str)): - raise Exception(f"Expected nonce_ to be a str, received: {type(nonce_)}") - - if parent_id_ is not None and not isinstance(parent_id_, (bytes, str)): - raise Exception( - f"Expected parent_id_ to be a str, received: {type(parent_id_)}" - ) - - if placement_ is not None and not isinstance(placement_, (dict, Placement)): - raise Exception( - f"Expected placement_ to be a Placement, received: {type(placement_)}" - ) - - self.addresses = addresses_ - self.base = base_ - self.constraints = constraints_ - self.container_type = container_type_ - self.disks = disks_ - self.hardware_characteristics = hardware_characteristics_ - self.instance_id = instance_id_ - self.jobs = jobs_ - self.nonce = nonce_ - self.parent_id = parent_id_ - self.placement = placement_ - self.unknown_fields = unknown_fields - - -class AddMachines(Type): - _toSchema = {"params": "params"} - _toPy = {"params": "params"} - - def __init__(self, params=None, **unknown_fields): - """Params : typing.Sequence[~AddMachineParams]""" - params_ = [AddMachineParams.from_json(o) for o in params or []] - - # Validate arguments against known Juju API types. - if params_ is not None and not isinstance(params_, (bytes, str, list)): - raise Exception( - f"Expected params_ to be a Sequence, received: {type(params_)}" - ) - - self.params = params_ - self.unknown_fields = unknown_fields - - -class AddMachinesResult(Type): - _toSchema = {"error": "error", "machine": "machine"} - _toPy = {"error": "error", "machine": "machine"} - - def __init__(self, error=None, machine=None, **unknown_fields): - """Error : Error - machine : str - """ - error_ = Error.from_json(error) if error else None - machine_ = machine - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if machine_ is not None and not isinstance(machine_, (bytes, str)): - raise Exception( - f"Expected machine_ to be a str, received: {type(machine_)}" - ) - - self.error = error_ - self.machine = machine_ - self.unknown_fields = unknown_fields - - -class AddMachinesResults(Type): - _toSchema = {"machines": "machines"} - _toPy = {"machines": "machines"} - - def __init__(self, machines=None, **unknown_fields): - """Machines : typing.Sequence[~AddMachinesResult]""" - machines_ = [AddMachinesResult.from_json(o) for o in machines or []] - - # Validate arguments against known Juju API types. - if machines_ is not None and not isinstance(machines_, (bytes, str, list)): - raise Exception( - f"Expected machines_ to be a Sequence, received: {type(machines_)}" - ) - - self.machines = machines_ - self.unknown_fields = unknown_fields - - -class AddPendingResourcesArgsV2(Type): - _toSchema = { - "charm_origin": "charm-origin", - "entity": "Entity", - "macaroon": "macaroon", - "resources": "resources", - "tag": "tag", - "url": "url", - } - _toPy = { - "Entity": "entity", - "charm-origin": "charm_origin", - "macaroon": "macaroon", - "resources": "resources", - "tag": "tag", - "url": "url", - } - - def __init__( - self, - entity=None, - charm_origin=None, - macaroon=None, - resources=None, - tag=None, - url=None, - **unknown_fields, - ): - """Entity : Entity - charm_origin : CharmOrigin - macaroon : Macaroon - resources : typing.Sequence[~CharmResource] - tag : str - url : str - """ - entity_ = Entity.from_json(entity) if entity else None - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - macaroon_ = Macaroon.from_json(macaroon) if macaroon else None - resources_ = [CharmResource.from_json(o) for o in resources or []] - tag_ = tag - url_ = url - - # Validate arguments against known Juju API types. - if entity_ is not None and not isinstance(entity_, (dict, Entity)): - raise Exception( - f"Expected entity_ to be a Entity, received: {type(entity_)}" - ) - - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): - raise Exception( - f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" - ) - - if resources_ is not None and not isinstance(resources_, (bytes, str, list)): - raise Exception( - f"Expected resources_ to be a Sequence, received: {type(resources_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - if url_ is not None and not isinstance(url_, (bytes, str)): - raise Exception(f"Expected url_ to be a str, received: {type(url_)}") - - self.entity = entity_ - self.charm_origin = charm_origin_ - self.macaroon = macaroon_ - self.resources = resources_ - self.tag = tag_ - self.url = url_ - self.unknown_fields = unknown_fields - - -class AddPendingResourcesResult(Type): - _toSchema = { - "error": "error", - "errorresult": "ErrorResult", - "pending_ids": "pending-ids", - } - _toPy = { - "ErrorResult": "errorresult", - "error": "error", - "pending-ids": "pending_ids", - } - - def __init__( - self, errorresult=None, error=None, pending_ids=None, **unknown_fields - ): - """Errorresult : ErrorResult - error : Error - pending_ids : typing.Sequence[str] - """ - errorresult_ = ErrorResult.from_json(errorresult) if errorresult else None - error_ = Error.from_json(error) if error else None - pending_ids_ = pending_ids - - # Validate arguments against known Juju API types. - if errorresult_ is not None and not isinstance( - errorresult_, (dict, ErrorResult) - ): - raise Exception( - f"Expected errorresult_ to be a ErrorResult, received: {type(errorresult_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if pending_ids_ is not None and not isinstance( - pending_ids_, (bytes, str, list) - ): - raise Exception( - f"Expected pending_ids_ to be a Sequence, received: {type(pending_ids_)}" - ) - - self.errorresult = errorresult_ - self.error = error_ - self.pending_ids = pending_ids_ - self.unknown_fields = unknown_fields - - -class AddRelation(Type): - _toSchema = {"endpoints": "endpoints", "via_cidrs": "via-cidrs"} - _toPy = {"endpoints": "endpoints", "via-cidrs": "via_cidrs"} - - def __init__(self, endpoints=None, via_cidrs=None, **unknown_fields): - """Endpoints : typing.Sequence[str] - via_cidrs : typing.Sequence[str] - """ - endpoints_ = endpoints - via_cidrs_ = via_cidrs - - # Validate arguments against known Juju API types. - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if via_cidrs_ is not None and not isinstance(via_cidrs_, (bytes, str, list)): - raise Exception( - f"Expected via_cidrs_ to be a Sequence, received: {type(via_cidrs_)}" - ) - - self.endpoints = endpoints_ - self.via_cidrs = via_cidrs_ - self.unknown_fields = unknown_fields - - -class AddRelationResults(Type): - _toSchema = {"endpoints": "endpoints"} - _toPy = {"endpoints": "endpoints"} - - def __init__(self, endpoints=None, **unknown_fields): - """Endpoints : typing.Mapping[str, ~CharmRelation]""" - endpoints_ = { - k: CharmRelation.from_json(v) for k, v in (endpoints or dict()).items() - } - - # Validate arguments against known Juju API types. - if endpoints_ is not None and not isinstance(endpoints_, dict): - raise Exception( - f"Expected endpoints_ to be a Mapping, received: {type(endpoints_)}" - ) - - self.endpoints = endpoints_ - self.unknown_fields = unknown_fields - - -class AddSecretBackendArg(Type): - _toSchema = { - "backend_type": "backend-type", - "config": "config", - "id_": "id", - "name": "name", - "secretbackend": "SecretBackend", - "token_rotate_interval": "token-rotate-interval", - } - _toPy = { - "SecretBackend": "secretbackend", - "backend-type": "backend_type", - "config": "config", - "id": "id_", - "name": "name", - "token-rotate-interval": "token_rotate_interval", - } - - def __init__( - self, - secretbackend=None, - backend_type=None, - config=None, - id_=None, - name=None, - token_rotate_interval=None, - **unknown_fields, - ): - """Secretbackend : SecretBackend - backend_type : str - config : typing.Mapping[str, typing.Any] - id_ : str - name : str - token_rotate_interval : int - """ - secretbackend_ = ( - SecretBackend.from_json(secretbackend) if secretbackend else None - ) - backend_type_ = backend_type - config_ = config - id__ = id_ - name_ = name - token_rotate_interval_ = token_rotate_interval - - # Validate arguments against known Juju API types. - if secretbackend_ is not None and not isinstance( - secretbackend_, (dict, SecretBackend) - ): - raise Exception( - f"Expected secretbackend_ to be a SecretBackend, received: {type(secretbackend_)}" - ) - - if backend_type_ is not None and not isinstance(backend_type_, (bytes, str)): - raise Exception( - f"Expected backend_type_ to be a str, received: {type(backend_type_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if token_rotate_interval_ is not None and not isinstance( - token_rotate_interval_, int - ): - raise Exception( - f"Expected token_rotate_interval_ to be a int, received: {type(token_rotate_interval_)}" - ) - - self.secretbackend = secretbackend_ - self.backend_type = backend_type_ - self.config = config_ - self.id_ = id__ - self.name = name_ - self.token_rotate_interval = token_rotate_interval_ - self.unknown_fields = unknown_fields - - -class AddSecretBackendArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~AddSecretBackendArg]""" - args_ = [AddSecretBackendArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class AddStorageDetails(Type): - _toSchema = {"storage_tags": "storage-tags"} - _toPy = {"storage-tags": "storage_tags"} - - def __init__(self, storage_tags=None, **unknown_fields): - """storage_tags : typing.Sequence[str]""" - storage_tags_ = storage_tags - - # Validate arguments against known Juju API types. - if storage_tags_ is not None and not isinstance( - storage_tags_, (bytes, str, list) - ): - raise Exception( - f"Expected storage_tags_ to be a Sequence, received: {type(storage_tags_)}" - ) - - self.storage_tags = storage_tags_ - self.unknown_fields = unknown_fields - - -class AddStorageResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : AddStorageDetails - """ - error_ = Error.from_json(error) if error else None - result_ = AddStorageDetails.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, AddStorageDetails)): - raise Exception( - f"Expected result_ to be a AddStorageDetails, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class AddStorageResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~AddStorageResult]""" - results_ = [AddStorageResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class AddUser(Type): - _toSchema = { - "display_name": "display-name", - "password": "password", - "username": "username", - } - _toPy = { - "display-name": "display_name", - "password": "password", - "username": "username", - } - - def __init__( - self, display_name=None, password=None, username=None, **unknown_fields - ): - """display_name : str - password : str - username : str - """ - display_name_ = display_name - password_ = password - username_ = username - - # Validate arguments against known Juju API types. - if display_name_ is not None and not isinstance(display_name_, (bytes, str)): - raise Exception( - f"Expected display_name_ to be a str, received: {type(display_name_)}" - ) - - if password_ is not None and not isinstance(password_, (bytes, str)): - raise Exception( - f"Expected password_ to be a str, received: {type(password_)}" - ) - - if username_ is not None and not isinstance(username_, (bytes, str)): - raise Exception( - f"Expected username_ to be a str, received: {type(username_)}" - ) - - self.display_name = display_name_ - self.password = password_ - self.username = username_ - self.unknown_fields = unknown_fields - - -class AddUserResult(Type): - _toSchema = {"error": "error", "secret_key": "secret-key", "tag": "tag"} - _toPy = {"error": "error", "secret-key": "secret_key", "tag": "tag"} - - def __init__(self, error=None, secret_key=None, tag=None, **unknown_fields): - """Error : Error - secret_key : typing.Sequence[int] - tag : str - """ - error_ = Error.from_json(error) if error else None - secret_key_ = secret_key - tag_ = tag - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if secret_key_ is not None and not isinstance(secret_key_, (bytes, str, list)): - raise Exception( - f"Expected secret_key_ to be a Sequence, received: {type(secret_key_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.error = error_ - self.secret_key = secret_key_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class AddUserResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~AddUserResult]""" - results_ = [AddUserResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class AddUsers(Type): - _toSchema = {"users": "users"} - _toPy = {"users": "users"} - - def __init__(self, users=None, **unknown_fields): - """Users : typing.Sequence[~AddUser]""" - users_ = [AddUser.from_json(o) for o in users or []] - - # Validate arguments against known Juju API types. - if users_ is not None and not isinstance(users_, (bytes, str, list)): - raise Exception( - f"Expected users_ to be a Sequence, received: {type(users_)}" - ) - - self.users = users_ - self.unknown_fields = unknown_fields - - -class Address(Type): - _toSchema = { - "cidr": "cidr", - "config_type": "config-type", - "is_secondary": "is-secondary", - "scope": "scope", - "space_id": "space-id", - "space_name": "space-name", - "type_": "type", - "value": "value", - } - _toPy = { - "cidr": "cidr", - "config-type": "config_type", - "is-secondary": "is_secondary", - "scope": "scope", - "space-id": "space_id", - "space-name": "space_name", - "type": "type_", - "value": "value", - } - - def __init__( - self, - cidr=None, - config_type=None, - is_secondary=None, - scope=None, - space_id=None, - space_name=None, - type_=None, - value=None, - **unknown_fields, - ): - """Cidr : str - config_type : str - is_secondary : bool - scope : str - space_id : str - space_name : str - type_ : str - value : str - """ - cidr_ = cidr - config_type_ = config_type - is_secondary_ = is_secondary - scope_ = scope - space_id_ = space_id - space_name_ = space_name - type__ = type_ - value_ = value - - # Validate arguments against known Juju API types. - if cidr_ is not None and not isinstance(cidr_, (bytes, str)): - raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") - - if config_type_ is not None and not isinstance(config_type_, (bytes, str)): - raise Exception( - f"Expected config_type_ to be a str, received: {type(config_type_)}" - ) - - if is_secondary_ is not None and not isinstance(is_secondary_, bool): - raise Exception( - f"Expected is_secondary_ to be a bool, received: {type(is_secondary_)}" - ) - - if scope_ is not None and not isinstance(scope_, (bytes, str)): - raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") - - if space_id_ is not None and not isinstance(space_id_, (bytes, str)): - raise Exception( - f"Expected space_id_ to be a str, received: {type(space_id_)}" - ) - - if space_name_ is not None and not isinstance(space_name_, (bytes, str)): - raise Exception( - f"Expected space_name_ to be a str, received: {type(space_name_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - if value_ is not None and not isinstance(value_, (bytes, str)): - raise Exception(f"Expected value_ to be a str, received: {type(value_)}") - - self.cidr = cidr_ - self.config_type = config_type_ - self.is_secondary = is_secondary_ - self.scope = scope_ - self.space_id = space_id_ - self.space_name = space_name_ - self.type_ = type__ - self.value = value_ - self.unknown_fields = unknown_fields - - -class AllWatcherId(Type): - _toSchema = {"watcher_id": "watcher-id"} - _toPy = {"watcher-id": "watcher_id"} - - def __init__(self, watcher_id=None, **unknown_fields): - """watcher_id : str""" - watcher_id_ = watcher_id - - # Validate arguments against known Juju API types. - if watcher_id_ is not None and not isinstance(watcher_id_, (bytes, str)): - raise Exception( - f"Expected watcher_id_ to be a str, received: {type(watcher_id_)}" - ) - - self.watcher_id = watcher_id_ - self.unknown_fields = unknown_fields - - -class AllWatcherNextResults(Type): - _toSchema = {"deltas": "deltas"} - _toPy = {"deltas": "deltas"} - - def __init__(self, deltas=None, **unknown_fields): - """Deltas : typing.Sequence[~Delta]""" - deltas_ = [Delta.from_json(o) for o in deltas or []] - - # Validate arguments against known Juju API types. - if deltas_ is not None and not isinstance(deltas_, (bytes, str, list)): - raise Exception( - f"Expected deltas_ to be a Sequence, received: {type(deltas_)}" - ) - - self.deltas = deltas_ - self.unknown_fields = unknown_fields - - -class AnnotationsGetResult(Type): - _toSchema = {"annotations": "annotations", "entity": "entity", "error": "error"} - _toPy = {"annotations": "annotations", "entity": "entity", "error": "error"} - - def __init__(self, annotations=None, entity=None, error=None, **unknown_fields): - """Annotations : typing.Mapping[str, str] - entity : str - error : ErrorResult - """ - annotations_ = annotations - entity_ = entity - error_ = ErrorResult.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if annotations_ is not None and not isinstance(annotations_, dict): - raise Exception( - f"Expected annotations_ to be a Mapping, received: {type(annotations_)}" - ) - - if entity_ is not None and not isinstance(entity_, (bytes, str)): - raise Exception(f"Expected entity_ to be a str, received: {type(entity_)}") - - if error_ is not None and not isinstance(error_, (dict, ErrorResult)): - raise Exception( - f"Expected error_ to be a ErrorResult, received: {type(error_)}" - ) - - self.annotations = annotations_ - self.entity = entity_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class AnnotationsGetResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~AnnotationsGetResult]""" - results_ = [AnnotationsGetResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class AnnotationsSet(Type): - _toSchema = {"annotations": "annotations"} - _toPy = {"annotations": "annotations"} - - def __init__(self, annotations=None, **unknown_fields): - """Annotations : typing.Sequence[~EntityAnnotations]""" - annotations_ = [EntityAnnotations.from_json(o) for o in annotations or []] - - # Validate arguments against known Juju API types. - if annotations_ is not None and not isinstance( - annotations_, (bytes, str, list) - ): - raise Exception( - f"Expected annotations_ to be a Sequence, received: {type(annotations_)}" - ) - - self.annotations = annotations_ - self.unknown_fields = unknown_fields - - -class ApplicationCharmActionsResult(Type): - _toSchema = { - "actions": "actions", - "application_tag": "application-tag", - "error": "error", - } - _toPy = { - "actions": "actions", - "application-tag": "application_tag", - "error": "error", - } - - def __init__( - self, actions=None, application_tag=None, error=None, **unknown_fields - ): - """Actions : typing.Mapping[str, ~ActionSpec] - application_tag : str - error : Error - """ - actions_ = {k: ActionSpec.from_json(v) for k, v in (actions or dict()).items()} - application_tag_ = application_tag - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if actions_ is not None and not isinstance(actions_, dict): - raise Exception( - f"Expected actions_ to be a Mapping, received: {type(actions_)}" - ) - - if application_tag_ is not None and not isinstance( - application_tag_, (bytes, str) - ): - raise Exception( - f"Expected application_tag_ to be a str, received: {type(application_tag_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.actions = actions_ - self.application_tag = application_tag_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class ApplicationCharmPlacement(Type): - _toSchema = {"application": "application", "charm_url": "charm-url"} - _toPy = {"application": "application", "charm-url": "charm_url"} - - def __init__(self, application=None, charm_url=None, **unknown_fields): - """Application : str - charm_url : str - """ - application_ = application - charm_url_ = charm_url - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): - raise Exception( - f"Expected charm_url_ to be a str, received: {type(charm_url_)}" - ) - - self.application = application_ - self.charm_url = charm_url_ - self.unknown_fields = unknown_fields - - -class ApplicationCharmPlacements(Type): - _toSchema = {"placements": "placements"} - _toPy = {"placements": "placements"} - - def __init__(self, placements=None, **unknown_fields): - """Placements : typing.Sequence[~ApplicationCharmPlacement]""" - placements_ = [ApplicationCharmPlacement.from_json(o) for o in placements or []] - - # Validate arguments against known Juju API types. - if placements_ is not None and not isinstance(placements_, (bytes, str, list)): - raise Exception( - f"Expected placements_ to be a Sequence, received: {type(placements_)}" - ) - - self.placements = placements_ - self.unknown_fields = unknown_fields - - -class ApplicationCharmRelations(Type): - _toSchema = {"application": "application"} - _toPy = {"application": "application"} - - def __init__(self, application=None, **unknown_fields): - """Application : str""" - application_ = application - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - self.application = application_ - self.unknown_fields = unknown_fields - - -class ApplicationCharmRelationsResults(Type): - _toSchema = {"charm_relations": "charm-relations"} - _toPy = {"charm-relations": "charm_relations"} - - def __init__(self, charm_relations=None, **unknown_fields): - """charm_relations : typing.Sequence[str]""" - charm_relations_ = charm_relations - - # Validate arguments against known Juju API types. - if charm_relations_ is not None and not isinstance( - charm_relations_, (bytes, str, list) - ): - raise Exception( - f"Expected charm_relations_ to be a Sequence, received: {type(charm_relations_)}" - ) - - self.charm_relations = charm_relations_ - self.unknown_fields = unknown_fields - - -class ApplicationConfigUnsetArgs(Type): - _toSchema = {"args": "Args"} - _toPy = {"Args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~ApplicationUnset]""" - args_ = [ApplicationUnset.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class ApplicationConstraint(Type): - _toSchema = {"constraints": "constraints", "error": "error"} - _toPy = {"constraints": "constraints", "error": "error"} - - def __init__(self, constraints=None, error=None, **unknown_fields): - """Constraints : Value - error : Error - """ - constraints_ = Value.from_json(constraints) if constraints else None - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if constraints_ is not None and not isinstance(constraints_, (dict, Value)): - raise Exception( - f"Expected constraints_ to be a Value, received: {type(constraints_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.constraints = constraints_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class ApplicationDeploy(Type): - _toSchema = { - "application": "application", - "attach_storage": "attach-storage", - "channel": "channel", - "charm_origin": "charm-origin", - "charm_url": "charm-url", - "config": "config", - "config_yaml": "config-yaml", - "constraints": "constraints", - "devices": "devices", - "endpoint_bindings": "endpoint-bindings", - "force": "Force", - "num_units": "num-units", - "placement": "placement", - "policy": "policy", - "resources": "resources", - "storage": "storage", - } - _toPy = { - "Force": "force", - "application": "application", - "attach-storage": "attach_storage", - "channel": "channel", - "charm-origin": "charm_origin", - "charm-url": "charm_url", - "config": "config", - "config-yaml": "config_yaml", - "constraints": "constraints", - "devices": "devices", - "endpoint-bindings": "endpoint_bindings", - "num-units": "num_units", - "placement": "placement", - "policy": "policy", - "resources": "resources", - "storage": "storage", - } - - def __init__( - self, - force=None, - application=None, - attach_storage=None, - channel=None, - charm_origin=None, - charm_url=None, - config=None, - config_yaml=None, - constraints=None, - devices=None, - endpoint_bindings=None, - num_units=None, - placement=None, - policy=None, - resources=None, - storage=None, - **unknown_fields, - ): - """Force : bool - application : str - attach_storage : typing.Sequence[str] - channel : str - charm_origin : CharmOrigin - charm_url : str - config : typing.Mapping[str, str] - config_yaml : str - constraints : Value - devices : typing.Mapping[str, ~Constraints] - endpoint_bindings : typing.Mapping[str, str] - num_units : int - placement : typing.Sequence[~Placement] - policy : str - resources : typing.Mapping[str, str] - storage : typing.Mapping[str, ~Constraints] - """ - force_ = force - application_ = application - attach_storage_ = attach_storage - channel_ = channel - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - charm_url_ = charm_url - config_ = config - config_yaml_ = config_yaml - constraints_ = Value.from_json(constraints) if constraints else None - devices_ = {k: Constraints.from_json(v) for k, v in (devices or dict()).items()} - endpoint_bindings_ = endpoint_bindings - num_units_ = num_units - placement_ = [Placement.from_json(o) for o in placement or []] - policy_ = policy - resources_ = resources - storage_ = {k: Constraints.from_json(v) for k, v in (storage or dict()).items()} - - # Validate arguments against known Juju API types. - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if attach_storage_ is not None and not isinstance( - attach_storage_, (bytes, str, list) - ): - raise Exception( - f"Expected attach_storage_ to be a Sequence, received: {type(attach_storage_)}" - ) - - if channel_ is not None and not isinstance(channel_, (bytes, str)): - raise Exception( - f"Expected channel_ to be a str, received: {type(channel_)}" - ) - - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): - raise Exception( - f"Expected charm_url_ to be a str, received: {type(charm_url_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if config_yaml_ is not None and not isinstance(config_yaml_, (bytes, str)): - raise Exception( - f"Expected config_yaml_ to be a str, received: {type(config_yaml_)}" - ) - - if constraints_ is not None and not isinstance(constraints_, (dict, Value)): - raise Exception( - f"Expected constraints_ to be a Value, received: {type(constraints_)}" - ) - - if devices_ is not None and not isinstance(devices_, dict): - raise Exception( - f"Expected devices_ to be a Mapping, received: {type(devices_)}" - ) - - if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): - raise Exception( - f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" - ) - - if num_units_ is not None and not isinstance(num_units_, int): - raise Exception( - f"Expected num_units_ to be a int, received: {type(num_units_)}" - ) - - if placement_ is not None and not isinstance(placement_, (bytes, str, list)): - raise Exception( - f"Expected placement_ to be a Sequence, received: {type(placement_)}" - ) - - if policy_ is not None and not isinstance(policy_, (bytes, str)): - raise Exception(f"Expected policy_ to be a str, received: {type(policy_)}") - - if resources_ is not None and not isinstance(resources_, dict): - raise Exception( - f"Expected resources_ to be a Mapping, received: {type(resources_)}" - ) - - if storage_ is not None and not isinstance(storage_, dict): - raise Exception( - f"Expected storage_ to be a Mapping, received: {type(storage_)}" - ) - - self.force = force_ - self.application = application_ - self.attach_storage = attach_storage_ - self.channel = channel_ - self.charm_origin = charm_origin_ - self.charm_url = charm_url_ - self.config = config_ - self.config_yaml = config_yaml_ - self.constraints = constraints_ - self.devices = devices_ - self.endpoint_bindings = endpoint_bindings_ - self.num_units = num_units_ - self.placement = placement_ - self.policy = policy_ - self.resources = resources_ - self.storage = storage_ - self.unknown_fields = unknown_fields - - -class ApplicationExpose(Type): - _toSchema = {"application": "application", "exposed_endpoints": "exposed-endpoints"} - _toPy = {"application": "application", "exposed-endpoints": "exposed_endpoints"} - - def __init__(self, application=None, exposed_endpoints=None, **unknown_fields): - """Application : str - exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] - """ - application_ = application - exposed_endpoints_ = { - k: ExposedEndpoint.from_json(v) - for k, v in (exposed_endpoints or dict()).items() - } - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if exposed_endpoints_ is not None and not isinstance(exposed_endpoints_, dict): - raise Exception( - f"Expected exposed_endpoints_ to be a Mapping, received: {type(exposed_endpoints_)}" - ) - - self.application = application_ - self.exposed_endpoints = exposed_endpoints_ - self.unknown_fields = unknown_fields - - -class ApplicationGet(Type): - _toSchema = {"application": "application", "branch": "branch"} - _toPy = {"application": "application", "branch": "branch"} - - def __init__(self, application=None, branch=None, **unknown_fields): - """Application : str - branch : str - """ - application_ = application - branch_ = branch - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if branch_ is not None and not isinstance(branch_, (bytes, str)): - raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") - - self.application = application_ - self.branch = branch_ - self.unknown_fields = unknown_fields - - -class ApplicationGetArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~ApplicationGet]""" - args_ = [ApplicationGet.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class ApplicationGetConfigResults(Type): - _toSchema = {"results": "Results"} - _toPy = {"Results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ConfigResult]""" - results_ = [ConfigResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ApplicationGetConstraintsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ApplicationConstraint]""" - results_ = [ApplicationConstraint.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ApplicationGetResults(Type): - _toSchema = { - "application": "application", - "application_config": "application-config", - "base": "base", - "channel": "channel", - "charm": "charm", - "config": "config", - "constraints": "constraints", - "endpoint_bindings": "endpoint-bindings", - } - _toPy = { - "application": "application", - "application-config": "application_config", - "base": "base", - "channel": "channel", - "charm": "charm", - "config": "config", - "constraints": "constraints", - "endpoint-bindings": "endpoint_bindings", - } - - def __init__( - self, - application=None, - application_config=None, - base=None, - channel=None, - charm=None, - config=None, - constraints=None, - endpoint_bindings=None, - **unknown_fields, - ): - """Application : str - application_config : typing.Mapping[str, typing.Any] - base : Base - channel : str - charm : str - config : typing.Mapping[str, typing.Any] - constraints : Value - endpoint_bindings : typing.Mapping[str, str] - """ - application_ = application - application_config_ = application_config - base_ = Base.from_json(base) if base else None - channel_ = channel - charm_ = charm - config_ = config - constraints_ = Value.from_json(constraints) if constraints else None - endpoint_bindings_ = endpoint_bindings - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if application_config_ is not None and not isinstance( - application_config_, dict - ): - raise Exception( - f"Expected application_config_ to be a Mapping, received: {type(application_config_)}" - ) - - if base_ is not None and not isinstance(base_, (dict, Base)): - raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") - - if channel_ is not None and not isinstance(channel_, (bytes, str)): - raise Exception( - f"Expected channel_ to be a str, received: {type(channel_)}" - ) - - if charm_ is not None and not isinstance(charm_, (bytes, str)): - raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if constraints_ is not None and not isinstance(constraints_, (dict, Value)): - raise Exception( - f"Expected constraints_ to be a Value, received: {type(constraints_)}" - ) - - if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): - raise Exception( - f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" - ) - - self.application = application_ - self.application_config = application_config_ - self.base = base_ - self.channel = channel_ - self.charm = charm_ - self.config = config_ - self.constraints = constraints_ - self.endpoint_bindings = endpoint_bindings_ - self.unknown_fields = unknown_fields - - -class ApplicationInfoResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : ApplicationResult - """ - error_ = Error.from_json(error) if error else None - result_ = ApplicationResult.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, ApplicationResult)): - raise Exception( - f"Expected result_ to be a ApplicationResult, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class ApplicationInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ApplicationInfoResult]""" - results_ = [ApplicationInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ApplicationMergeBindings(Type): - _toSchema = { - "application_tag": "application-tag", - "bindings": "bindings", - "force": "force", - } - _toPy = { - "application-tag": "application_tag", - "bindings": "bindings", - "force": "force", - } - - def __init__( - self, application_tag=None, bindings=None, force=None, **unknown_fields - ): - """application_tag : str - bindings : typing.Mapping[str, str] - force : bool - """ - application_tag_ = application_tag - bindings_ = bindings - force_ = force - - # Validate arguments against known Juju API types. - if application_tag_ is not None and not isinstance( - application_tag_, (bytes, str) - ): - raise Exception( - f"Expected application_tag_ to be a str, received: {type(application_tag_)}" - ) - - if bindings_ is not None and not isinstance(bindings_, dict): - raise Exception( - f"Expected bindings_ to be a Mapping, received: {type(bindings_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - self.application_tag = application_tag_ - self.bindings = bindings_ - self.force = force_ - self.unknown_fields = unknown_fields - - -class ApplicationMergeBindingsArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~ApplicationMergeBindings]""" - args_ = [ApplicationMergeBindings.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class ApplicationMetricCredential(Type): - _toSchema = { - "application": "application", - "metrics_credentials": "metrics-credentials", - } - _toPy = {"application": "application", "metrics-credentials": "metrics_credentials"} - - def __init__(self, application=None, metrics_credentials=None, **unknown_fields): - """Application : str - metrics_credentials : typing.Sequence[int] - """ - application_ = application - metrics_credentials_ = metrics_credentials - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if metrics_credentials_ is not None and not isinstance( - metrics_credentials_, (bytes, str, list) - ): - raise Exception( - f"Expected metrics_credentials_ to be a Sequence, received: {type(metrics_credentials_)}" - ) - - self.application = application_ - self.metrics_credentials = metrics_credentials_ - self.unknown_fields = unknown_fields - - -class ApplicationMetricCredentials(Type): - _toSchema = {"creds": "creds"} - _toPy = {"creds": "creds"} - - def __init__(self, creds=None, **unknown_fields): - """Creds : typing.Sequence[~ApplicationMetricCredential]""" - creds_ = [ApplicationMetricCredential.from_json(o) for o in creds or []] - - # Validate arguments against known Juju API types. - if creds_ is not None and not isinstance(creds_, (bytes, str, list)): - raise Exception( - f"Expected creds_ to be a Sequence, received: {type(creds_)}" - ) - - self.creds = creds_ - self.unknown_fields = unknown_fields - - -class ApplicationOfferAdminDetails(Type): - _toSchema = { - "application_description": "application-description", - "application_name": "application-name", - "applicationofferdetails": "ApplicationOfferDetails", - "bindings": "bindings", - "charm_url": "charm-url", - "connections": "connections", - "endpoints": "endpoints", - "offer_name": "offer-name", - "offer_url": "offer-url", - "offer_uuid": "offer-uuid", - "source_model_tag": "source-model-tag", - "spaces": "spaces", - "users": "users", - } - _toPy = { - "ApplicationOfferDetails": "applicationofferdetails", - "application-description": "application_description", - "application-name": "application_name", - "bindings": "bindings", - "charm-url": "charm_url", - "connections": "connections", - "endpoints": "endpoints", - "offer-name": "offer_name", - "offer-url": "offer_url", - "offer-uuid": "offer_uuid", - "source-model-tag": "source_model_tag", - "spaces": "spaces", - "users": "users", - } - - def __init__( - self, - applicationofferdetails=None, - application_description=None, - application_name=None, - bindings=None, - charm_url=None, - connections=None, - endpoints=None, - offer_name=None, - offer_url=None, - offer_uuid=None, - source_model_tag=None, - spaces=None, - users=None, - **unknown_fields, - ): - """Applicationofferdetails : ApplicationOfferDetails - application_description : str - application_name : str - bindings : typing.Mapping[str, str] - charm_url : str - connections : typing.Sequence[~OfferConnection] - endpoints : typing.Sequence[~RemoteEndpoint] - offer_name : str - offer_url : str - offer_uuid : str - source_model_tag : str - spaces : typing.Sequence[~RemoteSpace] - users : typing.Sequence[~OfferUserDetails] - """ - applicationofferdetails_ = ( - ApplicationOfferDetails.from_json(applicationofferdetails) - if applicationofferdetails - else None - ) - application_description_ = application_description - application_name_ = application_name - bindings_ = bindings - charm_url_ = charm_url - connections_ = [OfferConnection.from_json(o) for o in connections or []] - endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] - offer_name_ = offer_name - offer_url_ = offer_url - offer_uuid_ = offer_uuid - source_model_tag_ = source_model_tag - spaces_ = [RemoteSpace.from_json(o) for o in spaces or []] - users_ = [OfferUserDetails.from_json(o) for o in users or []] - - # Validate arguments against known Juju API types. - if applicationofferdetails_ is not None and not isinstance( - applicationofferdetails_, (dict, ApplicationOfferDetails) - ): - raise Exception( - f"Expected applicationofferdetails_ to be a ApplicationOfferDetails, received: {type(applicationofferdetails_)}" - ) - - if application_description_ is not None and not isinstance( - application_description_, (bytes, str) - ): - raise Exception( - f"Expected application_description_ to be a str, received: {type(application_description_)}" - ) - - if application_name_ is not None and not isinstance( - application_name_, (bytes, str) - ): - raise Exception( - f"Expected application_name_ to be a str, received: {type(application_name_)}" - ) - - if bindings_ is not None and not isinstance(bindings_, dict): - raise Exception( - f"Expected bindings_ to be a Mapping, received: {type(bindings_)}" - ) - - if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): - raise Exception( - f"Expected charm_url_ to be a str, received: {type(charm_url_)}" - ) - - if connections_ is not None and not isinstance( - connections_, (bytes, str, list) - ): - raise Exception( - f"Expected connections_ to be a Sequence, received: {type(connections_)}" - ) - - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): - raise Exception( - f"Expected offer_url_ to be a str, received: {type(offer_url_)}" - ) - - if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): - raise Exception( - f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" - ) - - if source_model_tag_ is not None and not isinstance( - source_model_tag_, (bytes, str) - ): - raise Exception( - f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" - ) - - if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): - raise Exception( - f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" - ) - - if users_ is not None and not isinstance(users_, (bytes, str, list)): - raise Exception( - f"Expected users_ to be a Sequence, received: {type(users_)}" - ) - - self.applicationofferdetails = applicationofferdetails_ - self.application_description = application_description_ - self.application_name = application_name_ - self.bindings = bindings_ - self.charm_url = charm_url_ - self.connections = connections_ - self.endpoints = endpoints_ - self.offer_name = offer_name_ - self.offer_url = offer_url_ - self.offer_uuid = offer_uuid_ - self.source_model_tag = source_model_tag_ - self.spaces = spaces_ - self.users = users_ - self.unknown_fields = unknown_fields - - -class ApplicationOfferAdminDetailsV5(Type): - _toSchema = { - "application_description": "application-description", - "application_name": "application-name", - "applicationofferdetailsv5": "ApplicationOfferDetailsV5", - "charm_url": "charm-url", - "connections": "connections", - "endpoints": "endpoints", - "offer_name": "offer-name", - "offer_url": "offer-url", - "offer_uuid": "offer-uuid", - "source_model_tag": "source-model-tag", - "users": "users", - } - _toPy = { - "ApplicationOfferDetailsV5": "applicationofferdetailsv5", - "application-description": "application_description", - "application-name": "application_name", - "charm-url": "charm_url", - "connections": "connections", - "endpoints": "endpoints", - "offer-name": "offer_name", - "offer-url": "offer_url", - "offer-uuid": "offer_uuid", - "source-model-tag": "source_model_tag", - "users": "users", - } - - def __init__( - self, - applicationofferdetailsv5=None, - application_description=None, - application_name=None, - charm_url=None, - connections=None, - endpoints=None, - offer_name=None, - offer_url=None, - offer_uuid=None, - source_model_tag=None, - users=None, - **unknown_fields, - ): - """applicationofferdetailsv5 : ApplicationOfferDetailsV5 - application_description : str - application_name : str - charm_url : str - connections : typing.Sequence[~OfferConnection] - endpoints : typing.Sequence[~RemoteEndpoint] - offer_name : str - offer_url : str - offer_uuid : str - source_model_tag : str - users : typing.Sequence[~OfferUserDetails] - """ - applicationofferdetailsv5_ = ( - ApplicationOfferDetailsV5.from_json(applicationofferdetailsv5) - if applicationofferdetailsv5 - else None - ) - application_description_ = application_description - application_name_ = application_name - charm_url_ = charm_url - connections_ = [OfferConnection.from_json(o) for o in connections or []] - endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] - offer_name_ = offer_name - offer_url_ = offer_url - offer_uuid_ = offer_uuid - source_model_tag_ = source_model_tag - users_ = [OfferUserDetails.from_json(o) for o in users or []] - - # Validate arguments against known Juju API types. - if applicationofferdetailsv5_ is not None and not isinstance( - applicationofferdetailsv5_, (dict, ApplicationOfferDetailsV5) - ): - raise Exception( - f"Expected applicationofferdetailsv5_ to be a ApplicationOfferDetailsV5, received: {type(applicationofferdetailsv5_)}" - ) - - if application_description_ is not None and not isinstance( - application_description_, (bytes, str) - ): - raise Exception( - f"Expected application_description_ to be a str, received: {type(application_description_)}" - ) - - if application_name_ is not None and not isinstance( - application_name_, (bytes, str) - ): - raise Exception( - f"Expected application_name_ to be a str, received: {type(application_name_)}" - ) - - if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): - raise Exception( - f"Expected charm_url_ to be a str, received: {type(charm_url_)}" - ) - - if connections_ is not None and not isinstance( - connections_, (bytes, str, list) - ): - raise Exception( - f"Expected connections_ to be a Sequence, received: {type(connections_)}" - ) - - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): - raise Exception( - f"Expected offer_url_ to be a str, received: {type(offer_url_)}" - ) - - if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): - raise Exception( - f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" - ) - - if source_model_tag_ is not None and not isinstance( - source_model_tag_, (bytes, str) - ): - raise Exception( - f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" - ) - - if users_ is not None and not isinstance(users_, (bytes, str, list)): - raise Exception( - f"Expected users_ to be a Sequence, received: {type(users_)}" - ) - - self.applicationofferdetailsv5 = applicationofferdetailsv5_ - self.application_description = application_description_ - self.application_name = application_name_ - self.charm_url = charm_url_ - self.connections = connections_ - self.endpoints = endpoints_ - self.offer_name = offer_name_ - self.offer_url = offer_url_ - self.offer_uuid = offer_uuid_ - self.source_model_tag = source_model_tag_ - self.users = users_ - self.unknown_fields = unknown_fields - - -class ApplicationOfferDetails(Type): - _toSchema = { - "application_description": "application-description", - "bindings": "bindings", - "endpoints": "endpoints", - "offer_name": "offer-name", - "offer_url": "offer-url", - "offer_uuid": "offer-uuid", - "source_model_tag": "source-model-tag", - "spaces": "spaces", - "users": "users", - } - _toPy = { - "application-description": "application_description", - "bindings": "bindings", - "endpoints": "endpoints", - "offer-name": "offer_name", - "offer-url": "offer_url", - "offer-uuid": "offer_uuid", - "source-model-tag": "source_model_tag", - "spaces": "spaces", - "users": "users", - } - - def __init__( - self, - application_description=None, - bindings=None, - endpoints=None, - offer_name=None, - offer_url=None, - offer_uuid=None, - source_model_tag=None, - spaces=None, - users=None, - **unknown_fields, - ): - """application_description : str - bindings : typing.Mapping[str, str] - endpoints : typing.Sequence[~RemoteEndpoint] - offer_name : str - offer_url : str - offer_uuid : str - source_model_tag : str - spaces : typing.Sequence[~RemoteSpace] - users : typing.Sequence[~OfferUserDetails] - """ - application_description_ = application_description - bindings_ = bindings - endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] - offer_name_ = offer_name - offer_url_ = offer_url - offer_uuid_ = offer_uuid - source_model_tag_ = source_model_tag - spaces_ = [RemoteSpace.from_json(o) for o in spaces or []] - users_ = [OfferUserDetails.from_json(o) for o in users or []] - - # Validate arguments against known Juju API types. - if application_description_ is not None and not isinstance( - application_description_, (bytes, str) - ): - raise Exception( - f"Expected application_description_ to be a str, received: {type(application_description_)}" - ) - - if bindings_ is not None and not isinstance(bindings_, dict): - raise Exception( - f"Expected bindings_ to be a Mapping, received: {type(bindings_)}" - ) - - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): - raise Exception( - f"Expected offer_url_ to be a str, received: {type(offer_url_)}" - ) - - if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): - raise Exception( - f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" - ) - - if source_model_tag_ is not None and not isinstance( - source_model_tag_, (bytes, str) - ): - raise Exception( - f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" - ) - - if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): - raise Exception( - f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" - ) - - if users_ is not None and not isinstance(users_, (bytes, str, list)): - raise Exception( - f"Expected users_ to be a Sequence, received: {type(users_)}" - ) - - self.application_description = application_description_ - self.bindings = bindings_ - self.endpoints = endpoints_ - self.offer_name = offer_name_ - self.offer_url = offer_url_ - self.offer_uuid = offer_uuid_ - self.source_model_tag = source_model_tag_ - self.spaces = spaces_ - self.users = users_ - self.unknown_fields = unknown_fields - - -class ApplicationOfferDetailsV5(Type): - _toSchema = { - "application_description": "application-description", - "endpoints": "endpoints", - "offer_name": "offer-name", - "offer_url": "offer-url", - "offer_uuid": "offer-uuid", - "source_model_tag": "source-model-tag", - "users": "users", - } - _toPy = { - "application-description": "application_description", - "endpoints": "endpoints", - "offer-name": "offer_name", - "offer-url": "offer_url", - "offer-uuid": "offer_uuid", - "source-model-tag": "source_model_tag", - "users": "users", - } - - def __init__( - self, - application_description=None, - endpoints=None, - offer_name=None, - offer_url=None, - offer_uuid=None, - source_model_tag=None, - users=None, - **unknown_fields, - ): - """application_description : str - endpoints : typing.Sequence[~RemoteEndpoint] - offer_name : str - offer_url : str - offer_uuid : str - source_model_tag : str - users : typing.Sequence[~OfferUserDetails] - """ - application_description_ = application_description - endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] - offer_name_ = offer_name - offer_url_ = offer_url - offer_uuid_ = offer_uuid - source_model_tag_ = source_model_tag - users_ = [OfferUserDetails.from_json(o) for o in users or []] - - # Validate arguments against known Juju API types. - if application_description_ is not None and not isinstance( - application_description_, (bytes, str) - ): - raise Exception( - f"Expected application_description_ to be a str, received: {type(application_description_)}" - ) - - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): - raise Exception( - f"Expected offer_url_ to be a str, received: {type(offer_url_)}" - ) - - if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): - raise Exception( - f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" - ) - - if source_model_tag_ is not None and not isinstance( - source_model_tag_, (bytes, str) - ): - raise Exception( - f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" - ) - - if users_ is not None and not isinstance(users_, (bytes, str, list)): - raise Exception( - f"Expected users_ to be a Sequence, received: {type(users_)}" - ) - - self.application_description = application_description_ - self.endpoints = endpoints_ - self.offer_name = offer_name_ - self.offer_url = offer_url_ - self.offer_uuid = offer_uuid_ - self.source_model_tag = source_model_tag_ - self.users = users_ - self.unknown_fields = unknown_fields - - -class ApplicationOfferResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : ApplicationOfferAdminDetailsV5 - """ - error_ = Error.from_json(error) if error else None - result_ = ApplicationOfferAdminDetailsV5.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance( - result_, (dict, ApplicationOfferAdminDetailsV5) - ): - raise Exception( - f"Expected result_ to be a ApplicationOfferAdminDetailsV5, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class ApplicationOfferStatus(Type): - _toSchema = { - "active_connected_count": "active-connected-count", - "application_name": "application-name", - "charm": "charm", - "endpoints": "endpoints", - "err": "err", - "offer_name": "offer-name", - "total_connected_count": "total-connected-count", - } - _toPy = { - "active-connected-count": "active_connected_count", - "application-name": "application_name", - "charm": "charm", - "endpoints": "endpoints", - "err": "err", - "offer-name": "offer_name", - "total-connected-count": "total_connected_count", - } - - def __init__( - self, - active_connected_count=None, - application_name=None, - charm=None, - endpoints=None, - err=None, - offer_name=None, - total_connected_count=None, - **unknown_fields, - ): - """active_connected_count : int - application_name : str - charm : str - endpoints : typing.Mapping[str, ~RemoteEndpoint] - err : Error - offer_name : str - total_connected_count : int - """ - active_connected_count_ = active_connected_count - application_name_ = application_name - charm_ = charm - endpoints_ = { - k: RemoteEndpoint.from_json(v) for k, v in (endpoints or dict()).items() - } - err_ = Error.from_json(err) if err else None - offer_name_ = offer_name - total_connected_count_ = total_connected_count - - # Validate arguments against known Juju API types. - if active_connected_count_ is not None and not isinstance( - active_connected_count_, int - ): - raise Exception( - f"Expected active_connected_count_ to be a int, received: {type(active_connected_count_)}" - ) - - if application_name_ is not None and not isinstance( - application_name_, (bytes, str) - ): - raise Exception( - f"Expected application_name_ to be a str, received: {type(application_name_)}" - ) - - if charm_ is not None and not isinstance(charm_, (bytes, str)): - raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") - - if endpoints_ is not None and not isinstance(endpoints_, dict): - raise Exception( - f"Expected endpoints_ to be a Mapping, received: {type(endpoints_)}" - ) - - if err_ is not None and not isinstance(err_, (dict, Error)): - raise Exception(f"Expected err_ to be a Error, received: {type(err_)}") - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if total_connected_count_ is not None and not isinstance( - total_connected_count_, int - ): - raise Exception( - f"Expected total_connected_count_ to be a int, received: {type(total_connected_count_)}" - ) - - self.active_connected_count = active_connected_count_ - self.application_name = application_name_ - self.charm = charm_ - self.endpoints = endpoints_ - self.err = err_ - self.offer_name = offer_name_ - self.total_connected_count = total_connected_count_ - self.unknown_fields = unknown_fields - - -class ApplicationOffersResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ApplicationOfferResult]""" - results_ = [ApplicationOfferResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ApplicationResult(Type): - _toSchema = { - "base": "base", - "channel": "channel", - "charm": "charm", - "constraints": "constraints", - "endpoint_bindings": "endpoint-bindings", - "exposed": "exposed", - "exposed_endpoints": "exposed-endpoints", - "life": "life", - "principal": "principal", - "remote": "remote", - "tag": "tag", - } - _toPy = { - "base": "base", - "channel": "channel", - "charm": "charm", - "constraints": "constraints", - "endpoint-bindings": "endpoint_bindings", - "exposed": "exposed", - "exposed-endpoints": "exposed_endpoints", - "life": "life", - "principal": "principal", - "remote": "remote", - "tag": "tag", - } - - def __init__( - self, - base=None, - channel=None, - charm=None, - constraints=None, - endpoint_bindings=None, - exposed=None, - exposed_endpoints=None, - life=None, - principal=None, - remote=None, - tag=None, - **unknown_fields, - ): - """Base : Base - channel : str - charm : str - constraints : Value - endpoint_bindings : typing.Mapping[str, str] - exposed : bool - exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] - life : str - principal : bool - remote : bool - tag : str - """ - base_ = Base.from_json(base) if base else None - channel_ = channel - charm_ = charm - constraints_ = Value.from_json(constraints) if constraints else None - endpoint_bindings_ = endpoint_bindings - exposed_ = exposed - exposed_endpoints_ = { - k: ExposedEndpoint.from_json(v) - for k, v in (exposed_endpoints or dict()).items() - } - life_ = life - principal_ = principal - remote_ = remote - tag_ = tag - - # Validate arguments against known Juju API types. - if base_ is not None and not isinstance(base_, (dict, Base)): - raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") - - if channel_ is not None and not isinstance(channel_, (bytes, str)): - raise Exception( - f"Expected channel_ to be a str, received: {type(channel_)}" - ) - - if charm_ is not None and not isinstance(charm_, (bytes, str)): - raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") - - if constraints_ is not None and not isinstance(constraints_, (dict, Value)): - raise Exception( - f"Expected constraints_ to be a Value, received: {type(constraints_)}" - ) - - if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): - raise Exception( - f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" - ) - - if exposed_ is not None and not isinstance(exposed_, bool): - raise Exception( - f"Expected exposed_ to be a bool, received: {type(exposed_)}" - ) - - if exposed_endpoints_ is not None and not isinstance(exposed_endpoints_, dict): - raise Exception( - f"Expected exposed_endpoints_ to be a Mapping, received: {type(exposed_endpoints_)}" - ) - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if principal_ is not None and not isinstance(principal_, bool): - raise Exception( - f"Expected principal_ to be a bool, received: {type(principal_)}" - ) - - if remote_ is not None and not isinstance(remote_, bool): - raise Exception(f"Expected remote_ to be a bool, received: {type(remote_)}") - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.base = base_ - self.channel = channel_ - self.charm = charm_ - self.constraints = constraints_ - self.endpoint_bindings = endpoint_bindings_ - self.exposed = exposed_ - self.exposed_endpoints = exposed_endpoints_ - self.life = life_ - self.principal = principal_ - self.remote = remote_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class ApplicationSetCharm(Type): - _toSchema = { - "application": "application", - "channel": "channel", - "charm_origin": "charm-origin", - "charm_url": "charm-url", - "config_settings": "config-settings", - "config_settings_yaml": "config-settings-yaml", - "endpoint_bindings": "endpoint-bindings", - "force": "force", - "force_base": "force-base", - "force_units": "force-units", - "generation": "generation", - "resource_ids": "resource-ids", - "storage_constraints": "storage-constraints", - } - _toPy = { - "application": "application", - "channel": "channel", - "charm-origin": "charm_origin", - "charm-url": "charm_url", - "config-settings": "config_settings", - "config-settings-yaml": "config_settings_yaml", - "endpoint-bindings": "endpoint_bindings", - "force": "force", - "force-base": "force_base", - "force-units": "force_units", - "generation": "generation", - "resource-ids": "resource_ids", - "storage-constraints": "storage_constraints", - } - - def __init__( - self, - application=None, - channel=None, - charm_origin=None, - charm_url=None, - config_settings=None, - config_settings_yaml=None, - endpoint_bindings=None, - force=None, - force_base=None, - force_units=None, - generation=None, - resource_ids=None, - storage_constraints=None, - **unknown_fields, - ): - """Application : str - channel : str - charm_origin : CharmOrigin - charm_url : str - config_settings : typing.Mapping[str, str] - config_settings_yaml : str - endpoint_bindings : typing.Mapping[str, str] - force : bool - force_base : bool - force_units : bool - generation : str - resource_ids : typing.Mapping[str, str] - storage_constraints : typing.Mapping[str, ~StorageConstraints] - """ - application_ = application - channel_ = channel - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - charm_url_ = charm_url - config_settings_ = config_settings - config_settings_yaml_ = config_settings_yaml - endpoint_bindings_ = endpoint_bindings - force_ = force - force_base_ = force_base - force_units_ = force_units - generation_ = generation - resource_ids_ = resource_ids - storage_constraints_ = { - k: StorageConstraints.from_json(v) - for k, v in (storage_constraints or dict()).items() - } - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if channel_ is not None and not isinstance(channel_, (bytes, str)): - raise Exception( - f"Expected channel_ to be a str, received: {type(channel_)}" - ) - - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): - raise Exception( - f"Expected charm_url_ to be a str, received: {type(charm_url_)}" - ) - - if config_settings_ is not None and not isinstance(config_settings_, dict): - raise Exception( - f"Expected config_settings_ to be a Mapping, received: {type(config_settings_)}" - ) - - if config_settings_yaml_ is not None and not isinstance( - config_settings_yaml_, (bytes, str) - ): - raise Exception( - f"Expected config_settings_yaml_ to be a str, received: {type(config_settings_yaml_)}" - ) - - if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): - raise Exception( - f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if force_base_ is not None and not isinstance(force_base_, bool): - raise Exception( - f"Expected force_base_ to be a bool, received: {type(force_base_)}" - ) - - if force_units_ is not None and not isinstance(force_units_, bool): - raise Exception( - f"Expected force_units_ to be a bool, received: {type(force_units_)}" - ) - - if generation_ is not None and not isinstance(generation_, (bytes, str)): - raise Exception( - f"Expected generation_ to be a str, received: {type(generation_)}" - ) - - if resource_ids_ is not None and not isinstance(resource_ids_, dict): - raise Exception( - f"Expected resource_ids_ to be a Mapping, received: {type(resource_ids_)}" - ) - - if storage_constraints_ is not None and not isinstance( - storage_constraints_, dict - ): - raise Exception( - f"Expected storage_constraints_ to be a Mapping, received: {type(storage_constraints_)}" - ) - - self.application = application_ - self.channel = channel_ - self.charm_origin = charm_origin_ - self.charm_url = charm_url_ - self.config_settings = config_settings_ - self.config_settings_yaml = config_settings_yaml_ - self.endpoint_bindings = endpoint_bindings_ - self.force = force_ - self.force_base = force_base_ - self.force_units = force_units_ - self.generation = generation_ - self.resource_ids = resource_ids_ - self.storage_constraints = storage_constraints_ - self.unknown_fields = unknown_fields - - -class ApplicationStatus(Type): - _toSchema = { - "base": "base", - "can_upgrade_to": "can-upgrade-to", - "charm": "charm", - "charm_channel": "charm-channel", - "charm_profile": "charm-profile", - "charm_rev": "charm-rev", - "charm_version": "charm-version", - "endpoint_bindings": "endpoint-bindings", - "err": "err", - "exposed": "exposed", - "exposed_endpoints": "exposed-endpoints", - "int_": "int", - "life": "life", - "meter_statuses": "meter-statuses", - "provider_id": "provider-id", - "public_address": "public-address", - "relations": "relations", - "status": "status", - "subordinate_to": "subordinate-to", - "units": "units", - "workload_version": "workload-version", - } - _toPy = { - "base": "base", - "can-upgrade-to": "can_upgrade_to", - "charm": "charm", - "charm-channel": "charm_channel", - "charm-profile": "charm_profile", - "charm-rev": "charm_rev", - "charm-version": "charm_version", - "endpoint-bindings": "endpoint_bindings", - "err": "err", - "exposed": "exposed", - "exposed-endpoints": "exposed_endpoints", - "int": "int_", - "life": "life", - "meter-statuses": "meter_statuses", - "provider-id": "provider_id", - "public-address": "public_address", - "relations": "relations", - "status": "status", - "subordinate-to": "subordinate_to", - "units": "units", - "workload-version": "workload_version", - } - - def __init__( - self, - base=None, - can_upgrade_to=None, - charm=None, - charm_channel=None, - charm_profile=None, - charm_rev=None, - charm_version=None, - endpoint_bindings=None, - err=None, - exposed=None, - exposed_endpoints=None, - int_=None, - life=None, - meter_statuses=None, - provider_id=None, - public_address=None, - relations=None, - status=None, - subordinate_to=None, - units=None, - workload_version=None, - **unknown_fields, - ): - """Base : Base - can_upgrade_to : str - charm : str - charm_channel : str - charm_profile : str - charm_rev : int - charm_version : str - endpoint_bindings : typing.Mapping[str, str] - err : Error - exposed : bool - exposed_endpoints : typing.Mapping[str, ~ExposedEndpoint] - int_ : int - life : str - meter_statuses : typing.Mapping[str, ~MeterStatus] - provider_id : str - public_address : str - relations : typing.Mapping[str, typing.Sequence[str]] - status : DetailedStatus - subordinate_to : typing.Sequence[str] - units : typing.Mapping[str, ~UnitStatus] - workload_version : str - """ - base_ = Base.from_json(base) if base else None - can_upgrade_to_ = can_upgrade_to - charm_ = charm - charm_channel_ = charm_channel - charm_profile_ = charm_profile - charm_rev_ = charm_rev - charm_version_ = charm_version - endpoint_bindings_ = endpoint_bindings - err_ = Error.from_json(err) if err else None - exposed_ = exposed - exposed_endpoints_ = { - k: ExposedEndpoint.from_json(v) - for k, v in (exposed_endpoints or dict()).items() - } - int__ = int_ - life_ = life - meter_statuses_ = { - k: MeterStatus.from_json(v) for k, v in (meter_statuses or dict()).items() - } - provider_id_ = provider_id - public_address_ = public_address - relations_ = relations - status_ = DetailedStatus.from_json(status) if status else None - subordinate_to_ = subordinate_to - units_ = {k: UnitStatus.from_json(v) for k, v in (units or dict()).items()} - workload_version_ = workload_version - - # Validate arguments against known Juju API types. - if base_ is not None and not isinstance(base_, (dict, Base)): - raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") - - if can_upgrade_to_ is not None and not isinstance( - can_upgrade_to_, (bytes, str) - ): - raise Exception( - f"Expected can_upgrade_to_ to be a str, received: {type(can_upgrade_to_)}" - ) - - if charm_ is not None and not isinstance(charm_, (bytes, str)): - raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") - - if charm_channel_ is not None and not isinstance(charm_channel_, (bytes, str)): - raise Exception( - f"Expected charm_channel_ to be a str, received: {type(charm_channel_)}" - ) - - if charm_profile_ is not None and not isinstance(charm_profile_, (bytes, str)): - raise Exception( - f"Expected charm_profile_ to be a str, received: {type(charm_profile_)}" - ) - - if charm_rev_ is not None and not isinstance(charm_rev_, int): - raise Exception( - f"Expected charm_rev_ to be a int, received: {type(charm_rev_)}" - ) - - if charm_version_ is not None and not isinstance(charm_version_, (bytes, str)): - raise Exception( - f"Expected charm_version_ to be a str, received: {type(charm_version_)}" - ) - - if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): - raise Exception( - f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" - ) - - if err_ is not None and not isinstance(err_, (dict, Error)): - raise Exception(f"Expected err_ to be a Error, received: {type(err_)}") - - if exposed_ is not None and not isinstance(exposed_, bool): - raise Exception( - f"Expected exposed_ to be a bool, received: {type(exposed_)}" - ) - - if exposed_endpoints_ is not None and not isinstance(exposed_endpoints_, dict): - raise Exception( - f"Expected exposed_endpoints_ to be a Mapping, received: {type(exposed_endpoints_)}" - ) - - if int__ is not None and not isinstance(int__, int): - raise Exception(f"Expected int__ to be a int, received: {type(int__)}") - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if meter_statuses_ is not None and not isinstance(meter_statuses_, dict): - raise Exception( - f"Expected meter_statuses_ to be a Mapping, received: {type(meter_statuses_)}" - ) - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if public_address_ is not None and not isinstance( - public_address_, (bytes, str) - ): - raise Exception( - f"Expected public_address_ to be a str, received: {type(public_address_)}" - ) - - if relations_ is not None and not isinstance(relations_, dict): - raise Exception( - f"Expected relations_ to be a Mapping, received: {type(relations_)}" - ) - - if status_ is not None and not isinstance(status_, (dict, DetailedStatus)): - raise Exception( - f"Expected status_ to be a DetailedStatus, received: {type(status_)}" - ) - - if subordinate_to_ is not None and not isinstance( - subordinate_to_, (bytes, str, list) - ): - raise Exception( - f"Expected subordinate_to_ to be a Sequence, received: {type(subordinate_to_)}" - ) - - if units_ is not None and not isinstance(units_, dict): - raise Exception( - f"Expected units_ to be a Mapping, received: {type(units_)}" - ) - - if workload_version_ is not None and not isinstance( - workload_version_, (bytes, str) - ): - raise Exception( - f"Expected workload_version_ to be a str, received: {type(workload_version_)}" - ) - - self.base = base_ - self.can_upgrade_to = can_upgrade_to_ - self.charm = charm_ - self.charm_channel = charm_channel_ - self.charm_profile = charm_profile_ - self.charm_rev = charm_rev_ - self.charm_version = charm_version_ - self.endpoint_bindings = endpoint_bindings_ - self.err = err_ - self.exposed = exposed_ - self.exposed_endpoints = exposed_endpoints_ - self.int_ = int__ - self.life = life_ - self.meter_statuses = meter_statuses_ - self.provider_id = provider_id_ - self.public_address = public_address_ - self.relations = relations_ - self.status = status_ - self.subordinate_to = subordinate_to_ - self.units = units_ - self.workload_version = workload_version_ - self.unknown_fields = unknown_fields - - -class ApplicationUnexpose(Type): - _toSchema = {"application": "application", "exposed_endpoints": "exposed-endpoints"} - _toPy = {"application": "application", "exposed-endpoints": "exposed_endpoints"} - - def __init__(self, application=None, exposed_endpoints=None, **unknown_fields): - """Application : str - exposed_endpoints : typing.Sequence[str] - """ - application_ = application - exposed_endpoints_ = exposed_endpoints - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if exposed_endpoints_ is not None and not isinstance( - exposed_endpoints_, (bytes, str, list) - ): - raise Exception( - f"Expected exposed_endpoints_ to be a Sequence, received: {type(exposed_endpoints_)}" - ) - - self.application = application_ - self.exposed_endpoints = exposed_endpoints_ - self.unknown_fields = unknown_fields - - -class ApplicationUnset(Type): - _toSchema = {"application": "application", "branch": "branch", "options": "options"} - _toPy = {"application": "application", "branch": "branch", "options": "options"} - - def __init__(self, application=None, branch=None, options=None, **unknown_fields): - """Application : str - branch : str - options : typing.Sequence[str] - """ - application_ = application - branch_ = branch - options_ = options - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if branch_ is not None and not isinstance(branch_, (bytes, str)): - raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") - - if options_ is not None and not isinstance(options_, (bytes, str, list)): - raise Exception( - f"Expected options_ to be a Sequence, received: {type(options_)}" - ) - - self.application = application_ - self.branch = branch_ - self.options = options_ - self.unknown_fields = unknown_fields - - -class ApplicationsCharmActionsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ApplicationCharmActionsResult]""" - results_ = [ApplicationCharmActionsResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ApplicationsDeploy(Type): - _toSchema = {"applications": "applications"} - _toPy = {"applications": "applications"} - - def __init__(self, applications=None, **unknown_fields): - """Applications : typing.Sequence[~ApplicationDeploy]""" - applications_ = [ApplicationDeploy.from_json(o) for o in applications or []] - - # Validate arguments against known Juju API types. - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - self.applications = applications_ - self.unknown_fields = unknown_fields - - -class AuthUserInfo(Type): - _toSchema = { - "controller_access": "controller-access", - "credentials": "credentials", - "display_name": "display-name", - "identity": "identity", - "last_connection": "last-connection", - "model_access": "model-access", - } - _toPy = { - "controller-access": "controller_access", - "credentials": "credentials", - "display-name": "display_name", - "identity": "identity", - "last-connection": "last_connection", - "model-access": "model_access", - } - - def __init__( - self, - controller_access=None, - credentials=None, - display_name=None, - identity=None, - last_connection=None, - model_access=None, - **unknown_fields, - ): - """controller_access : str - credentials : str - display_name : str - identity : str - last_connection : str - model_access : str - """ - controller_access_ = controller_access - credentials_ = credentials - display_name_ = display_name - identity_ = identity - last_connection_ = last_connection - model_access_ = model_access - - # Validate arguments against known Juju API types. - if controller_access_ is not None and not isinstance( - controller_access_, (bytes, str) - ): - raise Exception( - f"Expected controller_access_ to be a str, received: {type(controller_access_)}" - ) - - if credentials_ is not None and not isinstance(credentials_, (bytes, str)): - raise Exception( - f"Expected credentials_ to be a str, received: {type(credentials_)}" - ) - - if display_name_ is not None and not isinstance(display_name_, (bytes, str)): - raise Exception( - f"Expected display_name_ to be a str, received: {type(display_name_)}" - ) - - if identity_ is not None and not isinstance(identity_, (bytes, str)): - raise Exception( - f"Expected identity_ to be a str, received: {type(identity_)}" - ) - - if last_connection_ is not None and not isinstance( - last_connection_, (bytes, str) - ): - raise Exception( - f"Expected last_connection_ to be a str, received: {type(last_connection_)}" - ) - - if model_access_ is not None and not isinstance(model_access_, (bytes, str)): - raise Exception( - f"Expected model_access_ to be a str, received: {type(model_access_)}" - ) - - self.controller_access = controller_access_ - self.credentials = credentials_ - self.display_name = display_name_ - self.identity = identity_ - self.last_connection = last_connection_ - self.model_access = model_access_ - self.unknown_fields = unknown_fields - - -class BackupsCreateArgs(Type): - _toSchema = {"no_download": "no-download", "notes": "notes"} - _toPy = {"no-download": "no_download", "notes": "notes"} - - def __init__(self, no_download=None, notes=None, **unknown_fields): - """no_download : bool - notes : str - """ - no_download_ = no_download - notes_ = notes - - # Validate arguments against known Juju API types. - if no_download_ is not None and not isinstance(no_download_, bool): - raise Exception( - f"Expected no_download_ to be a bool, received: {type(no_download_)}" - ) - - if notes_ is not None and not isinstance(notes_, (bytes, str)): - raise Exception(f"Expected notes_ to be a str, received: {type(notes_)}") - - self.no_download = no_download_ - self.notes = notes_ - self.unknown_fields = unknown_fields - - -class BackupsMetadataResult(Type): - _toSchema = { - "base": "base", - "checksum": "checksum", - "checksum_format": "checksum-format", - "controller_machine_id": "controller-machine-id", - "controller_machine_inst_id": "controller-machine-inst-id", - "controller_uuid": "controller-uuid", - "filename": "filename", - "finished": "finished", - "format_version": "format-version", - "ha_nodes": "ha-nodes", - "hostname": "hostname", - "id_": "id", - "machine": "machine", - "model": "model", - "notes": "notes", - "size": "size", - "started": "started", - "stored": "stored", - "version": "version", - } - _toPy = { - "base": "base", - "checksum": "checksum", - "checksum-format": "checksum_format", - "controller-machine-id": "controller_machine_id", - "controller-machine-inst-id": "controller_machine_inst_id", - "controller-uuid": "controller_uuid", - "filename": "filename", - "finished": "finished", - "format-version": "format_version", - "ha-nodes": "ha_nodes", - "hostname": "hostname", - "id": "id_", - "machine": "machine", - "model": "model", - "notes": "notes", - "size": "size", - "started": "started", - "stored": "stored", - "version": "version", - } - - def __init__( - self, - base=None, - checksum=None, - checksum_format=None, - controller_machine_id=None, - controller_machine_inst_id=None, - controller_uuid=None, - filename=None, - finished=None, - format_version=None, - ha_nodes=None, - hostname=None, - id_=None, - machine=None, - model=None, - notes=None, - size=None, - started=None, - stored=None, - version=None, - **unknown_fields, - ): - """Base : str - checksum : str - checksum_format : str - controller_machine_id : str - controller_machine_inst_id : str - controller_uuid : str - filename : str - finished : str - format_version : int - ha_nodes : int - hostname : str - id_ : str - machine : str - model : str - notes : str - size : int - started : str - stored : str - version : Number - """ - base_ = base - checksum_ = checksum - checksum_format_ = checksum_format - controller_machine_id_ = controller_machine_id - controller_machine_inst_id_ = controller_machine_inst_id - controller_uuid_ = controller_uuid - filename_ = filename - finished_ = finished - format_version_ = format_version - ha_nodes_ = ha_nodes - hostname_ = hostname - id__ = id_ - machine_ = machine - model_ = model - notes_ = notes - size_ = size - started_ = started - stored_ = stored - version_ = Number.from_json(version) if version else None - - # Validate arguments against known Juju API types. - if base_ is not None and not isinstance(base_, (bytes, str)): - raise Exception(f"Expected base_ to be a str, received: {type(base_)}") - - if checksum_ is not None and not isinstance(checksum_, (bytes, str)): - raise Exception( - f"Expected checksum_ to be a str, received: {type(checksum_)}" - ) - - if checksum_format_ is not None and not isinstance( - checksum_format_, (bytes, str) - ): - raise Exception( - f"Expected checksum_format_ to be a str, received: {type(checksum_format_)}" - ) - - if controller_machine_id_ is not None and not isinstance( - controller_machine_id_, (bytes, str) - ): - raise Exception( - f"Expected controller_machine_id_ to be a str, received: {type(controller_machine_id_)}" - ) - - if controller_machine_inst_id_ is not None and not isinstance( - controller_machine_inst_id_, (bytes, str) - ): - raise Exception( - f"Expected controller_machine_inst_id_ to be a str, received: {type(controller_machine_inst_id_)}" - ) - - if controller_uuid_ is not None and not isinstance( - controller_uuid_, (bytes, str) - ): - raise Exception( - f"Expected controller_uuid_ to be a str, received: {type(controller_uuid_)}" - ) - - if filename_ is not None and not isinstance(filename_, (bytes, str)): - raise Exception( - f"Expected filename_ to be a str, received: {type(filename_)}" - ) - - if finished_ is not None and not isinstance(finished_, (bytes, str)): - raise Exception( - f"Expected finished_ to be a str, received: {type(finished_)}" - ) - - if format_version_ is not None and not isinstance(format_version_, int): - raise Exception( - f"Expected format_version_ to be a int, received: {type(format_version_)}" - ) - - if ha_nodes_ is not None and not isinstance(ha_nodes_, int): - raise Exception( - f"Expected ha_nodes_ to be a int, received: {type(ha_nodes_)}" - ) - - if hostname_ is not None and not isinstance(hostname_, (bytes, str)): - raise Exception( - f"Expected hostname_ to be a str, received: {type(hostname_)}" - ) - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if machine_ is not None and not isinstance(machine_, (bytes, str)): - raise Exception( - f"Expected machine_ to be a str, received: {type(machine_)}" - ) - - if model_ is not None and not isinstance(model_, (bytes, str)): - raise Exception(f"Expected model_ to be a str, received: {type(model_)}") - - if notes_ is not None and not isinstance(notes_, (bytes, str)): - raise Exception(f"Expected notes_ to be a str, received: {type(notes_)}") - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - if started_ is not None and not isinstance(started_, (bytes, str)): - raise Exception( - f"Expected started_ to be a str, received: {type(started_)}" - ) - - if stored_ is not None and not isinstance(stored_, (bytes, str)): - raise Exception(f"Expected stored_ to be a str, received: {type(stored_)}") - - if version_ is not None and not isinstance(version_, (dict, Number)): - raise Exception( - f"Expected version_ to be a Number, received: {type(version_)}" - ) - - self.base = base_ - self.checksum = checksum_ - self.checksum_format = checksum_format_ - self.controller_machine_id = controller_machine_id_ - self.controller_machine_inst_id = controller_machine_inst_id_ - self.controller_uuid = controller_uuid_ - self.filename = filename_ - self.finished = finished_ - self.format_version = format_version_ - self.ha_nodes = ha_nodes_ - self.hostname = hostname_ - self.id_ = id__ - self.machine = machine_ - self.model = model_ - self.notes = notes_ - self.size = size_ - self.started = started_ - self.stored = stored_ - self.version = version_ - self.unknown_fields = unknown_fields - - -class Base(Type): - _toSchema = {"channel": "channel", "name": "name"} - _toPy = {"channel": "channel", "name": "name"} - - def __init__(self, channel=None, name=None, **unknown_fields): - """Channel : str - name : str - """ - channel_ = channel - name_ = name - - # Validate arguments against known Juju API types. - if channel_ is not None and not isinstance(channel_, (bytes, str)): - raise Exception( - f"Expected channel_ to be a str, received: {type(channel_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - self.channel = channel_ - self.name = name_ - self.unknown_fields = unknown_fields - - -class Binary(Type): - _toSchema = { - "arch": "Arch", - "build": "Build", - "major": "Major", - "minor": "Minor", - "number": "Number", - "patch": "Patch", - "release": "Release", - "tag": "Tag", - } - _toPy = { - "Arch": "arch", - "Build": "build", - "Major": "major", - "Minor": "minor", - "Number": "number", - "Patch": "patch", - "Release": "release", - "Tag": "tag", - } - - def __init__( - self, - arch=None, - build=None, - major=None, - minor=None, - number=None, - patch=None, - release=None, - tag=None, - **unknown_fields, - ): - """Arch : str - build : int - major : int - minor : int - number : Number - patch : int - release : str - tag : str - """ - arch_ = arch - build_ = build - major_ = major - minor_ = minor - number_ = Number.from_json(number) if number else None - patch_ = patch - release_ = release - tag_ = tag - - # Validate arguments against known Juju API types. - if arch_ is not None and not isinstance(arch_, (bytes, str)): - raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") - - if build_ is not None and not isinstance(build_, int): - raise Exception(f"Expected build_ to be a int, received: {type(build_)}") - - if major_ is not None and not isinstance(major_, int): - raise Exception(f"Expected major_ to be a int, received: {type(major_)}") - - if minor_ is not None and not isinstance(minor_, int): - raise Exception(f"Expected minor_ to be a int, received: {type(minor_)}") - - if number_ is not None and not isinstance(number_, (dict, Number)): - raise Exception( - f"Expected number_ to be a Number, received: {type(number_)}" - ) - - if patch_ is not None and not isinstance(patch_, int): - raise Exception(f"Expected patch_ to be a int, received: {type(patch_)}") - - if release_ is not None and not isinstance(release_, (bytes, str)): - raise Exception( - f"Expected release_ to be a str, received: {type(release_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.arch = arch_ - self.build = build_ - self.major = major_ - self.minor = minor_ - self.number = number_ - self.patch = patch_ - self.release = release_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class Block(Type): - _toSchema = {"id_": "id", "message": "message", "tag": "tag", "type_": "type"} - _toPy = {"id": "id_", "message": "message", "tag": "tag", "type": "type_"} - - def __init__(self, id_=None, message=None, tag=None, type_=None, **unknown_fields): - """id_ : str - message : str - tag : str - type_ : str - """ - id__ = id_ - message_ = message - tag_ = tag - type__ = type_ - - # Validate arguments against known Juju API types. - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.id_ = id__ - self.message = message_ - self.tag = tag_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class BlockResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : Block - """ - error_ = Error.from_json(error) if error else None - result_ = Block.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, Block)): - raise Exception( - f"Expected result_ to be a Block, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class BlockResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~BlockResult]""" - results_ = [BlockResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class BlockSwitchParams(Type): - _toSchema = {"message": "message", "type_": "type"} - _toPy = {"message": "message", "type": "type_"} - - def __init__(self, message=None, type_=None, **unknown_fields): - """Message : str - type_ : str - """ - message_ = message - type__ = type_ - - # Validate arguments against known Juju API types. - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.message = message_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class BoolResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : bool - """ - error_ = Error.from_json(error) if error else None - result_ = result - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, bool): - raise Exception(f"Expected result_ to be a bool, received: {type(result_)}") - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class BranchArg(Type): - _toSchema = {"branch": "branch"} - _toPy = {"branch": "branch"} - - def __init__(self, branch=None, **unknown_fields): - """Branch : str""" - branch_ = branch - - # Validate arguments against known Juju API types. - if branch_ is not None and not isinstance(branch_, (bytes, str)): - raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") - - self.branch = branch_ - self.unknown_fields = unknown_fields - - -class BranchInfoArgs(Type): - _toSchema = {"branches": "branches", "detailed": "detailed"} - _toPy = {"branches": "branches", "detailed": "detailed"} - - def __init__(self, branches=None, detailed=None, **unknown_fields): - """Branches : typing.Sequence[str] - detailed : bool - """ - branches_ = branches - detailed_ = detailed - - # Validate arguments against known Juju API types. - if branches_ is not None and not isinstance(branches_, (bytes, str, list)): - raise Exception( - f"Expected branches_ to be a Sequence, received: {type(branches_)}" - ) - - if detailed_ is not None and not isinstance(detailed_, bool): - raise Exception( - f"Expected detailed_ to be a bool, received: {type(detailed_)}" - ) - - self.branches = branches_ - self.detailed = detailed_ - self.unknown_fields = unknown_fields - - -class BranchResults(Type): - _toSchema = {"error": "error", "generations": "generations"} - _toPy = {"error": "error", "generations": "generations"} - - def __init__(self, error=None, generations=None, **unknown_fields): - """Error : Error - generations : typing.Sequence[~Generation] - """ - error_ = Error.from_json(error) if error else None - generations_ = [Generation.from_json(o) for o in generations or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if generations_ is not None and not isinstance( - generations_, (bytes, str, list) - ): - raise Exception( - f"Expected generations_ to be a Sequence, received: {type(generations_)}" - ) - - self.error = error_ - self.generations = generations_ - self.unknown_fields = unknown_fields - - -class BranchStatus(Type): - _toSchema = { - "assigned_units": "assigned-units", - "created": "created", - "created_by": "created-by", - } - _toPy = { - "assigned-units": "assigned_units", - "created": "created", - "created-by": "created_by", - } - - def __init__( - self, assigned_units=None, created=None, created_by=None, **unknown_fields - ): - """assigned_units : typing.Mapping[str, typing.Sequence[str]] - created : int - created_by : str - """ - assigned_units_ = assigned_units - created_ = created - created_by_ = created_by - - # Validate arguments against known Juju API types. - if assigned_units_ is not None and not isinstance(assigned_units_, dict): - raise Exception( - f"Expected assigned_units_ to be a Mapping, received: {type(assigned_units_)}" - ) - - if created_ is not None and not isinstance(created_, int): - raise Exception( - f"Expected created_ to be a int, received: {type(created_)}" - ) - - if created_by_ is not None and not isinstance(created_by_, (bytes, str)): - raise Exception( - f"Expected created_by_ to be a str, received: {type(created_by_)}" - ) - - self.assigned_units = assigned_units_ - self.created = created_ - self.created_by = created_by_ - self.unknown_fields = unknown_fields - - -class BranchTrackArg(Type): - _toSchema = {"branch": "branch", "entities": "entities", "num_units": "num-units"} - _toPy = {"branch": "branch", "entities": "entities", "num-units": "num_units"} - - def __init__(self, branch=None, entities=None, num_units=None, **unknown_fields): - """Branch : str - entities : typing.Sequence[~Entity] - num_units : int - """ - branch_ = branch - entities_ = [Entity.from_json(o) for o in entities or []] - num_units_ = num_units - - # Validate arguments against known Juju API types. - if branch_ is not None and not isinstance(branch_, (bytes, str)): - raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") - - if entities_ is not None and not isinstance(entities_, (bytes, str, list)): - raise Exception( - f"Expected entities_ to be a Sequence, received: {type(entities_)}" - ) - - if num_units_ is not None and not isinstance(num_units_, int): - raise Exception( - f"Expected num_units_ to be a int, received: {type(num_units_)}" - ) - - self.branch = branch_ - self.entities = entities_ - self.num_units = num_units_ - self.unknown_fields = unknown_fields - - -class BulkImportStorageParams(Type): - _toSchema = {"storage": "storage"} - _toPy = {"storage": "storage"} - - def __init__(self, storage=None, **unknown_fields): - """Storage : typing.Sequence[~ImportStorageParams]""" - storage_ = [ImportStorageParams.from_json(o) for o in storage or []] - - # Validate arguments against known Juju API types. - if storage_ is not None and not isinstance(storage_, (bytes, str, list)): - raise Exception( - f"Expected storage_ to be a Sequence, received: {type(storage_)}" - ) - - self.storage = storage_ - self.unknown_fields = unknown_fields - - -class BundleChange(Type): - _toSchema = { - "args": "args", - "id_": "id", - "method": "method", - "requires": "requires", - } - _toPy = {"args": "args", "id": "id_", "method": "method", "requires": "requires"} - - def __init__( - self, args=None, id_=None, method=None, requires=None, **unknown_fields - ): - """Args : typing.Sequence[typing.Any] - id_ : str - method : str - requires : typing.Sequence[str] - """ - args_ = args - id__ = id_ - method_ = method - requires_ = requires - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if method_ is not None and not isinstance(method_, (bytes, str)): - raise Exception(f"Expected method_ to be a str, received: {type(method_)}") - - if requires_ is not None and not isinstance(requires_, (bytes, str, list)): - raise Exception( - f"Expected requires_ to be a Sequence, received: {type(requires_)}" - ) - - self.args = args_ - self.id_ = id__ - self.method = method_ - self.requires = requires_ - self.unknown_fields = unknown_fields - - -class BundleChangesMapArgs(Type): - _toSchema = { - "args": "args", - "id_": "id", - "method": "method", - "requires": "requires", - } - _toPy = {"args": "args", "id": "id_", "method": "method", "requires": "requires"} - - def __init__( - self, args=None, id_=None, method=None, requires=None, **unknown_fields - ): - """Args : typing.Mapping[str, typing.Any] - id_ : str - method : str - requires : typing.Sequence[str] - """ - args_ = args - id__ = id_ - method_ = method - requires_ = requires - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, dict): - raise Exception(f"Expected args_ to be a Mapping, received: {type(args_)}") - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if method_ is not None and not isinstance(method_, (bytes, str)): - raise Exception(f"Expected method_ to be a str, received: {type(method_)}") - - if requires_ is not None and not isinstance(requires_, (bytes, str, list)): - raise Exception( - f"Expected requires_ to be a Sequence, received: {type(requires_)}" - ) - - self.args = args_ - self.id_ = id__ - self.method = method_ - self.requires = requires_ - self.unknown_fields = unknown_fields - - -class BundleChangesMapArgsResults(Type): - _toSchema = {"changes": "changes", "errors": "errors"} - _toPy = {"changes": "changes", "errors": "errors"} - - def __init__(self, changes=None, errors=None, **unknown_fields): - """Changes : typing.Sequence[~BundleChangesMapArgs] - errors : typing.Sequence[str] - """ - changes_ = [BundleChangesMapArgs.from_json(o) for o in changes or []] - errors_ = errors - - # Validate arguments against known Juju API types. - if changes_ is not None and not isinstance(changes_, (bytes, str, list)): - raise Exception( - f"Expected changes_ to be a Sequence, received: {type(changes_)}" - ) - - if errors_ is not None and not isinstance(errors_, (bytes, str, list)): - raise Exception( - f"Expected errors_ to be a Sequence, received: {type(errors_)}" - ) - - self.changes = changes_ - self.errors = errors_ - self.unknown_fields = unknown_fields - - -class BundleChangesParams(Type): - _toSchema = {"bundleurl": "bundleURL", "yaml": "yaml"} - _toPy = {"bundleURL": "bundleurl", "yaml": "yaml"} - - def __init__(self, bundleurl=None, yaml=None, **unknown_fields): - """Bundleurl : str - yaml : str - """ - bundleurl_ = bundleurl - yaml_ = yaml - - # Validate arguments against known Juju API types. - if bundleurl_ is not None and not isinstance(bundleurl_, (bytes, str)): - raise Exception( - f"Expected bundleurl_ to be a str, received: {type(bundleurl_)}" - ) - - if yaml_ is not None and not isinstance(yaml_, (bytes, str)): - raise Exception(f"Expected yaml_ to be a str, received: {type(yaml_)}") - - self.bundleurl = bundleurl_ - self.yaml = yaml_ - self.unknown_fields = unknown_fields - - -class BundleChangesResults(Type): - _toSchema = {"changes": "changes", "errors": "errors"} - _toPy = {"changes": "changes", "errors": "errors"} - - def __init__(self, changes=None, errors=None, **unknown_fields): - """Changes : typing.Sequence[~BundleChange] - errors : typing.Sequence[str] - """ - changes_ = [BundleChange.from_json(o) for o in changes or []] - errors_ = errors - - # Validate arguments against known Juju API types. - if changes_ is not None and not isinstance(changes_, (bytes, str, list)): - raise Exception( - f"Expected changes_ to be a Sequence, received: {type(changes_)}" - ) - - if errors_ is not None and not isinstance(errors_, (bytes, str, list)): - raise Exception( - f"Expected errors_ to be a Sequence, received: {type(errors_)}" - ) - - self.changes = changes_ - self.errors = errors_ - self.unknown_fields = unknown_fields - - -class CIDRParams(Type): - _toSchema = {"cidrs": "cidrs"} - _toPy = {"cidrs": "cidrs"} - - def __init__(self, cidrs=None, **unknown_fields): - """Cidrs : typing.Sequence[str]""" - cidrs_ = cidrs - - # Validate arguments against known Juju API types. - if cidrs_ is not None and not isinstance(cidrs_, (bytes, str, list)): - raise Exception( - f"Expected cidrs_ to be a Sequence, received: {type(cidrs_)}" - ) - - self.cidrs = cidrs_ - self.unknown_fields = unknown_fields - - -class ChangeModelCredentialParams(Type): - _toSchema = {"credential_tag": "credential-tag", "model_tag": "model-tag"} - _toPy = {"credential-tag": "credential_tag", "model-tag": "model_tag"} - - def __init__(self, credential_tag=None, model_tag=None, **unknown_fields): - """credential_tag : str - model_tag : str - """ - credential_tag_ = credential_tag - model_tag_ = model_tag - - # Validate arguments against known Juju API types. - if credential_tag_ is not None and not isinstance( - credential_tag_, (bytes, str) - ): - raise Exception( - f"Expected credential_tag_ to be a str, received: {type(credential_tag_)}" - ) - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - self.credential_tag = credential_tag_ - self.model_tag = model_tag_ - self.unknown_fields = unknown_fields - - -class ChangeModelCredentialsParams(Type): - _toSchema = {"model_credentials": "model-credentials"} - _toPy = {"model-credentials": "model_credentials"} - - def __init__(self, model_credentials=None, **unknown_fields): - """model_credentials : typing.Sequence[~ChangeModelCredentialParams]""" - model_credentials_ = [ - ChangeModelCredentialParams.from_json(o) for o in model_credentials or [] - ] - - # Validate arguments against known Juju API types. - if model_credentials_ is not None and not isinstance( - model_credentials_, (bytes, str, list) - ): - raise Exception( - f"Expected model_credentials_ to be a Sequence, received: {type(model_credentials_)}" - ) - - self.model_credentials = model_credentials_ - self.unknown_fields = unknown_fields - - -class Charm(Type): - _toSchema = { - "actions": "actions", - "config": "config", - "lxd_profile": "lxd-profile", - "manifest": "manifest", - "meta": "meta", - "metrics": "metrics", - "revision": "revision", - "url": "url", - } - _toPy = { - "actions": "actions", - "config": "config", - "lxd-profile": "lxd_profile", - "manifest": "manifest", - "meta": "meta", - "metrics": "metrics", - "revision": "revision", - "url": "url", - } - - def __init__( - self, - actions=None, - config=None, - lxd_profile=None, - manifest=None, - meta=None, - metrics=None, - revision=None, - url=None, - **unknown_fields, - ): - """Actions : CharmActions - config : typing.Mapping[str, ~CharmOption] - lxd_profile : CharmLXDProfile - manifest : CharmManifest - meta : CharmMeta - metrics : CharmMetrics - revision : int - url : str - """ - actions_ = CharmActions.from_json(actions) if actions else None - config_ = {k: CharmOption.from_json(v) for k, v in (config or dict()).items()} - lxd_profile_ = CharmLXDProfile.from_json(lxd_profile) if lxd_profile else None - manifest_ = CharmManifest.from_json(manifest) if manifest else None - meta_ = CharmMeta.from_json(meta) if meta else None - metrics_ = CharmMetrics.from_json(metrics) if metrics else None - revision_ = revision - url_ = url - - # Validate arguments against known Juju API types. - if actions_ is not None and not isinstance(actions_, (dict, CharmActions)): - raise Exception( - f"Expected actions_ to be a CharmActions, received: {type(actions_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if lxd_profile_ is not None and not isinstance( - lxd_profile_, (dict, CharmLXDProfile) - ): - raise Exception( - f"Expected lxd_profile_ to be a CharmLXDProfile, received: {type(lxd_profile_)}" - ) - - if manifest_ is not None and not isinstance(manifest_, (dict, CharmManifest)): - raise Exception( - f"Expected manifest_ to be a CharmManifest, received: {type(manifest_)}" - ) - - if meta_ is not None and not isinstance(meta_, (dict, CharmMeta)): - raise Exception( - f"Expected meta_ to be a CharmMeta, received: {type(meta_)}" - ) - - if metrics_ is not None and not isinstance(metrics_, (dict, CharmMetrics)): - raise Exception( - f"Expected metrics_ to be a CharmMetrics, received: {type(metrics_)}" - ) - - if revision_ is not None and not isinstance(revision_, int): - raise Exception( - f"Expected revision_ to be a int, received: {type(revision_)}" - ) - - if url_ is not None and not isinstance(url_, (bytes, str)): - raise Exception(f"Expected url_ to be a str, received: {type(url_)}") - - self.actions = actions_ - self.config = config_ - self.lxd_profile = lxd_profile_ - self.manifest = manifest_ - self.meta = meta_ - self.metrics = metrics_ - self.revision = revision_ - self.url = url_ - self.unknown_fields = unknown_fields - - -class CharmActionSpec(Type): - _toSchema = {"description": "description", "params": "params"} - _toPy = {"description": "description", "params": "params"} - - def __init__(self, description=None, params=None, **unknown_fields): - """Description : str - params : typing.Mapping[str, typing.Any] - """ - description_ = description - params_ = params - - # Validate arguments against known Juju API types. - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if params_ is not None and not isinstance(params_, dict): - raise Exception( - f"Expected params_ to be a Mapping, received: {type(params_)}" - ) - - self.description = description_ - self.params = params_ - self.unknown_fields = unknown_fields - - -class CharmActions(Type): - _toSchema = {"specs": "specs"} - _toPy = {"specs": "specs"} - - def __init__(self, specs=None, **unknown_fields): - """Specs : typing.Mapping[str, ~CharmActionSpec]""" - specs_ = {k: CharmActionSpec.from_json(v) for k, v in (specs or dict()).items()} - - # Validate arguments against known Juju API types. - if specs_ is not None and not isinstance(specs_, dict): - raise Exception( - f"Expected specs_ to be a Mapping, received: {type(specs_)}" - ) - - self.specs = specs_ - self.unknown_fields = unknown_fields - - -class CharmBase(Type): - _toSchema = {"architectures": "architectures", "channel": "channel", "name": "name"} - _toPy = {"architectures": "architectures", "channel": "channel", "name": "name"} - - def __init__(self, architectures=None, channel=None, name=None, **unknown_fields): - """Architectures : typing.Sequence[str] - channel : str - name : str - """ - architectures_ = architectures - channel_ = channel - name_ = name - - # Validate arguments against known Juju API types. - if architectures_ is not None and not isinstance( - architectures_, (bytes, str, list) - ): - raise Exception( - f"Expected architectures_ to be a Sequence, received: {type(architectures_)}" - ) - - if channel_ is not None and not isinstance(channel_, (bytes, str)): - raise Exception( - f"Expected channel_ to be a str, received: {type(channel_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - self.architectures = architectures_ - self.channel = channel_ - self.name = name_ - self.unknown_fields = unknown_fields - - -class CharmContainer(Type): - _toSchema = {"gid": "gid", "mounts": "mounts", "resource": "resource", "uid": "uid"} - _toPy = {"gid": "gid", "mounts": "mounts", "resource": "resource", "uid": "uid"} - - def __init__( - self, gid=None, mounts=None, resource=None, uid=None, **unknown_fields - ): - """Gid : int - mounts : typing.Sequence[~CharmMount] - resource : str - uid : int - """ - gid_ = gid - mounts_ = [CharmMount.from_json(o) for o in mounts or []] - resource_ = resource - uid_ = uid - - # Validate arguments against known Juju API types. - if gid_ is not None and not isinstance(gid_, int): - raise Exception(f"Expected gid_ to be a int, received: {type(gid_)}") - - if mounts_ is not None and not isinstance(mounts_, (bytes, str, list)): - raise Exception( - f"Expected mounts_ to be a Sequence, received: {type(mounts_)}" - ) - - if resource_ is not None and not isinstance(resource_, (bytes, str)): - raise Exception( - f"Expected resource_ to be a str, received: {type(resource_)}" - ) - - if uid_ is not None and not isinstance(uid_, int): - raise Exception(f"Expected uid_ to be a int, received: {type(uid_)}") - - self.gid = gid_ - self.mounts = mounts_ - self.resource = resource_ - self.uid = uid_ - self.unknown_fields = unknown_fields - - -class CharmDeployment(Type): - _toSchema = { - "min_version": "min-version", - "mode": "mode", - "service": "service", - "type_": "type", - } - _toPy = { - "min-version": "min_version", - "mode": "mode", - "service": "service", - "type": "type_", - } - - def __init__( - self, min_version=None, mode=None, service=None, type_=None, **unknown_fields - ): - """min_version : str - mode : str - service : str - type_ : str - """ - min_version_ = min_version - mode_ = mode - service_ = service - type__ = type_ - - # Validate arguments against known Juju API types. - if min_version_ is not None and not isinstance(min_version_, (bytes, str)): - raise Exception( - f"Expected min_version_ to be a str, received: {type(min_version_)}" - ) - - if mode_ is not None and not isinstance(mode_, (bytes, str)): - raise Exception(f"Expected mode_ to be a str, received: {type(mode_)}") - - if service_ is not None and not isinstance(service_, (bytes, str)): - raise Exception( - f"Expected service_ to be a str, received: {type(service_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.min_version = min_version_ - self.mode = mode_ - self.service = service_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmDevice(Type): - _toSchema = { - "countmax": "CountMax", - "countmin": "CountMin", - "description": "Description", - "name": "Name", - "type_": "Type", - } - _toPy = { - "CountMax": "countmax", - "CountMin": "countmin", - "Description": "description", - "Name": "name", - "Type": "type_", - } - - def __init__( - self, - countmax=None, - countmin=None, - description=None, - name=None, - type_=None, - **unknown_fields, - ): - """Countmax : int - countmin : int - description : str - name : str - type_ : str - """ - countmax_ = countmax - countmin_ = countmin - description_ = description - name_ = name - type__ = type_ - - # Validate arguments against known Juju API types. - if countmax_ is not None and not isinstance(countmax_, int): - raise Exception( - f"Expected countmax_ to be a int, received: {type(countmax_)}" - ) - - if countmin_ is not None and not isinstance(countmin_, int): - raise Exception( - f"Expected countmin_ to be a int, received: {type(countmin_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.countmax = countmax_ - self.countmin = countmin_ - self.description = description_ - self.name = name_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmLXDProfile(Type): - _toSchema = {"config": "config", "description": "description", "devices": "devices"} - _toPy = {"config": "config", "description": "description", "devices": "devices"} - - def __init__(self, config=None, description=None, devices=None, **unknown_fields): - """Config : typing.Mapping[str, str] - description : str - devices : typing.Mapping[str, typing.Any] - """ - config_ = config - description_ = description - devices_ = devices - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if devices_ is not None and not isinstance(devices_, dict): - raise Exception( - f"Expected devices_ to be a Mapping, received: {type(devices_)}" - ) - - self.config = config_ - self.description = description_ - self.devices = devices_ - self.unknown_fields = unknown_fields - - -class CharmManifest(Type): - _toSchema = {"bases": "bases"} - _toPy = {"bases": "bases"} - - def __init__(self, bases=None, **unknown_fields): - """Bases : typing.Sequence[~CharmBase]""" - bases_ = [CharmBase.from_json(o) for o in bases or []] - - # Validate arguments against known Juju API types. - if bases_ is not None and not isinstance(bases_, (bytes, str, list)): - raise Exception( - f"Expected bases_ to be a Sequence, received: {type(bases_)}" - ) - - self.bases = bases_ - self.unknown_fields = unknown_fields - - -class CharmMeta(Type): - _toSchema = { - "assumes_expr": "assumes-expr", - "categories": "categories", - "charm_user": "charm-user", - "containers": "containers", - "deployment": "deployment", - "description": "description", - "devices": "devices", - "extra_bindings": "extra-bindings", - "min_juju_version": "min-juju-version", - "name": "name", - "payload_classes": "payload-classes", - "peers": "peers", - "provides": "provides", - "requires": "requires", - "resources": "resources", - "series": "series", - "storage": "storage", - "subordinate": "subordinate", - "summary": "summary", - "tags": "tags", - "terms": "terms", - } - _toPy = { - "assumes-expr": "assumes_expr", - "categories": "categories", - "charm-user": "charm_user", - "containers": "containers", - "deployment": "deployment", - "description": "description", - "devices": "devices", - "extra-bindings": "extra_bindings", - "min-juju-version": "min_juju_version", - "name": "name", - "payload-classes": "payload_classes", - "peers": "peers", - "provides": "provides", - "requires": "requires", - "resources": "resources", - "series": "series", - "storage": "storage", - "subordinate": "subordinate", - "summary": "summary", - "tags": "tags", - "terms": "terms", - } - - def __init__( - self, - assumes_expr=None, - categories=None, - charm_user=None, - containers=None, - deployment=None, - description=None, - devices=None, - extra_bindings=None, - min_juju_version=None, - name=None, - payload_classes=None, - peers=None, - provides=None, - requires=None, - resources=None, - series=None, - storage=None, - subordinate=None, - summary=None, - tags=None, - terms=None, - **unknown_fields, - ): - """assumes_expr : ExpressionTree - categories : typing.Sequence[str] - charm_user : str - containers : typing.Mapping[str, ~CharmContainer] - deployment : CharmDeployment - description : str - devices : typing.Mapping[str, ~CharmDevice] - extra_bindings : typing.Mapping[str, str] - min_juju_version : str - name : str - payload_classes : typing.Mapping[str, ~CharmPayloadClass] - peers : typing.Mapping[str, ~CharmRelation] - provides : typing.Mapping[str, ~CharmRelation] - requires : typing.Mapping[str, ~CharmRelation] - resources : typing.Mapping[str, ~CharmResourceMeta] - series : typing.Sequence[str] - storage : typing.Mapping[str, ~CharmStorage] - subordinate : bool - summary : str - tags : typing.Sequence[str] - terms : typing.Sequence[str] - """ - assumes_expr_ = ExpressionTree.from_json(assumes_expr) if assumes_expr else None - categories_ = categories - charm_user_ = charm_user - containers_ = { - k: CharmContainer.from_json(v) for k, v in (containers or dict()).items() - } - deployment_ = CharmDeployment.from_json(deployment) if deployment else None - description_ = description - devices_ = {k: CharmDevice.from_json(v) for k, v in (devices or dict()).items()} - extra_bindings_ = extra_bindings - min_juju_version_ = min_juju_version - name_ = name - payload_classes_ = { - k: CharmPayloadClass.from_json(v) - for k, v in (payload_classes or dict()).items() - } - peers_ = {k: CharmRelation.from_json(v) for k, v in (peers or dict()).items()} - provides_ = { - k: CharmRelation.from_json(v) for k, v in (provides or dict()).items() - } - requires_ = { - k: CharmRelation.from_json(v) for k, v in (requires or dict()).items() - } - resources_ = { - k: CharmResourceMeta.from_json(v) for k, v in (resources or dict()).items() - } - series_ = series - storage_ = { - k: CharmStorage.from_json(v) for k, v in (storage or dict()).items() - } - subordinate_ = subordinate - summary_ = summary - tags_ = tags - terms_ = terms - - # Validate arguments against known Juju API types. - if assumes_expr_ is not None and not isinstance( - assumes_expr_, (dict, ExpressionTree) - ): - raise Exception( - f"Expected assumes_expr_ to be a ExpressionTree, received: {type(assumes_expr_)}" - ) - - if categories_ is not None and not isinstance(categories_, (bytes, str, list)): - raise Exception( - f"Expected categories_ to be a Sequence, received: {type(categories_)}" - ) - - if charm_user_ is not None and not isinstance(charm_user_, (bytes, str)): - raise Exception( - f"Expected charm_user_ to be a str, received: {type(charm_user_)}" - ) - - if containers_ is not None and not isinstance(containers_, dict): - raise Exception( - f"Expected containers_ to be a Mapping, received: {type(containers_)}" - ) - - if deployment_ is not None and not isinstance( - deployment_, (dict, CharmDeployment) - ): - raise Exception( - f"Expected deployment_ to be a CharmDeployment, received: {type(deployment_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if devices_ is not None and not isinstance(devices_, dict): - raise Exception( - f"Expected devices_ to be a Mapping, received: {type(devices_)}" - ) - - if extra_bindings_ is not None and not isinstance(extra_bindings_, dict): - raise Exception( - f"Expected extra_bindings_ to be a Mapping, received: {type(extra_bindings_)}" - ) - - if min_juju_version_ is not None and not isinstance( - min_juju_version_, (bytes, str) - ): - raise Exception( - f"Expected min_juju_version_ to be a str, received: {type(min_juju_version_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if payload_classes_ is not None and not isinstance(payload_classes_, dict): - raise Exception( - f"Expected payload_classes_ to be a Mapping, received: {type(payload_classes_)}" - ) - - if peers_ is not None and not isinstance(peers_, dict): - raise Exception( - f"Expected peers_ to be a Mapping, received: {type(peers_)}" - ) - - if provides_ is not None and not isinstance(provides_, dict): - raise Exception( - f"Expected provides_ to be a Mapping, received: {type(provides_)}" - ) - - if requires_ is not None and not isinstance(requires_, dict): - raise Exception( - f"Expected requires_ to be a Mapping, received: {type(requires_)}" - ) - - if resources_ is not None and not isinstance(resources_, dict): - raise Exception( - f"Expected resources_ to be a Mapping, received: {type(resources_)}" - ) - - if series_ is not None and not isinstance(series_, (bytes, str, list)): - raise Exception( - f"Expected series_ to be a Sequence, received: {type(series_)}" - ) - - if storage_ is not None and not isinstance(storage_, dict): - raise Exception( - f"Expected storage_ to be a Mapping, received: {type(storage_)}" - ) - - if subordinate_ is not None and not isinstance(subordinate_, bool): - raise Exception( - f"Expected subordinate_ to be a bool, received: {type(subordinate_)}" - ) - - if summary_ is not None and not isinstance(summary_, (bytes, str)): - raise Exception( - f"Expected summary_ to be a str, received: {type(summary_)}" - ) - - if tags_ is not None and not isinstance(tags_, (bytes, str, list)): - raise Exception(f"Expected tags_ to be a Sequence, received: {type(tags_)}") - - if terms_ is not None and not isinstance(terms_, (bytes, str, list)): - raise Exception( - f"Expected terms_ to be a Sequence, received: {type(terms_)}" - ) - - self.assumes_expr = assumes_expr_ - self.categories = categories_ - self.charm_user = charm_user_ - self.containers = containers_ - self.deployment = deployment_ - self.description = description_ - self.devices = devices_ - self.extra_bindings = extra_bindings_ - self.min_juju_version = min_juju_version_ - self.name = name_ - self.payload_classes = payload_classes_ - self.peers = peers_ - self.provides = provides_ - self.requires = requires_ - self.resources = resources_ - self.series = series_ - self.storage = storage_ - self.subordinate = subordinate_ - self.summary = summary_ - self.tags = tags_ - self.terms = terms_ - self.unknown_fields = unknown_fields - - -class CharmMetric(Type): - _toSchema = {"description": "description", "type_": "type"} - _toPy = {"description": "description", "type": "type_"} - - def __init__(self, description=None, type_=None, **unknown_fields): - """Description : str - type_ : str - """ - description_ = description - type__ = type_ - - # Validate arguments against known Juju API types. - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.description = description_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmMetrics(Type): - _toSchema = {"metrics": "metrics", "plan": "plan"} - _toPy = {"metrics": "metrics", "plan": "plan"} - - def __init__(self, metrics=None, plan=None, **unknown_fields): - """Metrics : typing.Mapping[str, ~CharmMetric] - plan : CharmPlan - """ - metrics_ = {k: CharmMetric.from_json(v) for k, v in (metrics or dict()).items()} - plan_ = CharmPlan.from_json(plan) if plan else None - - # Validate arguments against known Juju API types. - if metrics_ is not None and not isinstance(metrics_, dict): - raise Exception( - f"Expected metrics_ to be a Mapping, received: {type(metrics_)}" - ) - - if plan_ is not None and not isinstance(plan_, (dict, CharmPlan)): - raise Exception( - f"Expected plan_ to be a CharmPlan, received: {type(plan_)}" - ) - - self.metrics = metrics_ - self.plan = plan_ - self.unknown_fields = unknown_fields - - -class CharmMount(Type): - _toSchema = {"location": "location", "storage": "storage"} - _toPy = {"location": "location", "storage": "storage"} - - def __init__(self, location=None, storage=None, **unknown_fields): - """Location : str - storage : str - """ - location_ = location - storage_ = storage - - # Validate arguments against known Juju API types. - if location_ is not None and not isinstance(location_, (bytes, str)): - raise Exception( - f"Expected location_ to be a str, received: {type(location_)}" - ) - - if storage_ is not None and not isinstance(storage_, (bytes, str)): - raise Exception( - f"Expected storage_ to be a str, received: {type(storage_)}" - ) - - self.location = location_ - self.storage = storage_ - self.unknown_fields = unknown_fields - - -class CharmOption(Type): - _toSchema = {"default": "default", "description": "description", "type_": "type"} - _toPy = {"default": "default", "description": "description", "type": "type_"} - - def __init__(self, default=None, description=None, type_=None, **unknown_fields): - """Default : Any - description : str - type_ : str - """ - default_ = default - description_ = description - type__ = type_ - - # Validate arguments against known Juju API types. - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.default = default_ - self.description = description_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmOrigin(Type): - _toSchema = { - "architecture": "architecture", - "base": "base", - "branch": "branch", - "hash_": "hash", - "id_": "id", - "instance_key": "instance-key", - "revision": "revision", - "risk": "risk", - "source": "source", - "track": "track", - "type_": "type", - } - _toPy = { - "architecture": "architecture", - "base": "base", - "branch": "branch", - "hash": "hash_", - "id": "id_", - "instance-key": "instance_key", - "revision": "revision", - "risk": "risk", - "source": "source", - "track": "track", - "type": "type_", - } - - def __init__( - self, - architecture=None, - base=None, - branch=None, - hash_=None, - id_=None, - instance_key=None, - revision=None, - risk=None, - source=None, - track=None, - type_=None, - **unknown_fields, - ): - """Architecture : str - base : Base - branch : str - hash_ : str - id_ : str - instance_key : str - revision : int - risk : str - source : str - track : str - type_ : str - """ - architecture_ = architecture - base_ = Base.from_json(base) if base else None - branch_ = branch - hash__ = hash_ - id__ = id_ - instance_key_ = instance_key - revision_ = revision - risk_ = risk - source_ = source - track_ = track - type__ = type_ - - # Validate arguments against known Juju API types. - if architecture_ is not None and not isinstance(architecture_, (bytes, str)): - raise Exception( - f"Expected architecture_ to be a str, received: {type(architecture_)}" - ) - - if base_ is not None and not isinstance(base_, (dict, Base)): - raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") - - if branch_ is not None and not isinstance(branch_, (bytes, str)): - raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") - - if hash__ is not None and not isinstance(hash__, (bytes, str)): - raise Exception(f"Expected hash__ to be a str, received: {type(hash__)}") - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if instance_key_ is not None and not isinstance(instance_key_, (bytes, str)): - raise Exception( - f"Expected instance_key_ to be a str, received: {type(instance_key_)}" - ) - - if revision_ is not None and not isinstance(revision_, int): - raise Exception( - f"Expected revision_ to be a int, received: {type(revision_)}" - ) - - if risk_ is not None and not isinstance(risk_, (bytes, str)): - raise Exception(f"Expected risk_ to be a str, received: {type(risk_)}") - - if source_ is not None and not isinstance(source_, (bytes, str)): - raise Exception(f"Expected source_ to be a str, received: {type(source_)}") - - if track_ is not None and not isinstance(track_, (bytes, str)): - raise Exception(f"Expected track_ to be a str, received: {type(track_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.architecture = architecture_ - self.base = base_ - self.branch = branch_ - self.hash_ = hash__ - self.id_ = id__ - self.instance_key = instance_key_ - self.revision = revision_ - self.risk = risk_ - self.source = source_ - self.track = track_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmOriginResult(Type): - _toSchema = {"charm_origin": "charm-origin", "error": "error"} - _toPy = {"charm-origin": "charm_origin", "error": "error"} - - def __init__(self, charm_origin=None, error=None, **unknown_fields): - """charm_origin : CharmOrigin - error : Error - """ - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.charm_origin = charm_origin_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class CharmPayloadClass(Type): - _toSchema = {"name": "name", "type_": "type"} - _toPy = {"name": "name", "type": "type_"} - - def __init__(self, name=None, type_=None, **unknown_fields): - """Name : str - type_ : str - """ - name_ = name - type__ = type_ - - # Validate arguments against known Juju API types. - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.name = name_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmPlan(Type): - _toSchema = {"required": "required"} - _toPy = {"required": "required"} - - def __init__(self, required=None, **unknown_fields): - """Required : bool""" - required_ = required - - # Validate arguments against known Juju API types. - if required_ is not None and not isinstance(required_, bool): - raise Exception( - f"Expected required_ to be a bool, received: {type(required_)}" - ) - - self.required = required_ - self.unknown_fields = unknown_fields - - -class CharmRelation(Type): - _toSchema = { - "interface": "interface", - "limit": "limit", - "name": "name", - "optional": "optional", - "role": "role", - "scope": "scope", - } - _toPy = { - "interface": "interface", - "limit": "limit", - "name": "name", - "optional": "optional", - "role": "role", - "scope": "scope", - } - - def __init__( - self, - interface=None, - limit=None, - name=None, - optional=None, - role=None, - scope=None, - **unknown_fields, - ): - """Interface : str - limit : int - name : str - optional : bool - role : str - scope : str - """ - interface_ = interface - limit_ = limit - name_ = name - optional_ = optional - role_ = role - scope_ = scope - - # Validate arguments against known Juju API types. - if interface_ is not None and not isinstance(interface_, (bytes, str)): - raise Exception( - f"Expected interface_ to be a str, received: {type(interface_)}" - ) - - if limit_ is not None and not isinstance(limit_, int): - raise Exception(f"Expected limit_ to be a int, received: {type(limit_)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if optional_ is not None and not isinstance(optional_, bool): - raise Exception( - f"Expected optional_ to be a bool, received: {type(optional_)}" - ) - - if role_ is not None and not isinstance(role_, (bytes, str)): - raise Exception(f"Expected role_ to be a str, received: {type(role_)}") - - if scope_ is not None and not isinstance(scope_, (bytes, str)): - raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") - - self.interface = interface_ - self.limit = limit_ - self.name = name_ - self.optional = optional_ - self.role = role_ - self.scope = scope_ - self.unknown_fields = unknown_fields - - -class CharmResource(Type): - _toSchema = { - "description": "description", - "fingerprint": "fingerprint", - "name": "name", - "origin": "origin", - "path": "path", - "revision": "revision", - "size": "size", - "type_": "type", - } - _toPy = { - "description": "description", - "fingerprint": "fingerprint", - "name": "name", - "origin": "origin", - "path": "path", - "revision": "revision", - "size": "size", - "type": "type_", - } - - def __init__( - self, - description=None, - fingerprint=None, - name=None, - origin=None, - path=None, - revision=None, - size=None, - type_=None, - **unknown_fields, - ): - """Description : str - fingerprint : typing.Sequence[int] - name : str - origin : str - path : str - revision : int - size : int - type_ : str - """ - description_ = description - fingerprint_ = fingerprint - name_ = name - origin_ = origin - path_ = path - revision_ = revision - size_ = size - type__ = type_ - - # Validate arguments against known Juju API types. - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if fingerprint_ is not None and not isinstance( - fingerprint_, (bytes, str, list) - ): - raise Exception( - f"Expected fingerprint_ to be a Sequence, received: {type(fingerprint_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if origin_ is not None and not isinstance(origin_, (bytes, str)): - raise Exception(f"Expected origin_ to be a str, received: {type(origin_)}") - - if path_ is not None and not isinstance(path_, (bytes, str)): - raise Exception(f"Expected path_ to be a str, received: {type(path_)}") - - if revision_ is not None and not isinstance(revision_, int): - raise Exception( - f"Expected revision_ to be a int, received: {type(revision_)}" - ) - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.description = description_ - self.fingerprint = fingerprint_ - self.name = name_ - self.origin = origin_ - self.path = path_ - self.revision = revision_ - self.size = size_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmResourceMeta(Type): - _toSchema = { - "description": "description", - "name": "name", - "path": "path", - "type_": "type", - } - _toPy = { - "description": "description", - "name": "name", - "path": "path", - "type": "type_", - } - - def __init__( - self, description=None, name=None, path=None, type_=None, **unknown_fields - ): - """Description : str - name : str - path : str - type_ : str - """ - description_ = description - name_ = name - path_ = path - type__ = type_ - - # Validate arguments against known Juju API types. - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if path_ is not None and not isinstance(path_, (bytes, str)): - raise Exception(f"Expected path_ to be a str, received: {type(path_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.description = description_ - self.name = name_ - self.path = path_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmResourceResult(Type): - _toSchema = { - "charmresource": "CharmResource", - "description": "description", - "error": "error", - "errorresult": "ErrorResult", - "fingerprint": "fingerprint", - "name": "name", - "origin": "origin", - "path": "path", - "revision": "revision", - "size": "size", - "type_": "type", - } - _toPy = { - "CharmResource": "charmresource", - "ErrorResult": "errorresult", - "description": "description", - "error": "error", - "fingerprint": "fingerprint", - "name": "name", - "origin": "origin", - "path": "path", - "revision": "revision", - "size": "size", - "type": "type_", - } - - def __init__( - self, - charmresource=None, - errorresult=None, - description=None, - error=None, - fingerprint=None, - name=None, - origin=None, - path=None, - revision=None, - size=None, - type_=None, - **unknown_fields, - ): - """Charmresource : CharmResource - errorresult : ErrorResult - description : str - error : Error - fingerprint : typing.Sequence[int] - name : str - origin : str - path : str - revision : int - size : int - type_ : str - """ - charmresource_ = ( - CharmResource.from_json(charmresource) if charmresource else None - ) - errorresult_ = ErrorResult.from_json(errorresult) if errorresult else None - description_ = description - error_ = Error.from_json(error) if error else None - fingerprint_ = fingerprint - name_ = name - origin_ = origin - path_ = path - revision_ = revision - size_ = size - type__ = type_ - - # Validate arguments against known Juju API types. - if charmresource_ is not None and not isinstance( - charmresource_, (dict, CharmResource) - ): - raise Exception( - f"Expected charmresource_ to be a CharmResource, received: {type(charmresource_)}" - ) - - if errorresult_ is not None and not isinstance( - errorresult_, (dict, ErrorResult) - ): - raise Exception( - f"Expected errorresult_ to be a ErrorResult, received: {type(errorresult_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if fingerprint_ is not None and not isinstance( - fingerprint_, (bytes, str, list) - ): - raise Exception( - f"Expected fingerprint_ to be a Sequence, received: {type(fingerprint_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if origin_ is not None and not isinstance(origin_, (bytes, str)): - raise Exception(f"Expected origin_ to be a str, received: {type(origin_)}") - - if path_ is not None and not isinstance(path_, (bytes, str)): - raise Exception(f"Expected path_ to be a str, received: {type(path_)}") - - if revision_ is not None and not isinstance(revision_, int): - raise Exception( - f"Expected revision_ to be a int, received: {type(revision_)}" - ) - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.charmresource = charmresource_ - self.errorresult = errorresult_ - self.description = description_ - self.error = error_ - self.fingerprint = fingerprint_ - self.name = name_ - self.origin = origin_ - self.path = path_ - self.revision = revision_ - self.size = size_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmResourcesResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~CharmResourceResult]""" - results_ = [CharmResourceResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class CharmStorage(Type): - _toSchema = { - "count_max": "count-max", - "count_min": "count-min", - "description": "description", - "location": "location", - "minimum_size": "minimum-size", - "name": "name", - "properties": "properties", - "read_only": "read-only", - "shared": "shared", - "type_": "type", - } - _toPy = { - "count-max": "count_max", - "count-min": "count_min", - "description": "description", - "location": "location", - "minimum-size": "minimum_size", - "name": "name", - "properties": "properties", - "read-only": "read_only", - "shared": "shared", - "type": "type_", - } - - def __init__( - self, - count_max=None, - count_min=None, - description=None, - location=None, - minimum_size=None, - name=None, - properties=None, - read_only=None, - shared=None, - type_=None, - **unknown_fields, - ): - """count_max : int - count_min : int - description : str - location : str - minimum_size : int - name : str - properties : typing.Sequence[str] - read_only : bool - shared : bool - type_ : str - """ - count_max_ = count_max - count_min_ = count_min - description_ = description - location_ = location - minimum_size_ = minimum_size - name_ = name - properties_ = properties - read_only_ = read_only - shared_ = shared - type__ = type_ - - # Validate arguments against known Juju API types. - if count_max_ is not None and not isinstance(count_max_, int): - raise Exception( - f"Expected count_max_ to be a int, received: {type(count_max_)}" - ) - - if count_min_ is not None and not isinstance(count_min_, int): - raise Exception( - f"Expected count_min_ to be a int, received: {type(count_min_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if location_ is not None and not isinstance(location_, (bytes, str)): - raise Exception( - f"Expected location_ to be a str, received: {type(location_)}" - ) - - if minimum_size_ is not None and not isinstance(minimum_size_, int): - raise Exception( - f"Expected minimum_size_ to be a int, received: {type(minimum_size_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if properties_ is not None and not isinstance(properties_, (bytes, str, list)): - raise Exception( - f"Expected properties_ to be a Sequence, received: {type(properties_)}" - ) - - if read_only_ is not None and not isinstance(read_only_, bool): - raise Exception( - f"Expected read_only_ to be a bool, received: {type(read_only_)}" - ) - - if shared_ is not None and not isinstance(shared_, bool): - raise Exception(f"Expected shared_ to be a bool, received: {type(shared_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.count_max = count_max_ - self.count_min = count_min_ - self.description = description_ - self.location = location_ - self.minimum_size = minimum_size_ - self.name = name_ - self.properties = properties_ - self.read_only = read_only_ - self.shared = shared_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CharmURL(Type): - _toSchema = {"url": "url"} - _toPy = {"url": "url"} - - def __init__(self, url=None, **unknown_fields): - """Url : str""" - url_ = url - - # Validate arguments against known Juju API types. - if url_ is not None and not isinstance(url_, (bytes, str)): - raise Exception(f"Expected url_ to be a str, received: {type(url_)}") - - self.url = url_ - self.unknown_fields = unknown_fields - - -class CharmURLAndOrigin(Type): - _toSchema = { - "charm_origin": "charm-origin", - "charm_url": "charm-url", - "macaroon": "macaroon", - } - _toPy = { - "charm-origin": "charm_origin", - "charm-url": "charm_url", - "macaroon": "macaroon", - } - - def __init__( - self, charm_origin=None, charm_url=None, macaroon=None, **unknown_fields - ): - """charm_origin : CharmOrigin - charm_url : str - macaroon : Macaroon - """ - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - charm_url_ = charm_url - macaroon_ = Macaroon.from_json(macaroon) if macaroon else None - - # Validate arguments against known Juju API types. - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if charm_url_ is not None and not isinstance(charm_url_, (bytes, str)): - raise Exception( - f"Expected charm_url_ to be a str, received: {type(charm_url_)}" - ) - - if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): - raise Exception( - f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" - ) - - self.charm_origin = charm_origin_ - self.charm_url = charm_url_ - self.macaroon = macaroon_ - self.unknown_fields = unknown_fields - - -class CharmURLAndOrigins(Type): - _toSchema = {"entities": "entities"} - _toPy = {"entities": "entities"} - - def __init__(self, entities=None, **unknown_fields): - """Entities : typing.Sequence[~CharmURLAndOrigin]""" - entities_ = [CharmURLAndOrigin.from_json(o) for o in entities or []] - - # Validate arguments against known Juju API types. - if entities_ is not None and not isinstance(entities_, (bytes, str, list)): - raise Exception( - f"Expected entities_ to be a Sequence, received: {type(entities_)}" - ) - - self.entities = entities_ - self.unknown_fields = unknown_fields - - -class CharmURLOriginResult(Type): - _toSchema = {"charm_origin": "charm-origin", "error": "error", "url": "url"} - _toPy = {"charm-origin": "charm_origin", "error": "error", "url": "url"} - - def __init__(self, charm_origin=None, error=None, url=None, **unknown_fields): - """charm_origin : CharmOrigin - error : Error - url : str - """ - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - error_ = Error.from_json(error) if error else None - url_ = url - - # Validate arguments against known Juju API types. - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if url_ is not None and not isinstance(url_, (bytes, str)): - raise Exception(f"Expected url_ to be a str, received: {type(url_)}") - - self.charm_origin = charm_origin_ - self.error = error_ - self.url = url_ - self.unknown_fields = unknown_fields - - -class CharmsList(Type): - _toSchema = {"names": "names"} - _toPy = {"names": "names"} - - def __init__(self, names=None, **unknown_fields): - """Names : typing.Sequence[str]""" - names_ = names - - # Validate arguments against known Juju API types. - if names_ is not None and not isinstance(names_, (bytes, str, list)): - raise Exception( - f"Expected names_ to be a Sequence, received: {type(names_)}" - ) - - self.names = names_ - self.unknown_fields = unknown_fields - - -class CharmsListResult(Type): - _toSchema = {"charm_urls": "charm-urls"} - _toPy = {"charm-urls": "charm_urls"} - - def __init__(self, charm_urls=None, **unknown_fields): - """charm_urls : typing.Sequence[str]""" - charm_urls_ = charm_urls - - # Validate arguments against known Juju API types. - if charm_urls_ is not None and not isinstance(charm_urls_, (bytes, str, list)): - raise Exception( - f"Expected charm_urls_ to be a Sequence, received: {type(charm_urls_)}" - ) - - self.charm_urls = charm_urls_ - self.unknown_fields = unknown_fields - - -class Cloud(Type): - _toSchema = { - "auth_types": "auth-types", - "ca_certificates": "ca-certificates", - "config": "config", - "endpoint": "endpoint", - "host_cloud_region": "host-cloud-region", - "identity_endpoint": "identity-endpoint", - "is_controller_cloud": "is-controller-cloud", - "region_config": "region-config", - "regions": "regions", - "skip_tls_verify": "skip-tls-verify", - "storage_endpoint": "storage-endpoint", - "type_": "type", - } - _toPy = { - "auth-types": "auth_types", - "ca-certificates": "ca_certificates", - "config": "config", - "endpoint": "endpoint", - "host-cloud-region": "host_cloud_region", - "identity-endpoint": "identity_endpoint", - "is-controller-cloud": "is_controller_cloud", - "region-config": "region_config", - "regions": "regions", - "skip-tls-verify": "skip_tls_verify", - "storage-endpoint": "storage_endpoint", - "type": "type_", - } - - def __init__( - self, - auth_types=None, - ca_certificates=None, - config=None, - endpoint=None, - host_cloud_region=None, - identity_endpoint=None, - is_controller_cloud=None, - region_config=None, - regions=None, - skip_tls_verify=None, - storage_endpoint=None, - type_=None, - **unknown_fields, - ): - """auth_types : typing.Sequence[str] - ca_certificates : typing.Sequence[str] - config : typing.Mapping[str, typing.Any] - endpoint : str - host_cloud_region : str - identity_endpoint : str - is_controller_cloud : bool - region_config : typing.Mapping[str, typing.Any] - regions : typing.Sequence[~CloudRegion] - skip_tls_verify : bool - storage_endpoint : str - type_ : str - """ - auth_types_ = auth_types - ca_certificates_ = ca_certificates - config_ = config - endpoint_ = endpoint - host_cloud_region_ = host_cloud_region - identity_endpoint_ = identity_endpoint - is_controller_cloud_ = is_controller_cloud - region_config_ = region_config - regions_ = [CloudRegion.from_json(o) for o in regions or []] - skip_tls_verify_ = skip_tls_verify - storage_endpoint_ = storage_endpoint - type__ = type_ - - # Validate arguments against known Juju API types. - if auth_types_ is not None and not isinstance(auth_types_, (bytes, str, list)): - raise Exception( - f"Expected auth_types_ to be a Sequence, received: {type(auth_types_)}" - ) - - if ca_certificates_ is not None and not isinstance( - ca_certificates_, (bytes, str, list) - ): - raise Exception( - f"Expected ca_certificates_ to be a Sequence, received: {type(ca_certificates_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): - raise Exception( - f"Expected endpoint_ to be a str, received: {type(endpoint_)}" - ) - - if host_cloud_region_ is not None and not isinstance( - host_cloud_region_, (bytes, str) - ): - raise Exception( - f"Expected host_cloud_region_ to be a str, received: {type(host_cloud_region_)}" - ) - - if identity_endpoint_ is not None and not isinstance( - identity_endpoint_, (bytes, str) - ): - raise Exception( - f"Expected identity_endpoint_ to be a str, received: {type(identity_endpoint_)}" - ) - - if is_controller_cloud_ is not None and not isinstance( - is_controller_cloud_, bool - ): - raise Exception( - f"Expected is_controller_cloud_ to be a bool, received: {type(is_controller_cloud_)}" - ) - - if region_config_ is not None and not isinstance(region_config_, dict): - raise Exception( - f"Expected region_config_ to be a Mapping, received: {type(region_config_)}" - ) - - if regions_ is not None and not isinstance(regions_, (bytes, str, list)): - raise Exception( - f"Expected regions_ to be a Sequence, received: {type(regions_)}" - ) - - if skip_tls_verify_ is not None and not isinstance(skip_tls_verify_, bool): - raise Exception( - f"Expected skip_tls_verify_ to be a bool, received: {type(skip_tls_verify_)}" - ) - - if storage_endpoint_ is not None and not isinstance( - storage_endpoint_, (bytes, str) - ): - raise Exception( - f"Expected storage_endpoint_ to be a str, received: {type(storage_endpoint_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.auth_types = auth_types_ - self.ca_certificates = ca_certificates_ - self.config = config_ - self.endpoint = endpoint_ - self.host_cloud_region = host_cloud_region_ - self.identity_endpoint = identity_endpoint_ - self.is_controller_cloud = is_controller_cloud_ - self.region_config = region_config_ - self.regions = regions_ - self.skip_tls_verify = skip_tls_verify_ - self.storage_endpoint = storage_endpoint_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CloudCredential(Type): - _toSchema = {"attrs": "attrs", "auth_type": "auth-type", "redacted": "redacted"} - _toPy = {"attrs": "attrs", "auth-type": "auth_type", "redacted": "redacted"} - - def __init__(self, attrs=None, auth_type=None, redacted=None, **unknown_fields): - """Attrs : typing.Mapping[str, str] - auth_type : str - redacted : typing.Sequence[str] - """ - attrs_ = attrs - auth_type_ = auth_type - redacted_ = redacted - - # Validate arguments against known Juju API types. - if attrs_ is not None and not isinstance(attrs_, dict): - raise Exception( - f"Expected attrs_ to be a Mapping, received: {type(attrs_)}" - ) - - if auth_type_ is not None and not isinstance(auth_type_, (bytes, str)): - raise Exception( - f"Expected auth_type_ to be a str, received: {type(auth_type_)}" - ) - - if redacted_ is not None and not isinstance(redacted_, (bytes, str, list)): - raise Exception( - f"Expected redacted_ to be a Sequence, received: {type(redacted_)}" - ) - - self.attrs = attrs_ - self.auth_type = auth_type_ - self.redacted = redacted_ - self.unknown_fields = unknown_fields - - -class CloudCredentialArg(Type): - _toSchema = {"cloud_name": "cloud-name", "credential_name": "credential-name"} - _toPy = {"cloud-name": "cloud_name", "credential-name": "credential_name"} - - def __init__(self, cloud_name=None, credential_name=None, **unknown_fields): - """cloud_name : str - credential_name : str - """ - cloud_name_ = cloud_name - credential_name_ = credential_name - - # Validate arguments against known Juju API types. - if cloud_name_ is not None and not isinstance(cloud_name_, (bytes, str)): - raise Exception( - f"Expected cloud_name_ to be a str, received: {type(cloud_name_)}" - ) - - if credential_name_ is not None and not isinstance( - credential_name_, (bytes, str) - ): - raise Exception( - f"Expected credential_name_ to be a str, received: {type(credential_name_)}" - ) - - self.cloud_name = cloud_name_ - self.credential_name = credential_name_ - self.unknown_fields = unknown_fields - - -class CloudCredentialArgs(Type): - _toSchema = {"credentials": "credentials", "include_secrets": "include-secrets"} - _toPy = {"credentials": "credentials", "include-secrets": "include_secrets"} - - def __init__(self, credentials=None, include_secrets=None, **unknown_fields): - """Credentials : typing.Sequence[~CloudCredentialArg] - include_secrets : bool - """ - credentials_ = [CloudCredentialArg.from_json(o) for o in credentials or []] - include_secrets_ = include_secrets - - # Validate arguments against known Juju API types. - if credentials_ is not None and not isinstance( - credentials_, (bytes, str, list) - ): - raise Exception( - f"Expected credentials_ to be a Sequence, received: {type(credentials_)}" - ) - - if include_secrets_ is not None and not isinstance(include_secrets_, bool): - raise Exception( - f"Expected include_secrets_ to be a bool, received: {type(include_secrets_)}" - ) - - self.credentials = credentials_ - self.include_secrets = include_secrets_ - self.unknown_fields = unknown_fields - - -class CloudCredentialResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : CloudCredential - """ - error_ = Error.from_json(error) if error else None - result_ = CloudCredential.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, CloudCredential)): - raise Exception( - f"Expected result_ to be a CloudCredential, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class CloudCredentialResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~CloudCredentialResult]""" - results_ = [CloudCredentialResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class CloudDetails(Type): - _toSchema = { - "auth_types": "auth-types", - "endpoint": "endpoint", - "identity_endpoint": "identity-endpoint", - "regions": "regions", - "storage_endpoint": "storage-endpoint", - "type_": "type", - } - _toPy = { - "auth-types": "auth_types", - "endpoint": "endpoint", - "identity-endpoint": "identity_endpoint", - "regions": "regions", - "storage-endpoint": "storage_endpoint", - "type": "type_", - } - - def __init__( - self, - auth_types=None, - endpoint=None, - identity_endpoint=None, - regions=None, - storage_endpoint=None, - type_=None, - **unknown_fields, - ): - """auth_types : typing.Sequence[str] - endpoint : str - identity_endpoint : str - regions : typing.Sequence[~CloudRegion] - storage_endpoint : str - type_ : str - """ - auth_types_ = auth_types - endpoint_ = endpoint - identity_endpoint_ = identity_endpoint - regions_ = [CloudRegion.from_json(o) for o in regions or []] - storage_endpoint_ = storage_endpoint - type__ = type_ - - # Validate arguments against known Juju API types. - if auth_types_ is not None and not isinstance(auth_types_, (bytes, str, list)): - raise Exception( - f"Expected auth_types_ to be a Sequence, received: {type(auth_types_)}" - ) - - if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): - raise Exception( - f"Expected endpoint_ to be a str, received: {type(endpoint_)}" - ) - - if identity_endpoint_ is not None and not isinstance( - identity_endpoint_, (bytes, str) - ): - raise Exception( - f"Expected identity_endpoint_ to be a str, received: {type(identity_endpoint_)}" - ) - - if regions_ is not None and not isinstance(regions_, (bytes, str, list)): - raise Exception( - f"Expected regions_ to be a Sequence, received: {type(regions_)}" - ) - - if storage_endpoint_ is not None and not isinstance( - storage_endpoint_, (bytes, str) - ): - raise Exception( - f"Expected storage_endpoint_ to be a str, received: {type(storage_endpoint_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.auth_types = auth_types_ - self.endpoint = endpoint_ - self.identity_endpoint = identity_endpoint_ - self.regions = regions_ - self.storage_endpoint = storage_endpoint_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CloudImageMetadata(Type): - _toSchema = { - "arch": "arch", - "image_id": "image-id", - "priority": "priority", - "region": "region", - "root_storage_size": "root-storage-size", - "root_storage_type": "root-storage-type", - "source": "source", - "stream": "stream", - "version": "version", - "virt_type": "virt-type", - } - _toPy = { - "arch": "arch", - "image-id": "image_id", - "priority": "priority", - "region": "region", - "root-storage-size": "root_storage_size", - "root-storage-type": "root_storage_type", - "source": "source", - "stream": "stream", - "version": "version", - "virt-type": "virt_type", - } - - def __init__( - self, - arch=None, - image_id=None, - priority=None, - region=None, - root_storage_size=None, - root_storage_type=None, - source=None, - stream=None, - version=None, - virt_type=None, - **unknown_fields, - ): - """Arch : str - image_id : str - priority : int - region : str - root_storage_size : int - root_storage_type : str - source : str - stream : str - version : str - virt_type : str - """ - arch_ = arch - image_id_ = image_id - priority_ = priority - region_ = region - root_storage_size_ = root_storage_size - root_storage_type_ = root_storage_type - source_ = source - stream_ = stream - version_ = version - virt_type_ = virt_type - - # Validate arguments against known Juju API types. - if arch_ is not None and not isinstance(arch_, (bytes, str)): - raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") - - if image_id_ is not None and not isinstance(image_id_, (bytes, str)): - raise Exception( - f"Expected image_id_ to be a str, received: {type(image_id_)}" - ) - - if priority_ is not None and not isinstance(priority_, int): - raise Exception( - f"Expected priority_ to be a int, received: {type(priority_)}" - ) - - if region_ is not None and not isinstance(region_, (bytes, str)): - raise Exception(f"Expected region_ to be a str, received: {type(region_)}") - - if root_storage_size_ is not None and not isinstance(root_storage_size_, int): - raise Exception( - f"Expected root_storage_size_ to be a int, received: {type(root_storage_size_)}" - ) - - if root_storage_type_ is not None and not isinstance( - root_storage_type_, (bytes, str) - ): - raise Exception( - f"Expected root_storage_type_ to be a str, received: {type(root_storage_type_)}" - ) - - if source_ is not None and not isinstance(source_, (bytes, str)): - raise Exception(f"Expected source_ to be a str, received: {type(source_)}") - - if stream_ is not None and not isinstance(stream_, (bytes, str)): - raise Exception(f"Expected stream_ to be a str, received: {type(stream_)}") - - if version_ is not None and not isinstance(version_, (bytes, str)): - raise Exception( - f"Expected version_ to be a str, received: {type(version_)}" - ) - - if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): - raise Exception( - f"Expected virt_type_ to be a str, received: {type(virt_type_)}" - ) - - self.arch = arch_ - self.image_id = image_id_ - self.priority = priority_ - self.region = region_ - self.root_storage_size = root_storage_size_ - self.root_storage_type = root_storage_type_ - self.source = source_ - self.stream = stream_ - self.version = version_ - self.virt_type = virt_type_ - self.unknown_fields = unknown_fields - - -class CloudImageMetadataList(Type): - _toSchema = {"metadata": "metadata"} - _toPy = {"metadata": "metadata"} - - def __init__(self, metadata=None, **unknown_fields): - """Metadata : typing.Sequence[~CloudImageMetadata]""" - metadata_ = [CloudImageMetadata.from_json(o) for o in metadata or []] - - # Validate arguments against known Juju API types. - if metadata_ is not None and not isinstance(metadata_, (bytes, str, list)): - raise Exception( - f"Expected metadata_ to be a Sequence, received: {type(metadata_)}" - ) - - self.metadata = metadata_ - self.unknown_fields = unknown_fields - - -class CloudInfo(Type): - _toSchema = {"clouddetails": "CloudDetails", "users": "users"} - _toPy = {"CloudDetails": "clouddetails", "users": "users"} - - def __init__(self, clouddetails=None, users=None, **unknown_fields): - """Clouddetails : CloudDetails - users : typing.Sequence[~CloudUserInfo] - """ - clouddetails_ = CloudDetails.from_json(clouddetails) if clouddetails else None - users_ = [CloudUserInfo.from_json(o) for o in users or []] - - # Validate arguments against known Juju API types. - if clouddetails_ is not None and not isinstance( - clouddetails_, (dict, CloudDetails) - ): - raise Exception( - f"Expected clouddetails_ to be a CloudDetails, received: {type(clouddetails_)}" - ) - - if users_ is not None and not isinstance(users_, (bytes, str, list)): - raise Exception( - f"Expected users_ to be a Sequence, received: {type(users_)}" - ) - - self.clouddetails = clouddetails_ - self.users = users_ - self.unknown_fields = unknown_fields - - -class CloudInfoResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : CloudInfo - """ - error_ = Error.from_json(error) if error else None - result_ = CloudInfo.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, CloudInfo)): - raise Exception( - f"Expected result_ to be a CloudInfo, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class CloudInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~CloudInfoResult]""" - results_ = [CloudInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class CloudInstanceTypesConstraint(Type): - _toSchema = { - "cloud_tag": "cloud-tag", - "constraints": "constraints", - "region": "region", - } - _toPy = {"cloud-tag": "cloud_tag", "constraints": "constraints", "region": "region"} - - def __init__(self, cloud_tag=None, constraints=None, region=None, **unknown_fields): - """cloud_tag : str - constraints : Value - region : str - """ - cloud_tag_ = cloud_tag - constraints_ = Value.from_json(constraints) if constraints else None - region_ = region - - # Validate arguments against known Juju API types. - if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): - raise Exception( - f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" - ) - - if constraints_ is not None and not isinstance(constraints_, (dict, Value)): - raise Exception( - f"Expected constraints_ to be a Value, received: {type(constraints_)}" - ) - - if region_ is not None and not isinstance(region_, (bytes, str)): - raise Exception(f"Expected region_ to be a str, received: {type(region_)}") - - self.cloud_tag = cloud_tag_ - self.constraints = constraints_ - self.region = region_ - self.unknown_fields = unknown_fields - - -class CloudInstanceTypesConstraints(Type): - _toSchema = {"constraints": "constraints"} - _toPy = {"constraints": "constraints"} - - def __init__(self, constraints=None, **unknown_fields): - """Constraints : typing.Sequence[~CloudInstanceTypesConstraint]""" - constraints_ = [ - CloudInstanceTypesConstraint.from_json(o) for o in constraints or [] - ] - - # Validate arguments against known Juju API types. - if constraints_ is not None and not isinstance( - constraints_, (bytes, str, list) - ): - raise Exception( - f"Expected constraints_ to be a Sequence, received: {type(constraints_)}" - ) - - self.constraints = constraints_ - self.unknown_fields = unknown_fields - - -class CloudRegion(Type): - _toSchema = { - "endpoint": "endpoint", - "identity_endpoint": "identity-endpoint", - "name": "name", - "storage_endpoint": "storage-endpoint", - } - _toPy = { - "endpoint": "endpoint", - "identity-endpoint": "identity_endpoint", - "name": "name", - "storage-endpoint": "storage_endpoint", - } - - def __init__( - self, - endpoint=None, - identity_endpoint=None, - name=None, - storage_endpoint=None, - **unknown_fields, - ): - """Endpoint : str - identity_endpoint : str - name : str - storage_endpoint : str - """ - endpoint_ = endpoint - identity_endpoint_ = identity_endpoint - name_ = name - storage_endpoint_ = storage_endpoint - - # Validate arguments against known Juju API types. - if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): - raise Exception( - f"Expected endpoint_ to be a str, received: {type(endpoint_)}" - ) - - if identity_endpoint_ is not None and not isinstance( - identity_endpoint_, (bytes, str) - ): - raise Exception( - f"Expected identity_endpoint_ to be a str, received: {type(identity_endpoint_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if storage_endpoint_ is not None and not isinstance( - storage_endpoint_, (bytes, str) - ): - raise Exception( - f"Expected storage_endpoint_ to be a str, received: {type(storage_endpoint_)}" - ) - - self.endpoint = endpoint_ - self.identity_endpoint = identity_endpoint_ - self.name = name_ - self.storage_endpoint = storage_endpoint_ - self.unknown_fields = unknown_fields - - -class CloudResult(Type): - _toSchema = {"cloud": "cloud", "error": "error"} - _toPy = {"cloud": "cloud", "error": "error"} - - def __init__(self, cloud=None, error=None, **unknown_fields): - """Cloud : Cloud - error : Error - """ - cloud_ = Cloud.from_json(cloud) if cloud else None - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if cloud_ is not None and not isinstance(cloud_, (dict, Cloud)): - raise Exception(f"Expected cloud_ to be a Cloud, received: {type(cloud_)}") - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.cloud = cloud_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class CloudResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~CloudResult]""" - results_ = [CloudResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class CloudSpec(Type): - _toSchema = { - "cacertificates": "cacertificates", - "credential": "credential", - "endpoint": "endpoint", - "identity_endpoint": "identity-endpoint", - "is_controller_cloud": "is-controller-cloud", - "name": "name", - "region": "region", - "skip_tls_verify": "skip-tls-verify", - "storage_endpoint": "storage-endpoint", - "type_": "type", - } - _toPy = { - "cacertificates": "cacertificates", - "credential": "credential", - "endpoint": "endpoint", - "identity-endpoint": "identity_endpoint", - "is-controller-cloud": "is_controller_cloud", - "name": "name", - "region": "region", - "skip-tls-verify": "skip_tls_verify", - "storage-endpoint": "storage_endpoint", - "type": "type_", - } - - def __init__( - self, - cacertificates=None, - credential=None, - endpoint=None, - identity_endpoint=None, - is_controller_cloud=None, - name=None, - region=None, - skip_tls_verify=None, - storage_endpoint=None, - type_=None, - **unknown_fields, - ): - """Cacertificates : typing.Sequence[str] - credential : CloudCredential - endpoint : str - identity_endpoint : str - is_controller_cloud : bool - name : str - region : str - skip_tls_verify : bool - storage_endpoint : str - type_ : str - """ - cacertificates_ = cacertificates - credential_ = CloudCredential.from_json(credential) if credential else None - endpoint_ = endpoint - identity_endpoint_ = identity_endpoint - is_controller_cloud_ = is_controller_cloud - name_ = name - region_ = region - skip_tls_verify_ = skip_tls_verify - storage_endpoint_ = storage_endpoint - type__ = type_ - - # Validate arguments against known Juju API types. - if cacertificates_ is not None and not isinstance( - cacertificates_, (bytes, str, list) - ): - raise Exception( - f"Expected cacertificates_ to be a Sequence, received: {type(cacertificates_)}" - ) - - if credential_ is not None and not isinstance( - credential_, (dict, CloudCredential) - ): - raise Exception( - f"Expected credential_ to be a CloudCredential, received: {type(credential_)}" - ) - - if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): - raise Exception( - f"Expected endpoint_ to be a str, received: {type(endpoint_)}" - ) - - if identity_endpoint_ is not None and not isinstance( - identity_endpoint_, (bytes, str) - ): - raise Exception( - f"Expected identity_endpoint_ to be a str, received: {type(identity_endpoint_)}" - ) - - if is_controller_cloud_ is not None and not isinstance( - is_controller_cloud_, bool - ): - raise Exception( - f"Expected is_controller_cloud_ to be a bool, received: {type(is_controller_cloud_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if region_ is not None and not isinstance(region_, (bytes, str)): - raise Exception(f"Expected region_ to be a str, received: {type(region_)}") - - if skip_tls_verify_ is not None and not isinstance(skip_tls_verify_, bool): - raise Exception( - f"Expected skip_tls_verify_ to be a bool, received: {type(skip_tls_verify_)}" - ) - - if storage_endpoint_ is not None and not isinstance( - storage_endpoint_, (bytes, str) - ): - raise Exception( - f"Expected storage_endpoint_ to be a str, received: {type(storage_endpoint_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.cacertificates = cacertificates_ - self.credential = credential_ - self.endpoint = endpoint_ - self.identity_endpoint = identity_endpoint_ - self.is_controller_cloud = is_controller_cloud_ - self.name = name_ - self.region = region_ - self.skip_tls_verify = skip_tls_verify_ - self.storage_endpoint = storage_endpoint_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class CloudSpecResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : CloudSpec - """ - error_ = Error.from_json(error) if error else None - result_ = CloudSpec.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, CloudSpec)): - raise Exception( - f"Expected result_ to be a CloudSpec, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class CloudSpecResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~CloudSpecResult]""" - results_ = [CloudSpecResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class CloudUserInfo(Type): - _toSchema = {"access": "access", "display_name": "display-name", "user": "user"} - _toPy = {"access": "access", "display-name": "display_name", "user": "user"} - - def __init__(self, access=None, display_name=None, user=None, **unknown_fields): - """Access : str - display_name : str - user : str - """ - access_ = access - display_name_ = display_name - user_ = user - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if display_name_ is not None and not isinstance(display_name_, (bytes, str)): - raise Exception( - f"Expected display_name_ to be a str, received: {type(display_name_)}" - ) - - if user_ is not None and not isinstance(user_, (bytes, str)): - raise Exception(f"Expected user_ to be a str, received: {type(user_)}") - - self.access = access_ - self.display_name = display_name_ - self.user = user_ - self.unknown_fields = unknown_fields - - -class CloudsResult(Type): - _toSchema = {"clouds": "clouds"} - _toPy = {"clouds": "clouds"} - - def __init__(self, clouds=None, **unknown_fields): - """Clouds : typing.Mapping[str, ~Cloud]""" - clouds_ = {k: Cloud.from_json(v) for k, v in (clouds or dict()).items()} - - # Validate arguments against known Juju API types. - if clouds_ is not None and not isinstance(clouds_, dict): - raise Exception( - f"Expected clouds_ to be a Mapping, received: {type(clouds_)}" - ) - - self.clouds = clouds_ - self.unknown_fields = unknown_fields - - -class ConfigResult(Type): - _toSchema = {"config": "config", "error": "error"} - _toPy = {"config": "config", "error": "error"} - - def __init__(self, config=None, error=None, **unknown_fields): - """Config : typing.Mapping[str, typing.Any] - error : Error - """ - config_ = config - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.config = config_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class ConfigSet(Type): - _toSchema = { - "application": "application", - "config": "config", - "config_yaml": "config-yaml", - "generation": "generation", - } - _toPy = { - "application": "application", - "config": "config", - "config-yaml": "config_yaml", - "generation": "generation", - } - - def __init__( - self, - application=None, - config=None, - config_yaml=None, - generation=None, - **unknown_fields, - ): - """Application : str - config : typing.Mapping[str, str] - config_yaml : str - generation : str - """ - application_ = application - config_ = config - config_yaml_ = config_yaml - generation_ = generation - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if config_yaml_ is not None and not isinstance(config_yaml_, (bytes, str)): - raise Exception( - f"Expected config_yaml_ to be a str, received: {type(config_yaml_)}" - ) - - if generation_ is not None and not isinstance(generation_, (bytes, str)): - raise Exception( - f"Expected generation_ to be a str, received: {type(generation_)}" - ) - - self.application = application_ - self.config = config_ - self.config_yaml = config_yaml_ - self.generation = generation_ - self.unknown_fields = unknown_fields - - -class ConfigSetArgs(Type): - _toSchema = {"args": "Args"} - _toPy = {"Args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~ConfigSet]""" - args_ = [ConfigSet.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class ConfigValue(Type): - _toSchema = {"source": "source", "value": "value"} - _toPy = {"source": "source", "value": "value"} - - def __init__(self, source=None, value=None, **unknown_fields): - """Source : str - value : Any - """ - source_ = source - value_ = value - - # Validate arguments against known Juju API types. - if source_ is not None and not isinstance(source_, (bytes, str)): - raise Exception(f"Expected source_ to be a str, received: {type(source_)}") - - self.source = source_ - self.value = value_ - self.unknown_fields = unknown_fields - - -class Constraints(Type): - _toSchema = {"count": "Count", "pool": "Pool", "size": "Size"} - _toPy = {"Count": "count", "Pool": "pool", "Size": "size"} - - def __init__(self, count=None, pool=None, size=None, **unknown_fields): - """Count : int - pool : str - size : int - """ - count_ = count - pool_ = pool - size_ = size - - # Validate arguments against known Juju API types. - if count_ is not None and not isinstance(count_, int): - raise Exception(f"Expected count_ to be a int, received: {type(count_)}") - - if pool_ is not None and not isinstance(pool_, (bytes, str)): - raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - self.count = count_ - self.pool = pool_ - self.size = size_ - self.unknown_fields = unknown_fields - - -class ConsumeApplicationArg(Type): - _toSchema = { - "application_alias": "application-alias", - "application_description": "application-description", - "applicationofferdetails": "ApplicationOfferDetails", - "bindings": "bindings", - "endpoints": "endpoints", - "external_controller": "external-controller", - "macaroon": "macaroon", - "offer_name": "offer-name", - "offer_url": "offer-url", - "offer_uuid": "offer-uuid", - "source_model_tag": "source-model-tag", - "spaces": "spaces", - "users": "users", - } - _toPy = { - "ApplicationOfferDetails": "applicationofferdetails", - "application-alias": "application_alias", - "application-description": "application_description", - "bindings": "bindings", - "endpoints": "endpoints", - "external-controller": "external_controller", - "macaroon": "macaroon", - "offer-name": "offer_name", - "offer-url": "offer_url", - "offer-uuid": "offer_uuid", - "source-model-tag": "source_model_tag", - "spaces": "spaces", - "users": "users", - } - - def __init__( - self, - applicationofferdetails=None, - application_alias=None, - application_description=None, - bindings=None, - endpoints=None, - external_controller=None, - macaroon=None, - offer_name=None, - offer_url=None, - offer_uuid=None, - source_model_tag=None, - spaces=None, - users=None, - **unknown_fields, - ): - """Applicationofferdetails : ApplicationOfferDetails - application_alias : str - application_description : str - bindings : typing.Mapping[str, str] - endpoints : typing.Sequence[~RemoteEndpoint] - external_controller : ExternalControllerInfo - macaroon : Macaroon - offer_name : str - offer_url : str - offer_uuid : str - source_model_tag : str - spaces : typing.Sequence[~RemoteSpace] - users : typing.Sequence[~OfferUserDetails] - """ - applicationofferdetails_ = ( - ApplicationOfferDetails.from_json(applicationofferdetails) - if applicationofferdetails - else None - ) - application_alias_ = application_alias - application_description_ = application_description - bindings_ = bindings - endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] - external_controller_ = ( - ExternalControllerInfo.from_json(external_controller) - if external_controller - else None - ) - macaroon_ = Macaroon.from_json(macaroon) if macaroon else None - offer_name_ = offer_name - offer_url_ = offer_url - offer_uuid_ = offer_uuid - source_model_tag_ = source_model_tag - spaces_ = [RemoteSpace.from_json(o) for o in spaces or []] - users_ = [OfferUserDetails.from_json(o) for o in users or []] - - # Validate arguments against known Juju API types. - if applicationofferdetails_ is not None and not isinstance( - applicationofferdetails_, (dict, ApplicationOfferDetails) - ): - raise Exception( - f"Expected applicationofferdetails_ to be a ApplicationOfferDetails, received: {type(applicationofferdetails_)}" - ) - - if application_alias_ is not None and not isinstance( - application_alias_, (bytes, str) - ): - raise Exception( - f"Expected application_alias_ to be a str, received: {type(application_alias_)}" - ) - - if application_description_ is not None and not isinstance( - application_description_, (bytes, str) - ): - raise Exception( - f"Expected application_description_ to be a str, received: {type(application_description_)}" - ) - - if bindings_ is not None and not isinstance(bindings_, dict): - raise Exception( - f"Expected bindings_ to be a Mapping, received: {type(bindings_)}" - ) - - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if external_controller_ is not None and not isinstance( - external_controller_, (dict, ExternalControllerInfo) - ): - raise Exception( - f"Expected external_controller_ to be a ExternalControllerInfo, received: {type(external_controller_)}" - ) - - if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): - raise Exception( - f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" - ) - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): - raise Exception( - f"Expected offer_url_ to be a str, received: {type(offer_url_)}" - ) - - if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): - raise Exception( - f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" - ) - - if source_model_tag_ is not None and not isinstance( - source_model_tag_, (bytes, str) - ): - raise Exception( - f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" - ) - - if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): - raise Exception( - f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" - ) - - if users_ is not None and not isinstance(users_, (bytes, str, list)): - raise Exception( - f"Expected users_ to be a Sequence, received: {type(users_)}" - ) - - self.applicationofferdetails = applicationofferdetails_ - self.application_alias = application_alias_ - self.application_description = application_description_ - self.bindings = bindings_ - self.endpoints = endpoints_ - self.external_controller = external_controller_ - self.macaroon = macaroon_ - self.offer_name = offer_name_ - self.offer_url = offer_url_ - self.offer_uuid = offer_uuid_ - self.source_model_tag = source_model_tag_ - self.spaces = spaces_ - self.users = users_ - self.unknown_fields = unknown_fields - - -class ConsumeApplicationArgV5(Type): - _toSchema = { - "application_alias": "application-alias", - "application_description": "application-description", - "applicationofferdetailsv5": "ApplicationOfferDetailsV5", - "endpoints": "endpoints", - "external_controller": "external-controller", - "macaroon": "macaroon", - "offer_name": "offer-name", - "offer_url": "offer-url", - "offer_uuid": "offer-uuid", - "source_model_tag": "source-model-tag", - "users": "users", - } - _toPy = { - "ApplicationOfferDetailsV5": "applicationofferdetailsv5", - "application-alias": "application_alias", - "application-description": "application_description", - "endpoints": "endpoints", - "external-controller": "external_controller", - "macaroon": "macaroon", - "offer-name": "offer_name", - "offer-url": "offer_url", - "offer-uuid": "offer_uuid", - "source-model-tag": "source_model_tag", - "users": "users", - } - - def __init__( - self, - applicationofferdetailsv5=None, - application_alias=None, - application_description=None, - endpoints=None, - external_controller=None, - macaroon=None, - offer_name=None, - offer_url=None, - offer_uuid=None, - source_model_tag=None, - users=None, - **unknown_fields, - ): - """applicationofferdetailsv5 : ApplicationOfferDetailsV5 - application_alias : str - application_description : str - endpoints : typing.Sequence[~RemoteEndpoint] - external_controller : ExternalControllerInfo - macaroon : Macaroon - offer_name : str - offer_url : str - offer_uuid : str - source_model_tag : str - users : typing.Sequence[~OfferUserDetails] - """ - applicationofferdetailsv5_ = ( - ApplicationOfferDetailsV5.from_json(applicationofferdetailsv5) - if applicationofferdetailsv5 - else None - ) - application_alias_ = application_alias - application_description_ = application_description - endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] - external_controller_ = ( - ExternalControllerInfo.from_json(external_controller) - if external_controller - else None - ) - macaroon_ = Macaroon.from_json(macaroon) if macaroon else None - offer_name_ = offer_name - offer_url_ = offer_url - offer_uuid_ = offer_uuid - source_model_tag_ = source_model_tag - users_ = [OfferUserDetails.from_json(o) for o in users or []] - - # Validate arguments against known Juju API types. - if applicationofferdetailsv5_ is not None and not isinstance( - applicationofferdetailsv5_, (dict, ApplicationOfferDetailsV5) - ): - raise Exception( - f"Expected applicationofferdetailsv5_ to be a ApplicationOfferDetailsV5, received: {type(applicationofferdetailsv5_)}" - ) - - if application_alias_ is not None and not isinstance( - application_alias_, (bytes, str) - ): - raise Exception( - f"Expected application_alias_ to be a str, received: {type(application_alias_)}" - ) - - if application_description_ is not None and not isinstance( - application_description_, (bytes, str) - ): - raise Exception( - f"Expected application_description_ to be a str, received: {type(application_description_)}" - ) - - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if external_controller_ is not None and not isinstance( - external_controller_, (dict, ExternalControllerInfo) - ): - raise Exception( - f"Expected external_controller_ to be a ExternalControllerInfo, received: {type(external_controller_)}" - ) - - if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): - raise Exception( - f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" - ) - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): - raise Exception( - f"Expected offer_url_ to be a str, received: {type(offer_url_)}" - ) - - if offer_uuid_ is not None and not isinstance(offer_uuid_, (bytes, str)): - raise Exception( - f"Expected offer_uuid_ to be a str, received: {type(offer_uuid_)}" - ) - - if source_model_tag_ is not None and not isinstance( - source_model_tag_, (bytes, str) - ): - raise Exception( - f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" - ) - - if users_ is not None and not isinstance(users_, (bytes, str, list)): - raise Exception( - f"Expected users_ to be a Sequence, received: {type(users_)}" - ) - - self.applicationofferdetailsv5 = applicationofferdetailsv5_ - self.application_alias = application_alias_ - self.application_description = application_description_ - self.endpoints = endpoints_ - self.external_controller = external_controller_ - self.macaroon = macaroon_ - self.offer_name = offer_name_ - self.offer_url = offer_url_ - self.offer_uuid = offer_uuid_ - self.source_model_tag = source_model_tag_ - self.users = users_ - self.unknown_fields = unknown_fields - - -class ConsumeApplicationArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~ConsumeApplicationArg]""" - args_ = [ConsumeApplicationArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class ConsumeApplicationArgsV5(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~ConsumeApplicationArgV5]""" - args_ = [ConsumeApplicationArgV5.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class ConsumeOfferDetails(Type): - _toSchema = { - "external_controller": "external-controller", - "macaroon": "macaroon", - "offer": "offer", - } - _toPy = { - "external-controller": "external_controller", - "macaroon": "macaroon", - "offer": "offer", - } - - def __init__( - self, external_controller=None, macaroon=None, offer=None, **unknown_fields - ): - """external_controller : ExternalControllerInfo - macaroon : Macaroon - offer : ApplicationOfferDetailsV5 - """ - external_controller_ = ( - ExternalControllerInfo.from_json(external_controller) - if external_controller - else None - ) - macaroon_ = Macaroon.from_json(macaroon) if macaroon else None - offer_ = ApplicationOfferDetailsV5.from_json(offer) if offer else None - - # Validate arguments against known Juju API types. - if external_controller_ is not None and not isinstance( - external_controller_, (dict, ExternalControllerInfo) - ): - raise Exception( - f"Expected external_controller_ to be a ExternalControllerInfo, received: {type(external_controller_)}" - ) - - if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): - raise Exception( - f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" - ) - - if offer_ is not None and not isinstance( - offer_, (dict, ApplicationOfferDetailsV5) - ): - raise Exception( - f"Expected offer_ to be a ApplicationOfferDetailsV5, received: {type(offer_)}" - ) - - self.external_controller = external_controller_ - self.macaroon = macaroon_ - self.offer = offer_ - self.unknown_fields = unknown_fields - - -class ConsumeOfferDetailsArg(Type): - _toSchema = {"offer_urls": "offer-urls", "user_tag": "user-tag"} - _toPy = {"offer-urls": "offer_urls", "user-tag": "user_tag"} - - def __init__(self, offer_urls=None, user_tag=None, **unknown_fields): - """offer_urls : OfferURLs - user_tag : str - """ - offer_urls_ = OfferURLs.from_json(offer_urls) if offer_urls else None - user_tag_ = user_tag - - # Validate arguments against known Juju API types. - if offer_urls_ is not None and not isinstance(offer_urls_, (dict, OfferURLs)): - raise Exception( - f"Expected offer_urls_ to be a OfferURLs, received: {type(offer_urls_)}" - ) - - if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): - raise Exception( - f"Expected user_tag_ to be a str, received: {type(user_tag_)}" - ) - - self.offer_urls = offer_urls_ - self.user_tag = user_tag_ - self.unknown_fields = unknown_fields - - -class ConsumeOfferDetailsResult(Type): - _toSchema = { - "consumeofferdetails": "ConsumeOfferDetails", - "error": "error", - "external_controller": "external-controller", - "macaroon": "macaroon", - "offer": "offer", - } - _toPy = { - "ConsumeOfferDetails": "consumeofferdetails", - "error": "error", - "external-controller": "external_controller", - "macaroon": "macaroon", - "offer": "offer", - } - - def __init__( - self, - consumeofferdetails=None, - error=None, - external_controller=None, - macaroon=None, - offer=None, - **unknown_fields, - ): - """Consumeofferdetails : ConsumeOfferDetails - error : Error - external_controller : ExternalControllerInfo - macaroon : Macaroon - offer : ApplicationOfferDetailsV5 - """ - consumeofferdetails_ = ( - ConsumeOfferDetails.from_json(consumeofferdetails) - if consumeofferdetails - else None - ) - error_ = Error.from_json(error) if error else None - external_controller_ = ( - ExternalControllerInfo.from_json(external_controller) - if external_controller - else None - ) - macaroon_ = Macaroon.from_json(macaroon) if macaroon else None - offer_ = ApplicationOfferDetailsV5.from_json(offer) if offer else None - - # Validate arguments against known Juju API types. - if consumeofferdetails_ is not None and not isinstance( - consumeofferdetails_, (dict, ConsumeOfferDetails) - ): - raise Exception( - f"Expected consumeofferdetails_ to be a ConsumeOfferDetails, received: {type(consumeofferdetails_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if external_controller_ is not None and not isinstance( - external_controller_, (dict, ExternalControllerInfo) - ): - raise Exception( - f"Expected external_controller_ to be a ExternalControllerInfo, received: {type(external_controller_)}" - ) - - if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): - raise Exception( - f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" - ) - - if offer_ is not None and not isinstance( - offer_, (dict, ApplicationOfferDetailsV5) - ): - raise Exception( - f"Expected offer_ to be a ApplicationOfferDetailsV5, received: {type(offer_)}" - ) - - self.consumeofferdetails = consumeofferdetails_ - self.error = error_ - self.external_controller = external_controller_ - self.macaroon = macaroon_ - self.offer = offer_ - self.unknown_fields = unknown_fields - - -class ConsumeOfferDetailsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ConsumeOfferDetailsResult]""" - results_ = [ConsumeOfferDetailsResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ControllerAPIInfoResult(Type): - _toSchema = {"addresses": "addresses", "cacert": "cacert", "error": "error"} - _toPy = {"addresses": "addresses", "cacert": "cacert", "error": "error"} - - def __init__(self, addresses=None, cacert=None, error=None, **unknown_fields): - """Addresses : typing.Sequence[str] - cacert : str - error : Error - """ - addresses_ = addresses - cacert_ = cacert - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if addresses_ is not None and not isinstance(addresses_, (bytes, str, list)): - raise Exception( - f"Expected addresses_ to be a Sequence, received: {type(addresses_)}" - ) - - if cacert_ is not None and not isinstance(cacert_, (bytes, str)): - raise Exception(f"Expected cacert_ to be a str, received: {type(cacert_)}") - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.addresses = addresses_ - self.cacert = cacert_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class ControllerAPIInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ControllerAPIInfoResult]""" - results_ = [ControllerAPIInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ControllerConfigResult(Type): - _toSchema = {"config": "config"} - _toPy = {"config": "config"} - - def __init__(self, config=None, **unknown_fields): - """Config : typing.Mapping[str, typing.Any]""" - config_ = config - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - self.config = config_ - self.unknown_fields = unknown_fields - - -class ControllerConfigSet(Type): - _toSchema = {"config": "config"} - _toPy = {"config": "config"} - - def __init__(self, config=None, **unknown_fields): - """Config : typing.Mapping[str, typing.Any]""" - config_ = config - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - self.config = config_ - self.unknown_fields = unknown_fields - - -class ControllerCredentialInfo(Type): - _toSchema = {"content": "content", "models": "models"} - _toPy = {"content": "content", "models": "models"} - - def __init__(self, content=None, models=None, **unknown_fields): - """Content : CredentialContent - models : typing.Sequence[~ModelAccess] - """ - content_ = CredentialContent.from_json(content) if content else None - models_ = [ModelAccess.from_json(o) for o in models or []] - - # Validate arguments against known Juju API types. - if content_ is not None and not isinstance(content_, (dict, CredentialContent)): - raise Exception( - f"Expected content_ to be a CredentialContent, received: {type(content_)}" - ) - - if models_ is not None and not isinstance(models_, (bytes, str, list)): - raise Exception( - f"Expected models_ to be a Sequence, received: {type(models_)}" - ) - - self.content = content_ - self.models = models_ - self.unknown_fields = unknown_fields - - -class ControllerVersionResults(Type): - _toSchema = {"git_commit": "git-commit", "version": "version"} - _toPy = {"git-commit": "git_commit", "version": "version"} - - def __init__(self, git_commit=None, version=None, **unknown_fields): - """git_commit : str - version : str - """ - git_commit_ = git_commit - version_ = version - - # Validate arguments against known Juju API types. - if git_commit_ is not None and not isinstance(git_commit_, (bytes, str)): - raise Exception( - f"Expected git_commit_ to be a str, received: {type(git_commit_)}" - ) - - if version_ is not None and not isinstance(version_, (bytes, str)): - raise Exception( - f"Expected version_ to be a str, received: {type(version_)}" - ) - - self.git_commit = git_commit_ - self.version = version_ - self.unknown_fields = unknown_fields - - -class ControllersChangeResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : ControllersChanges - """ - error_ = Error.from_json(error) if error else None - result_ = ControllersChanges.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, ControllersChanges)): - raise Exception( - f"Expected result_ to be a ControllersChanges, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class ControllersChangeResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ControllersChangeResult]""" - results_ = [ControllersChangeResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ControllersChanges(Type): - _toSchema = { - "added": "added", - "converted": "converted", - "maintained": "maintained", - "removed": "removed", - } - _toPy = { - "added": "added", - "converted": "converted", - "maintained": "maintained", - "removed": "removed", - } - - def __init__( - self, - added=None, - converted=None, - maintained=None, - removed=None, - **unknown_fields, - ): - """Added : typing.Sequence[str] - converted : typing.Sequence[str] - maintained : typing.Sequence[str] - removed : typing.Sequence[str] - """ - added_ = added - converted_ = converted - maintained_ = maintained - removed_ = removed - - # Validate arguments against known Juju API types. - if added_ is not None and not isinstance(added_, (bytes, str, list)): - raise Exception( - f"Expected added_ to be a Sequence, received: {type(added_)}" - ) - - if converted_ is not None and not isinstance(converted_, (bytes, str, list)): - raise Exception( - f"Expected converted_ to be a Sequence, received: {type(converted_)}" - ) - - if maintained_ is not None and not isinstance(maintained_, (bytes, str, list)): - raise Exception( - f"Expected maintained_ to be a Sequence, received: {type(maintained_)}" - ) - - if removed_ is not None and not isinstance(removed_, (bytes, str, list)): - raise Exception( - f"Expected removed_ to be a Sequence, received: {type(removed_)}" - ) - - self.added = added_ - self.converted = converted_ - self.maintained = maintained_ - self.removed = removed_ - self.unknown_fields = unknown_fields - - -class ControllersSpec(Type): - _toSchema = { - "constraints": "constraints", - "num_controllers": "num-controllers", - "placement": "placement", - } - _toPy = { - "constraints": "constraints", - "num-controllers": "num_controllers", - "placement": "placement", - } - - def __init__( - self, constraints=None, num_controllers=None, placement=None, **unknown_fields - ): - """Constraints : Value - num_controllers : int - placement : typing.Sequence[str] - """ - constraints_ = Value.from_json(constraints) if constraints else None - num_controllers_ = num_controllers - placement_ = placement - - # Validate arguments against known Juju API types. - if constraints_ is not None and not isinstance(constraints_, (dict, Value)): - raise Exception( - f"Expected constraints_ to be a Value, received: {type(constraints_)}" - ) - - if num_controllers_ is not None and not isinstance(num_controllers_, int): - raise Exception( - f"Expected num_controllers_ to be a int, received: {type(num_controllers_)}" - ) - - if placement_ is not None and not isinstance(placement_, (bytes, str, list)): - raise Exception( - f"Expected placement_ to be a Sequence, received: {type(placement_)}" - ) - - self.constraints = constraints_ - self.num_controllers = num_controllers_ - self.placement = placement_ - self.unknown_fields = unknown_fields - - -class ControllersSpecs(Type): - _toSchema = {"specs": "specs"} - _toPy = {"specs": "specs"} - - def __init__(self, specs=None, **unknown_fields): - """Specs : typing.Sequence[~ControllersSpec]""" - specs_ = [ControllersSpec.from_json(o) for o in specs or []] - - # Validate arguments against known Juju API types. - if specs_ is not None and not isinstance(specs_, (bytes, str, list)): - raise Exception( - f"Expected specs_ to be a Sequence, received: {type(specs_)}" - ) - - self.specs = specs_ - self.unknown_fields = unknown_fields - - -class CreateSecretArg(Type): - _toSchema = { - "content": "content", - "description": "description", - "expire_time": "expire-time", - "label": "label", - "owner_tag": "owner-tag", - "params": "params", - "rotate_policy": "rotate-policy", - "upsertsecretarg": "UpsertSecretArg", - "uri": "uri", - } - _toPy = { - "UpsertSecretArg": "upsertsecretarg", - "content": "content", - "description": "description", - "expire-time": "expire_time", - "label": "label", - "owner-tag": "owner_tag", - "params": "params", - "rotate-policy": "rotate_policy", - "uri": "uri", - } - - def __init__( - self, - upsertsecretarg=None, - content=None, - description=None, - expire_time=None, - label=None, - owner_tag=None, - params=None, - rotate_policy=None, - uri=None, - **unknown_fields, - ): - """Upsertsecretarg : UpsertSecretArg - content : SecretContentParams - description : str - expire_time : str - label : str - owner_tag : str - params : typing.Mapping[str, typing.Any] - rotate_policy : str - uri : str - """ - upsertsecretarg_ = ( - UpsertSecretArg.from_json(upsertsecretarg) if upsertsecretarg else None - ) - content_ = SecretContentParams.from_json(content) if content else None - description_ = description - expire_time_ = expire_time - label_ = label - owner_tag_ = owner_tag - params_ = params - rotate_policy_ = rotate_policy - uri_ = uri - - # Validate arguments against known Juju API types. - if upsertsecretarg_ is not None and not isinstance( - upsertsecretarg_, (dict, UpsertSecretArg) - ): - raise Exception( - f"Expected upsertsecretarg_ to be a UpsertSecretArg, received: {type(upsertsecretarg_)}" - ) - - if content_ is not None and not isinstance( - content_, (dict, SecretContentParams) - ): - raise Exception( - f"Expected content_ to be a SecretContentParams, received: {type(content_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if expire_time_ is not None and not isinstance(expire_time_, (bytes, str)): - raise Exception( - f"Expected expire_time_ to be a str, received: {type(expire_time_)}" - ) - - if label_ is not None and not isinstance(label_, (bytes, str)): - raise Exception(f"Expected label_ to be a str, received: {type(label_)}") - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - if params_ is not None and not isinstance(params_, dict): - raise Exception( - f"Expected params_ to be a Mapping, received: {type(params_)}" - ) - - if rotate_policy_ is not None and not isinstance(rotate_policy_, (bytes, str)): - raise Exception( - f"Expected rotate_policy_ to be a str, received: {type(rotate_policy_)}" - ) - - if uri_ is not None and not isinstance(uri_, (bytes, str)): - raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") - - self.upsertsecretarg = upsertsecretarg_ - self.content = content_ - self.description = description_ - self.expire_time = expire_time_ - self.label = label_ - self.owner_tag = owner_tag_ - self.params = params_ - self.rotate_policy = rotate_policy_ - self.uri = uri_ - self.unknown_fields = unknown_fields - - -class CreateSecretArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~CreateSecretArg]""" - args_ = [CreateSecretArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class CreateSpaceParams(Type): - _toSchema = { - "cidrs": "cidrs", - "provider_id": "provider-id", - "public": "public", - "space_tag": "space-tag", - } - _toPy = { - "cidrs": "cidrs", - "provider-id": "provider_id", - "public": "public", - "space-tag": "space_tag", - } - - def __init__( - self, - cidrs=None, - provider_id=None, - public=None, - space_tag=None, - **unknown_fields, - ): - """Cidrs : typing.Sequence[str] - provider_id : str - public : bool - space_tag : str - """ - cidrs_ = cidrs - provider_id_ = provider_id - public_ = public - space_tag_ = space_tag - - # Validate arguments against known Juju API types. - if cidrs_ is not None and not isinstance(cidrs_, (bytes, str, list)): - raise Exception( - f"Expected cidrs_ to be a Sequence, received: {type(cidrs_)}" - ) - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if public_ is not None and not isinstance(public_, bool): - raise Exception(f"Expected public_ to be a bool, received: {type(public_)}") - - if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): - raise Exception( - f"Expected space_tag_ to be a str, received: {type(space_tag_)}" - ) - - self.cidrs = cidrs_ - self.provider_id = provider_id_ - self.public = public_ - self.space_tag = space_tag_ - self.unknown_fields = unknown_fields - - -class CreateSpacesParams(Type): - _toSchema = {"spaces": "spaces"} - _toPy = {"spaces": "spaces"} - - def __init__(self, spaces=None, **unknown_fields): - """Spaces : typing.Sequence[~CreateSpaceParams]""" - spaces_ = [CreateSpaceParams.from_json(o) for o in spaces or []] - - # Validate arguments against known Juju API types. - if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): - raise Exception( - f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" - ) - - self.spaces = spaces_ - self.unknown_fields = unknown_fields - - -class CredentialContent(Type): - _toSchema = { - "attrs": "attrs", - "auth_type": "auth-type", - "cloud": "cloud", - "name": "name", - "valid": "valid", - } - _toPy = { - "attrs": "attrs", - "auth-type": "auth_type", - "cloud": "cloud", - "name": "name", - "valid": "valid", - } - - def __init__( - self, - attrs=None, - auth_type=None, - cloud=None, - name=None, - valid=None, - **unknown_fields, - ): - """Attrs : typing.Mapping[str, str] - auth_type : str - cloud : str - name : str - valid : bool - """ - attrs_ = attrs - auth_type_ = auth_type - cloud_ = cloud - name_ = name - valid_ = valid - - # Validate arguments against known Juju API types. - if attrs_ is not None and not isinstance(attrs_, dict): - raise Exception( - f"Expected attrs_ to be a Mapping, received: {type(attrs_)}" - ) - - if auth_type_ is not None and not isinstance(auth_type_, (bytes, str)): - raise Exception( - f"Expected auth_type_ to be a str, received: {type(auth_type_)}" - ) - - if cloud_ is not None and not isinstance(cloud_, (bytes, str)): - raise Exception(f"Expected cloud_ to be a str, received: {type(cloud_)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if valid_ is not None and not isinstance(valid_, bool): - raise Exception(f"Expected valid_ to be a bool, received: {type(valid_)}") - - self.attrs = attrs_ - self.auth_type = auth_type_ - self.cloud = cloud_ - self.name = name_ - self.valid = valid_ - self.unknown_fields = unknown_fields - - -class CredentialContentResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : ControllerCredentialInfo - """ - error_ = Error.from_json(error) if error else None - result_ = ControllerCredentialInfo.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance( - result_, (dict, ControllerCredentialInfo) - ): - raise Exception( - f"Expected result_ to be a ControllerCredentialInfo, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class CredentialContentResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~CredentialContentResult]""" - results_ = [CredentialContentResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class DashboardConnectionInfo(Type): - _toSchema = { - "error": "error", - "proxy_connection": "proxy-connection", - "ssh_connection": "ssh-connection", - } - _toPy = { - "error": "error", - "proxy-connection": "proxy_connection", - "ssh-connection": "ssh_connection", - } - - def __init__( - self, error=None, proxy_connection=None, ssh_connection=None, **unknown_fields - ): - """Error : Error - proxy_connection : Proxy - ssh_connection : DashboardConnectionSSHTunnel - """ - error_ = Error.from_json(error) if error else None - proxy_connection_ = ( - Proxy.from_json(proxy_connection) if proxy_connection else None - ) - ssh_connection_ = ( - DashboardConnectionSSHTunnel.from_json(ssh_connection) - if ssh_connection - else None - ) - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if proxy_connection_ is not None and not isinstance( - proxy_connection_, (dict, Proxy) - ): - raise Exception( - f"Expected proxy_connection_ to be a Proxy, received: {type(proxy_connection_)}" - ) - - if ssh_connection_ is not None and not isinstance( - ssh_connection_, (dict, DashboardConnectionSSHTunnel) - ): - raise Exception( - f"Expected ssh_connection_ to be a DashboardConnectionSSHTunnel, received: {type(ssh_connection_)}" - ) - - self.error = error_ - self.proxy_connection = proxy_connection_ - self.ssh_connection = ssh_connection_ - self.unknown_fields = unknown_fields - - -class DashboardConnectionSSHTunnel(Type): - _toSchema = {"entity": "entity", "host": "host", "model": "model", "port": "port"} - _toPy = {"entity": "entity", "host": "host", "model": "model", "port": "port"} - - def __init__(self, entity=None, host=None, model=None, port=None, **unknown_fields): - """Entity : str - host : str - model : str - port : str - """ - entity_ = entity - host_ = host - model_ = model - port_ = port - - # Validate arguments against known Juju API types. - if entity_ is not None and not isinstance(entity_, (bytes, str)): - raise Exception(f"Expected entity_ to be a str, received: {type(entity_)}") - - if host_ is not None and not isinstance(host_, (bytes, str)): - raise Exception(f"Expected host_ to be a str, received: {type(host_)}") - - if model_ is not None and not isinstance(model_, (bytes, str)): - raise Exception(f"Expected model_ to be a str, received: {type(model_)}") - - if port_ is not None and not isinstance(port_, (bytes, str)): - raise Exception(f"Expected port_ to be a str, received: {type(port_)}") - - self.entity = entity_ - self.host = host_ - self.model = model_ - self.port = port_ - self.unknown_fields = unknown_fields - - -class DeleteSecretArg(Type): - _toSchema = {"label": "label", "revisions": "revisions", "uri": "uri"} - _toPy = {"label": "label", "revisions": "revisions", "uri": "uri"} - - def __init__(self, label=None, revisions=None, uri=None, **unknown_fields): - """Label : str - revisions : typing.Sequence[int] - uri : str - """ - label_ = label - revisions_ = revisions - uri_ = uri - - # Validate arguments against known Juju API types. - if label_ is not None and not isinstance(label_, (bytes, str)): - raise Exception(f"Expected label_ to be a str, received: {type(label_)}") - - if revisions_ is not None and not isinstance(revisions_, (bytes, str, list)): - raise Exception( - f"Expected revisions_ to be a Sequence, received: {type(revisions_)}" - ) - - if uri_ is not None and not isinstance(uri_, (bytes, str)): - raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") - - self.label = label_ - self.revisions = revisions_ - self.uri = uri_ - self.unknown_fields = unknown_fields - - -class DeleteSecretArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~DeleteSecretArg]""" - args_ = [DeleteSecretArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class Delta(Type): - _toSchema = {"entity": "entity", "removed": "removed"} - _toPy = {"entity": "entity", "removed": "removed"} - - def __init__(self, entity=None, removed=None, **unknown_fields): - """Entity : Any - removed : bool - """ - entity_ = entity - removed_ = removed - - # Validate arguments against known Juju API types. - if removed_ is not None and not isinstance(removed_, bool): - raise Exception( - f"Expected removed_ to be a bool, received: {type(removed_)}" - ) - - self.entity = entity_ - self.removed = removed_ - self.unknown_fields = unknown_fields - - -class DeployFromRepositoryArg(Type): - _toSchema = { - "applicationname": "ApplicationName", - "attachstorage": "AttachStorage", - "base": "base", - "channel": "channel", - "charmname": "CharmName", - "configyaml": "ConfigYAML", - "cons": "Cons", - "devices": "Devices", - "dryrun": "DryRun", - "endpoint_bindings": "endpoint-bindings", - "force": "force", - "num_units": "num-units", - "placement": "Placement", - "resources": "resources", - "revision": "revision", - "storage": "Storage", - "trust": "Trust", - } - _toPy = { - "ApplicationName": "applicationname", - "AttachStorage": "attachstorage", - "CharmName": "charmname", - "ConfigYAML": "configyaml", - "Cons": "cons", - "Devices": "devices", - "DryRun": "dryrun", - "Placement": "placement", - "Storage": "storage", - "Trust": "trust", - "base": "base", - "channel": "channel", - "endpoint-bindings": "endpoint_bindings", - "force": "force", - "num-units": "num_units", - "resources": "resources", - "revision": "revision", - } - - def __init__( - self, - applicationname=None, - attachstorage=None, - charmname=None, - configyaml=None, - cons=None, - devices=None, - dryrun=None, - placement=None, - storage=None, - trust=None, - base=None, - channel=None, - endpoint_bindings=None, - force=None, - num_units=None, - resources=None, - revision=None, - **unknown_fields, - ): - """Applicationname : str - attachstorage : typing.Sequence[str] - charmname : str - configyaml : str - cons : Value - devices : typing.Mapping[str, ~Constraints] - dryrun : bool - placement : typing.Sequence[~Placement] - storage : typing.Mapping[str, ~Constraints] - trust : bool - base : Base - channel : str - endpoint_bindings : typing.Mapping[str, str] - force : bool - num_units : int - resources : typing.Mapping[str, str] - revision : int - """ - applicationname_ = applicationname - attachstorage_ = attachstorage - charmname_ = charmname - configyaml_ = configyaml - cons_ = Value.from_json(cons) if cons else None - devices_ = {k: Constraints.from_json(v) for k, v in (devices or dict()).items()} - dryrun_ = dryrun - placement_ = [Placement.from_json(o) for o in placement or []] - storage_ = {k: Constraints.from_json(v) for k, v in (storage or dict()).items()} - trust_ = trust - base_ = Base.from_json(base) if base else None - channel_ = channel - endpoint_bindings_ = endpoint_bindings - force_ = force - num_units_ = num_units - resources_ = resources - revision_ = revision - - # Validate arguments against known Juju API types. - if applicationname_ is not None and not isinstance( - applicationname_, (bytes, str) - ): - raise Exception( - f"Expected applicationname_ to be a str, received: {type(applicationname_)}" - ) - - if attachstorage_ is not None and not isinstance( - attachstorage_, (bytes, str, list) - ): - raise Exception( - f"Expected attachstorage_ to be a Sequence, received: {type(attachstorage_)}" - ) - - if charmname_ is not None and not isinstance(charmname_, (bytes, str)): - raise Exception( - f"Expected charmname_ to be a str, received: {type(charmname_)}" - ) - - if configyaml_ is not None and not isinstance(configyaml_, (bytes, str)): - raise Exception( - f"Expected configyaml_ to be a str, received: {type(configyaml_)}" - ) - - if cons_ is not None and not isinstance(cons_, (dict, Value)): - raise Exception(f"Expected cons_ to be a Value, received: {type(cons_)}") - - if devices_ is not None and not isinstance(devices_, dict): - raise Exception( - f"Expected devices_ to be a Mapping, received: {type(devices_)}" - ) - - if dryrun_ is not None and not isinstance(dryrun_, bool): - raise Exception(f"Expected dryrun_ to be a bool, received: {type(dryrun_)}") - - if placement_ is not None and not isinstance(placement_, (bytes, str, list)): - raise Exception( - f"Expected placement_ to be a Sequence, received: {type(placement_)}" - ) - - if storage_ is not None and not isinstance(storage_, dict): - raise Exception( - f"Expected storage_ to be a Mapping, received: {type(storage_)}" - ) - - if trust_ is not None and not isinstance(trust_, bool): - raise Exception(f"Expected trust_ to be a bool, received: {type(trust_)}") - - if base_ is not None and not isinstance(base_, (dict, Base)): - raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") - - if channel_ is not None and not isinstance(channel_, (bytes, str)): - raise Exception( - f"Expected channel_ to be a str, received: {type(channel_)}" - ) - - if endpoint_bindings_ is not None and not isinstance(endpoint_bindings_, dict): - raise Exception( - f"Expected endpoint_bindings_ to be a Mapping, received: {type(endpoint_bindings_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if num_units_ is not None and not isinstance(num_units_, int): - raise Exception( - f"Expected num_units_ to be a int, received: {type(num_units_)}" - ) - - if resources_ is not None and not isinstance(resources_, dict): - raise Exception( - f"Expected resources_ to be a Mapping, received: {type(resources_)}" - ) - - if revision_ is not None and not isinstance(revision_, int): - raise Exception( - f"Expected revision_ to be a int, received: {type(revision_)}" - ) - - self.applicationname = applicationname_ - self.attachstorage = attachstorage_ - self.charmname = charmname_ - self.configyaml = configyaml_ - self.cons = cons_ - self.devices = devices_ - self.dryrun = dryrun_ - self.placement = placement_ - self.storage = storage_ - self.trust = trust_ - self.base = base_ - self.channel = channel_ - self.endpoint_bindings = endpoint_bindings_ - self.force = force_ - self.num_units = num_units_ - self.resources = resources_ - self.revision = revision_ - self.unknown_fields = unknown_fields - - -class DeployFromRepositoryArgs(Type): - _toSchema = {"args": "Args"} - _toPy = {"Args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~DeployFromRepositoryArg]""" - args_ = [DeployFromRepositoryArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class DeployFromRepositoryInfo(Type): - _toSchema = { - "architecture": "architecture", - "base": "base", - "channel": "channel", - "effective_channel": "effective-channel", - "name": "name", - "revision": "revision", - } - _toPy = { - "architecture": "architecture", - "base": "base", - "channel": "channel", - "effective-channel": "effective_channel", - "name": "name", - "revision": "revision", - } - - def __init__( - self, - architecture=None, - base=None, - channel=None, - effective_channel=None, - name=None, - revision=None, - **unknown_fields, - ): - """Architecture : str - base : Base - channel : str - effective_channel : str - name : str - revision : int - """ - architecture_ = architecture - base_ = Base.from_json(base) if base else None - channel_ = channel - effective_channel_ = effective_channel - name_ = name - revision_ = revision - - # Validate arguments against known Juju API types. - if architecture_ is not None and not isinstance(architecture_, (bytes, str)): - raise Exception( - f"Expected architecture_ to be a str, received: {type(architecture_)}" - ) - - if base_ is not None and not isinstance(base_, (dict, Base)): - raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") - - if channel_ is not None and not isinstance(channel_, (bytes, str)): - raise Exception( - f"Expected channel_ to be a str, received: {type(channel_)}" - ) - - if effective_channel_ is not None and not isinstance( - effective_channel_, (bytes, str) - ): - raise Exception( - f"Expected effective_channel_ to be a str, received: {type(effective_channel_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if revision_ is not None and not isinstance(revision_, int): - raise Exception( - f"Expected revision_ to be a int, received: {type(revision_)}" - ) - - self.architecture = architecture_ - self.base = base_ - self.channel = channel_ - self.effective_channel = effective_channel_ - self.name = name_ - self.revision = revision_ - self.unknown_fields = unknown_fields - - -class DeployFromRepositoryResult(Type): - _toSchema = { - "errors": "Errors", - "info": "Info", - "pendingresourceuploads": "PendingResourceUploads", - } - _toPy = { - "Errors": "errors", - "Info": "info", - "PendingResourceUploads": "pendingresourceuploads", - } - - def __init__( - self, errors=None, info=None, pendingresourceuploads=None, **unknown_fields - ): - """Errors : typing.Sequence[~Error] - info : DeployFromRepositoryInfo - pendingresourceuploads : typing.Sequence[~PendingResourceUpload] - """ - errors_ = [Error.from_json(o) for o in errors or []] - info_ = DeployFromRepositoryInfo.from_json(info) if info else None - pendingresourceuploads_ = [ - PendingResourceUpload.from_json(o) for o in pendingresourceuploads or [] - ] - - # Validate arguments against known Juju API types. - if errors_ is not None and not isinstance(errors_, (bytes, str, list)): - raise Exception( - f"Expected errors_ to be a Sequence, received: {type(errors_)}" - ) - - if info_ is not None and not isinstance( - info_, (dict, DeployFromRepositoryInfo) - ): - raise Exception( - f"Expected info_ to be a DeployFromRepositoryInfo, received: {type(info_)}" - ) - - if pendingresourceuploads_ is not None and not isinstance( - pendingresourceuploads_, (bytes, str, list) - ): - raise Exception( - f"Expected pendingresourceuploads_ to be a Sequence, received: {type(pendingresourceuploads_)}" - ) - - self.errors = errors_ - self.info = info_ - self.pendingresourceuploads = pendingresourceuploads_ - self.unknown_fields = unknown_fields - - -class DeployFromRepositoryResults(Type): - _toSchema = {"results": "Results"} - _toPy = {"Results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~DeployFromRepositoryResult]""" - results_ = [DeployFromRepositoryResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class DestroyApplicationInfo(Type): - _toSchema = { - "destroyed_storage": "destroyed-storage", - "destroyed_units": "destroyed-units", - "detached_storage": "detached-storage", - } - _toPy = { - "destroyed-storage": "destroyed_storage", - "destroyed-units": "destroyed_units", - "detached-storage": "detached_storage", - } - - def __init__( - self, - destroyed_storage=None, - destroyed_units=None, - detached_storage=None, - **unknown_fields, - ): - """destroyed_storage : typing.Sequence[~Entity] - destroyed_units : typing.Sequence[~Entity] - detached_storage : typing.Sequence[~Entity] - """ - destroyed_storage_ = [Entity.from_json(o) for o in destroyed_storage or []] - destroyed_units_ = [Entity.from_json(o) for o in destroyed_units or []] - detached_storage_ = [Entity.from_json(o) for o in detached_storage or []] - - # Validate arguments against known Juju API types. - if destroyed_storage_ is not None and not isinstance( - destroyed_storage_, (bytes, str, list) - ): - raise Exception( - f"Expected destroyed_storage_ to be a Sequence, received: {type(destroyed_storage_)}" - ) - - if destroyed_units_ is not None and not isinstance( - destroyed_units_, (bytes, str, list) - ): - raise Exception( - f"Expected destroyed_units_ to be a Sequence, received: {type(destroyed_units_)}" - ) - - if detached_storage_ is not None and not isinstance( - detached_storage_, (bytes, str, list) - ): - raise Exception( - f"Expected detached_storage_ to be a Sequence, received: {type(detached_storage_)}" - ) - - self.destroyed_storage = destroyed_storage_ - self.destroyed_units = destroyed_units_ - self.detached_storage = detached_storage_ - self.unknown_fields = unknown_fields - - -class DestroyApplicationOffers(Type): - _toSchema = {"force": "force", "offer_urls": "offer-urls"} - _toPy = {"force": "force", "offer-urls": "offer_urls"} - - def __init__(self, force=None, offer_urls=None, **unknown_fields): - """Force : bool - offer_urls : typing.Sequence[str] - """ - force_ = force - offer_urls_ = offer_urls - - # Validate arguments against known Juju API types. - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if offer_urls_ is not None and not isinstance(offer_urls_, (bytes, str, list)): - raise Exception( - f"Expected offer_urls_ to be a Sequence, received: {type(offer_urls_)}" - ) - - self.force = force_ - self.offer_urls = offer_urls_ - self.unknown_fields = unknown_fields - - -class DestroyApplicationParams(Type): - _toSchema = { - "application_tag": "application-tag", - "destroy_storage": "destroy-storage", - "dry_run": "dry-run", - "force": "force", - "max_wait": "max-wait", - } - _toPy = { - "application-tag": "application_tag", - "destroy-storage": "destroy_storage", - "dry-run": "dry_run", - "force": "force", - "max-wait": "max_wait", - } - - def __init__( - self, - application_tag=None, - destroy_storage=None, - dry_run=None, - force=None, - max_wait=None, - **unknown_fields, - ): - """application_tag : str - destroy_storage : bool - dry_run : bool - force : bool - max_wait : int - """ - application_tag_ = application_tag - destroy_storage_ = destroy_storage - dry_run_ = dry_run - force_ = force - max_wait_ = max_wait - - # Validate arguments against known Juju API types. - if application_tag_ is not None and not isinstance( - application_tag_, (bytes, str) - ): - raise Exception( - f"Expected application_tag_ to be a str, received: {type(application_tag_)}" - ) - - if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): - raise Exception( - f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" - ) - - if dry_run_ is not None and not isinstance(dry_run_, bool): - raise Exception( - f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if max_wait_ is not None and not isinstance(max_wait_, int): - raise Exception( - f"Expected max_wait_ to be a int, received: {type(max_wait_)}" - ) - - self.application_tag = application_tag_ - self.destroy_storage = destroy_storage_ - self.dry_run = dry_run_ - self.force = force_ - self.max_wait = max_wait_ - self.unknown_fields = unknown_fields - - -class DestroyApplicationResult(Type): - _toSchema = {"error": "error", "info": "info"} - _toPy = {"error": "error", "info": "info"} - - def __init__(self, error=None, info=None, **unknown_fields): - """Error : Error - info : DestroyApplicationInfo - """ - error_ = Error.from_json(error) if error else None - info_ = DestroyApplicationInfo.from_json(info) if info else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if info_ is not None and not isinstance(info_, (dict, DestroyApplicationInfo)): - raise Exception( - f"Expected info_ to be a DestroyApplicationInfo, received: {type(info_)}" - ) - - self.error = error_ - self.info = info_ - self.unknown_fields = unknown_fields - - -class DestroyApplicationResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~DestroyApplicationResult]""" - results_ = [DestroyApplicationResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class DestroyApplicationsParams(Type): - _toSchema = {"applications": "applications"} - _toPy = {"applications": "applications"} - - def __init__(self, applications=None, **unknown_fields): - """Applications : typing.Sequence[~DestroyApplicationParams]""" - applications_ = [ - DestroyApplicationParams.from_json(o) for o in applications or [] - ] - - # Validate arguments against known Juju API types. - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - self.applications = applications_ - self.unknown_fields = unknown_fields - - -class DestroyConsumedApplicationParams(Type): - _toSchema = { - "application_tag": "application-tag", - "force": "force", - "max_wait": "max-wait", - } - _toPy = { - "application-tag": "application_tag", - "force": "force", - "max-wait": "max_wait", - } - - def __init__( - self, application_tag=None, force=None, max_wait=None, **unknown_fields - ): - """application_tag : str - force : bool - max_wait : int - """ - application_tag_ = application_tag - force_ = force - max_wait_ = max_wait - - # Validate arguments against known Juju API types. - if application_tag_ is not None and not isinstance( - application_tag_, (bytes, str) - ): - raise Exception( - f"Expected application_tag_ to be a str, received: {type(application_tag_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if max_wait_ is not None and not isinstance(max_wait_, int): - raise Exception( - f"Expected max_wait_ to be a int, received: {type(max_wait_)}" - ) - - self.application_tag = application_tag_ - self.force = force_ - self.max_wait = max_wait_ - self.unknown_fields = unknown_fields - - -class DestroyConsumedApplicationsParams(Type): - _toSchema = {"applications": "applications"} - _toPy = {"applications": "applications"} - - def __init__(self, applications=None, **unknown_fields): - """Applications : typing.Sequence[~DestroyConsumedApplicationParams]""" - applications_ = [ - DestroyConsumedApplicationParams.from_json(o) for o in applications or [] - ] - - # Validate arguments against known Juju API types. - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - self.applications = applications_ - self.unknown_fields = unknown_fields - - -class DestroyControllerArgs(Type): - _toSchema = { - "destroy_models": "destroy-models", - "destroy_storage": "destroy-storage", - "force": "force", - "max_wait": "max-wait", - "model_timeout": "model-timeout", - } - _toPy = { - "destroy-models": "destroy_models", - "destroy-storage": "destroy_storage", - "force": "force", - "max-wait": "max_wait", - "model-timeout": "model_timeout", - } - - def __init__( - self, - destroy_models=None, - destroy_storage=None, - force=None, - max_wait=None, - model_timeout=None, - **unknown_fields, - ): - """destroy_models : bool - destroy_storage : bool - force : bool - max_wait : int - model_timeout : int - """ - destroy_models_ = destroy_models - destroy_storage_ = destroy_storage - force_ = force - max_wait_ = max_wait - model_timeout_ = model_timeout - - # Validate arguments against known Juju API types. - if destroy_models_ is not None and not isinstance(destroy_models_, bool): - raise Exception( - f"Expected destroy_models_ to be a bool, received: {type(destroy_models_)}" - ) - - if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): - raise Exception( - f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if max_wait_ is not None and not isinstance(max_wait_, int): - raise Exception( - f"Expected max_wait_ to be a int, received: {type(max_wait_)}" - ) - - if model_timeout_ is not None and not isinstance(model_timeout_, int): - raise Exception( - f"Expected model_timeout_ to be a int, received: {type(model_timeout_)}" - ) - - self.destroy_models = destroy_models_ - self.destroy_storage = destroy_storage_ - self.force = force_ - self.max_wait = max_wait_ - self.model_timeout = model_timeout_ - self.unknown_fields = unknown_fields - - -class DestroyMachineInfo(Type): - _toSchema = { - "destroyed_containers": "destroyed-containers", - "destroyed_storage": "destroyed-storage", - "destroyed_units": "destroyed-units", - "detached_storage": "detached-storage", - "machine_id": "machine-id", - } - _toPy = { - "destroyed-containers": "destroyed_containers", - "destroyed-storage": "destroyed_storage", - "destroyed-units": "destroyed_units", - "detached-storage": "detached_storage", - "machine-id": "machine_id", - } - - def __init__( - self, - destroyed_containers=None, - destroyed_storage=None, - destroyed_units=None, - detached_storage=None, - machine_id=None, - **unknown_fields, - ): - """destroyed_containers : typing.Sequence[~DestroyMachineResult] - destroyed_storage : typing.Sequence[~Entity] - destroyed_units : typing.Sequence[~Entity] - detached_storage : typing.Sequence[~Entity] - machine_id : str - """ - destroyed_containers_ = [ - DestroyMachineResult.from_json(o) for o in destroyed_containers or [] - ] - destroyed_storage_ = [Entity.from_json(o) for o in destroyed_storage or []] - destroyed_units_ = [Entity.from_json(o) for o in destroyed_units or []] - detached_storage_ = [Entity.from_json(o) for o in detached_storage or []] - machine_id_ = machine_id - - # Validate arguments against known Juju API types. - if destroyed_containers_ is not None and not isinstance( - destroyed_containers_, (bytes, str, list) - ): - raise Exception( - f"Expected destroyed_containers_ to be a Sequence, received: {type(destroyed_containers_)}" - ) - - if destroyed_storage_ is not None and not isinstance( - destroyed_storage_, (bytes, str, list) - ): - raise Exception( - f"Expected destroyed_storage_ to be a Sequence, received: {type(destroyed_storage_)}" - ) - - if destroyed_units_ is not None and not isinstance( - destroyed_units_, (bytes, str, list) - ): - raise Exception( - f"Expected destroyed_units_ to be a Sequence, received: {type(destroyed_units_)}" - ) - - if detached_storage_ is not None and not isinstance( - detached_storage_, (bytes, str, list) - ): - raise Exception( - f"Expected detached_storage_ to be a Sequence, received: {type(detached_storage_)}" - ) - - if machine_id_ is not None and not isinstance(machine_id_, (bytes, str)): - raise Exception( - f"Expected machine_id_ to be a str, received: {type(machine_id_)}" - ) - - self.destroyed_containers = destroyed_containers_ - self.destroyed_storage = destroyed_storage_ - self.destroyed_units = destroyed_units_ - self.detached_storage = detached_storage_ - self.machine_id = machine_id_ - self.unknown_fields = unknown_fields - - -class DestroyMachineResult(Type): - _toSchema = {"error": "error", "info": "info"} - _toPy = {"error": "error", "info": "info"} - - def __init__(self, error=None, info=None, **unknown_fields): - """Error : Error - info : DestroyMachineInfo - """ - error_ = Error.from_json(error) if error else None - info_ = DestroyMachineInfo.from_json(info) if info else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if info_ is not None and not isinstance(info_, (dict, DestroyMachineInfo)): - raise Exception( - f"Expected info_ to be a DestroyMachineInfo, received: {type(info_)}" - ) - - self.error = error_ - self.info = info_ - self.unknown_fields = unknown_fields - - -class DestroyMachineResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~DestroyMachineResult]""" - results_ = [DestroyMachineResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class DestroyMachinesParams(Type): - _toSchema = { - "dry_run": "dry-run", - "force": "force", - "keep": "keep", - "machine_tags": "machine-tags", - "max_wait": "max-wait", - } - _toPy = { - "dry-run": "dry_run", - "force": "force", - "keep": "keep", - "machine-tags": "machine_tags", - "max-wait": "max_wait", - } - - def __init__( - self, - dry_run=None, - force=None, - keep=None, - machine_tags=None, - max_wait=None, - **unknown_fields, - ): - """dry_run : bool - force : bool - keep : bool - machine_tags : typing.Sequence[str] - max_wait : int - """ - dry_run_ = dry_run - force_ = force - keep_ = keep - machine_tags_ = machine_tags - max_wait_ = max_wait - - # Validate arguments against known Juju API types. - if dry_run_ is not None and not isinstance(dry_run_, bool): - raise Exception( - f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if keep_ is not None and not isinstance(keep_, bool): - raise Exception(f"Expected keep_ to be a bool, received: {type(keep_)}") - - if machine_tags_ is not None and not isinstance( - machine_tags_, (bytes, str, list) - ): - raise Exception( - f"Expected machine_tags_ to be a Sequence, received: {type(machine_tags_)}" - ) - - if max_wait_ is not None and not isinstance(max_wait_, int): - raise Exception( - f"Expected max_wait_ to be a int, received: {type(max_wait_)}" - ) - - self.dry_run = dry_run_ - self.force = force_ - self.keep = keep_ - self.machine_tags = machine_tags_ - self.max_wait = max_wait_ - self.unknown_fields = unknown_fields - - -class DestroyModelParams(Type): - _toSchema = { - "destroy_storage": "destroy-storage", - "force": "force", - "max_wait": "max-wait", - "model_tag": "model-tag", - "timeout": "timeout", - } - _toPy = { - "destroy-storage": "destroy_storage", - "force": "force", - "max-wait": "max_wait", - "model-tag": "model_tag", - "timeout": "timeout", - } - - def __init__( - self, - destroy_storage=None, - force=None, - max_wait=None, - model_tag=None, - timeout=None, - **unknown_fields, - ): - """destroy_storage : bool - force : bool - max_wait : int - model_tag : str - timeout : int - """ - destroy_storage_ = destroy_storage - force_ = force - max_wait_ = max_wait - model_tag_ = model_tag - timeout_ = timeout - - # Validate arguments against known Juju API types. - if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): - raise Exception( - f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if max_wait_ is not None and not isinstance(max_wait_, int): - raise Exception( - f"Expected max_wait_ to be a int, received: {type(max_wait_)}" - ) - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - if timeout_ is not None and not isinstance(timeout_, int): - raise Exception( - f"Expected timeout_ to be a int, received: {type(timeout_)}" - ) - - self.destroy_storage = destroy_storage_ - self.force = force_ - self.max_wait = max_wait_ - self.model_tag = model_tag_ - self.timeout = timeout_ - self.unknown_fields = unknown_fields - - -class DestroyModelsParams(Type): - _toSchema = {"models": "models"} - _toPy = {"models": "models"} - - def __init__(self, models=None, **unknown_fields): - """Models : typing.Sequence[~DestroyModelParams]""" - models_ = [DestroyModelParams.from_json(o) for o in models or []] - - # Validate arguments against known Juju API types. - if models_ is not None and not isinstance(models_, (bytes, str, list)): - raise Exception( - f"Expected models_ to be a Sequence, received: {type(models_)}" - ) - - self.models = models_ - self.unknown_fields = unknown_fields - - -class DestroyRelation(Type): - _toSchema = { - "endpoints": "endpoints", - "force": "force", - "max_wait": "max-wait", - "relation_id": "relation-id", - } - _toPy = { - "endpoints": "endpoints", - "force": "force", - "max-wait": "max_wait", - "relation-id": "relation_id", - } - - def __init__( - self, - endpoints=None, - force=None, - max_wait=None, - relation_id=None, - **unknown_fields, - ): - """Endpoints : typing.Sequence[str] - force : bool - max_wait : int - relation_id : int - """ - endpoints_ = endpoints - force_ = force - max_wait_ = max_wait - relation_id_ = relation_id - - # Validate arguments against known Juju API types. - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if max_wait_ is not None and not isinstance(max_wait_, int): - raise Exception( - f"Expected max_wait_ to be a int, received: {type(max_wait_)}" - ) - - if relation_id_ is not None and not isinstance(relation_id_, int): - raise Exception( - f"Expected relation_id_ to be a int, received: {type(relation_id_)}" - ) - - self.endpoints = endpoints_ - self.force = force_ - self.max_wait = max_wait_ - self.relation_id = relation_id_ - self.unknown_fields = unknown_fields - - -class DestroyUnitInfo(Type): - _toSchema = { - "destroyed_storage": "destroyed-storage", - "detached_storage": "detached-storage", - } - _toPy = { - "destroyed-storage": "destroyed_storage", - "detached-storage": "detached_storage", - } - - def __init__(self, destroyed_storage=None, detached_storage=None, **unknown_fields): - """destroyed_storage : typing.Sequence[~Entity] - detached_storage : typing.Sequence[~Entity] - """ - destroyed_storage_ = [Entity.from_json(o) for o in destroyed_storage or []] - detached_storage_ = [Entity.from_json(o) for o in detached_storage or []] - - # Validate arguments against known Juju API types. - if destroyed_storage_ is not None and not isinstance( - destroyed_storage_, (bytes, str, list) - ): - raise Exception( - f"Expected destroyed_storage_ to be a Sequence, received: {type(destroyed_storage_)}" - ) - - if detached_storage_ is not None and not isinstance( - detached_storage_, (bytes, str, list) - ): - raise Exception( - f"Expected detached_storage_ to be a Sequence, received: {type(detached_storage_)}" - ) - - self.destroyed_storage = destroyed_storage_ - self.detached_storage = detached_storage_ - self.unknown_fields = unknown_fields - - -class DestroyUnitParams(Type): - _toSchema = { - "destroy_storage": "destroy-storage", - "dry_run": "dry-run", - "force": "force", - "max_wait": "max-wait", - "unit_tag": "unit-tag", - } - _toPy = { - "destroy-storage": "destroy_storage", - "dry-run": "dry_run", - "force": "force", - "max-wait": "max_wait", - "unit-tag": "unit_tag", - } - - def __init__( - self, - destroy_storage=None, - dry_run=None, - force=None, - max_wait=None, - unit_tag=None, - **unknown_fields, - ): - """destroy_storage : bool - dry_run : bool - force : bool - max_wait : int - unit_tag : str - """ - destroy_storage_ = destroy_storage - dry_run_ = dry_run - force_ = force - max_wait_ = max_wait - unit_tag_ = unit_tag - - # Validate arguments against known Juju API types. - if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): - raise Exception( - f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" - ) - - if dry_run_ is not None and not isinstance(dry_run_, bool): - raise Exception( - f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if max_wait_ is not None and not isinstance(max_wait_, int): - raise Exception( - f"Expected max_wait_ to be a int, received: {type(max_wait_)}" - ) - - if unit_tag_ is not None and not isinstance(unit_tag_, (bytes, str)): - raise Exception( - f"Expected unit_tag_ to be a str, received: {type(unit_tag_)}" - ) - - self.destroy_storage = destroy_storage_ - self.dry_run = dry_run_ - self.force = force_ - self.max_wait = max_wait_ - self.unit_tag = unit_tag_ - self.unknown_fields = unknown_fields - - -class DestroyUnitResult(Type): - _toSchema = {"error": "error", "info": "info"} - _toPy = {"error": "error", "info": "info"} - - def __init__(self, error=None, info=None, **unknown_fields): - """Error : Error - info : DestroyUnitInfo - """ - error_ = Error.from_json(error) if error else None - info_ = DestroyUnitInfo.from_json(info) if info else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if info_ is not None and not isinstance(info_, (dict, DestroyUnitInfo)): - raise Exception( - f"Expected info_ to be a DestroyUnitInfo, received: {type(info_)}" - ) - - self.error = error_ - self.info = info_ - self.unknown_fields = unknown_fields - - -class DestroyUnitResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~DestroyUnitResult]""" - results_ = [DestroyUnitResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class DestroyUnitsParams(Type): - _toSchema = {"units": "units"} - _toPy = {"units": "units"} - - def __init__(self, units=None, **unknown_fields): - """Units : typing.Sequence[~DestroyUnitParams]""" - units_ = [DestroyUnitParams.from_json(o) for o in units or []] - - # Validate arguments against known Juju API types. - if units_ is not None and not isinstance(units_, (bytes, str, list)): - raise Exception( - f"Expected units_ to be a Sequence, received: {type(units_)}" - ) - - self.units = units_ - self.unknown_fields = unknown_fields - - -class DetailedStatus(Type): - _toSchema = { - "data": "data", - "err": "err", - "info": "info", - "kind": "kind", - "life": "life", - "since": "since", - "status": "status", - "version": "version", - } - _toPy = { - "data": "data", - "err": "err", - "info": "info", - "kind": "kind", - "life": "life", - "since": "since", - "status": "status", - "version": "version", - } - - def __init__( - self, - data=None, - err=None, - info=None, - kind=None, - life=None, - since=None, - status=None, - version=None, - **unknown_fields, - ): - """Data : typing.Mapping[str, typing.Any] - err : Error - info : str - kind : str - life : str - since : str - status : str - version : str - """ - data_ = data - err_ = Error.from_json(err) if err else None - info_ = info - kind_ = kind - life_ = life - since_ = since - status_ = status - version_ = version - - # Validate arguments against known Juju API types. - if data_ is not None and not isinstance(data_, dict): - raise Exception(f"Expected data_ to be a Mapping, received: {type(data_)}") - - if err_ is not None and not isinstance(err_, (dict, Error)): - raise Exception(f"Expected err_ to be a Error, received: {type(err_)}") - - if info_ is not None and not isinstance(info_, (bytes, str)): - raise Exception(f"Expected info_ to be a str, received: {type(info_)}") - - if kind_ is not None and not isinstance(kind_, (bytes, str)): - raise Exception(f"Expected kind_ to be a str, received: {type(kind_)}") - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if since_ is not None and not isinstance(since_, (bytes, str)): - raise Exception(f"Expected since_ to be a str, received: {type(since_)}") - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - if version_ is not None and not isinstance(version_, (bytes, str)): - raise Exception( - f"Expected version_ to be a str, received: {type(version_)}" - ) - - self.data = data_ - self.err = err_ - self.info = info_ - self.kind = kind_ - self.life = life_ - self.since = since_ - self.status = status_ - self.version = version_ - self.unknown_fields = unknown_fields - - -class DownloadInfoResult(Type): - _toSchema = {"charm_origin": "charm-origin", "url": "url"} - _toPy = {"charm-origin": "charm_origin", "url": "url"} - - def __init__(self, charm_origin=None, url=None, **unknown_fields): - """charm_origin : CharmOrigin - url : str - """ - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - url_ = url - - # Validate arguments against known Juju API types. - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if url_ is not None and not isinstance(url_, (bytes, str)): - raise Exception(f"Expected url_ to be a str, received: {type(url_)}") - - self.charm_origin = charm_origin_ - self.url = url_ - self.unknown_fields = unknown_fields - - -class DownloadInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~DownloadInfoResult]""" - results_ = [DownloadInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class DumpModelRequest(Type): - _toSchema = {"entities": "entities", "simplified": "simplified"} - _toPy = {"entities": "entities", "simplified": "simplified"} - - def __init__(self, entities=None, simplified=None, **unknown_fields): - """Entities : typing.Sequence[~Entity] - simplified : bool - """ - entities_ = [Entity.from_json(o) for o in entities or []] - simplified_ = simplified - - # Validate arguments against known Juju API types. - if entities_ is not None and not isinstance(entities_, (bytes, str, list)): - raise Exception( - f"Expected entities_ to be a Sequence, received: {type(entities_)}" - ) - - if simplified_ is not None and not isinstance(simplified_, bool): - raise Exception( - f"Expected simplified_ to be a bool, received: {type(simplified_)}" - ) - - self.entities = entities_ - self.simplified = simplified_ - self.unknown_fields = unknown_fields - - -class EndpointFilterAttributes(Type): - _toSchema = {"interface": "interface", "name": "name", "role": "role"} - _toPy = {"interface": "interface", "name": "name", "role": "role"} - - def __init__(self, interface=None, name=None, role=None, **unknown_fields): - """Interface : str - name : str - role : str - """ - interface_ = interface - name_ = name - role_ = role - - # Validate arguments against known Juju API types. - if interface_ is not None and not isinstance(interface_, (bytes, str)): - raise Exception( - f"Expected interface_ to be a str, received: {type(interface_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if role_ is not None and not isinstance(role_, (bytes, str)): - raise Exception(f"Expected role_ to be a str, received: {type(role_)}") - - self.interface = interface_ - self.name = name_ - self.role = role_ - self.unknown_fields = unknown_fields - - -class EndpointRelationData(Type): - _toSchema = { - "applicationdata": "ApplicationData", - "cross_model": "cross-model", - "endpoint": "endpoint", - "related_endpoint": "related-endpoint", - "relation_id": "relation-id", - "unit_relation_data": "unit-relation-data", - } - _toPy = { - "ApplicationData": "applicationdata", - "cross-model": "cross_model", - "endpoint": "endpoint", - "related-endpoint": "related_endpoint", - "relation-id": "relation_id", - "unit-relation-data": "unit_relation_data", - } - - def __init__( - self, - applicationdata=None, - cross_model=None, - endpoint=None, - related_endpoint=None, - relation_id=None, - unit_relation_data=None, - **unknown_fields, - ): - """Applicationdata : typing.Mapping[str, typing.Any] - cross_model : bool - endpoint : str - related_endpoint : str - relation_id : int - unit_relation_data : typing.Mapping[str, ~RelationData] - """ - applicationdata_ = applicationdata - cross_model_ = cross_model - endpoint_ = endpoint - related_endpoint_ = related_endpoint - relation_id_ = relation_id - unit_relation_data_ = { - k: RelationData.from_json(v) - for k, v in (unit_relation_data or dict()).items() - } - - # Validate arguments against known Juju API types. - if applicationdata_ is not None and not isinstance(applicationdata_, dict): - raise Exception( - f"Expected applicationdata_ to be a Mapping, received: {type(applicationdata_)}" - ) - - if cross_model_ is not None and not isinstance(cross_model_, bool): - raise Exception( - f"Expected cross_model_ to be a bool, received: {type(cross_model_)}" - ) - - if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): - raise Exception( - f"Expected endpoint_ to be a str, received: {type(endpoint_)}" - ) - - if related_endpoint_ is not None and not isinstance( - related_endpoint_, (bytes, str) - ): - raise Exception( - f"Expected related_endpoint_ to be a str, received: {type(related_endpoint_)}" - ) - - if relation_id_ is not None and not isinstance(relation_id_, int): - raise Exception( - f"Expected relation_id_ to be a int, received: {type(relation_id_)}" - ) - - if unit_relation_data_ is not None and not isinstance( - unit_relation_data_, dict - ): - raise Exception( - f"Expected unit_relation_data_ to be a Mapping, received: {type(unit_relation_data_)}" - ) - - self.applicationdata = applicationdata_ - self.cross_model = cross_model_ - self.endpoint = endpoint_ - self.related_endpoint = related_endpoint_ - self.relation_id = relation_id_ - self.unit_relation_data = unit_relation_data_ - self.unknown_fields = unknown_fields - - -class EndpointStatus(Type): - _toSchema = { - "application": "application", - "name": "name", - "role": "role", - "subordinate": "subordinate", - } - _toPy = { - "application": "application", - "name": "name", - "role": "role", - "subordinate": "subordinate", - } - - def __init__( - self, application=None, name=None, role=None, subordinate=None, **unknown_fields - ): - """Application : str - name : str - role : str - subordinate : bool - """ - application_ = application - name_ = name - role_ = role - subordinate_ = subordinate - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if role_ is not None and not isinstance(role_, (bytes, str)): - raise Exception(f"Expected role_ to be a str, received: {type(role_)}") - - if subordinate_ is not None and not isinstance(subordinate_, bool): - raise Exception( - f"Expected subordinate_ to be a bool, received: {type(subordinate_)}" - ) - - self.application = application_ - self.name = name_ - self.role = role_ - self.subordinate = subordinate_ - self.unknown_fields = unknown_fields - - -class EnqueuedActions(Type): - _toSchema = {"actions": "actions", "operation": "operation"} - _toPy = {"actions": "actions", "operation": "operation"} - - def __init__(self, actions=None, operation=None, **unknown_fields): - """Actions : typing.Sequence[~ActionResult] - operation : str - """ - actions_ = [ActionResult.from_json(o) for o in actions or []] - operation_ = operation - - # Validate arguments against known Juju API types. - if actions_ is not None and not isinstance(actions_, (bytes, str, list)): - raise Exception( - f"Expected actions_ to be a Sequence, received: {type(actions_)}" - ) - - if operation_ is not None and not isinstance(operation_, (bytes, str)): - raise Exception( - f"Expected operation_ to be a str, received: {type(operation_)}" - ) - - self.actions = actions_ - self.operation = operation_ - self.unknown_fields = unknown_fields - - -class Entities(Type): - _toSchema = {"entities": "entities"} - _toPy = {"entities": "entities"} - - def __init__(self, entities=None, **unknown_fields): - """Entities : typing.Sequence[~Entity]""" - entities_ = [Entity.from_json(o) for o in entities or []] - - # Validate arguments against known Juju API types. - if entities_ is not None and not isinstance(entities_, (bytes, str, list)): - raise Exception( - f"Expected entities_ to be a Sequence, received: {type(entities_)}" - ) - - self.entities = entities_ - self.unknown_fields = unknown_fields - - -class Entity(Type): - _toSchema = {"tag": "tag"} - _toPy = {"tag": "tag"} - - def __init__(self, tag=None, **unknown_fields): - """Tag : str""" - tag_ = tag - - # Validate arguments against known Juju API types. - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class EntityAnnotations(Type): - _toSchema = {"annotations": "annotations", "entity": "entity"} - _toPy = {"annotations": "annotations", "entity": "entity"} - - def __init__(self, annotations=None, entity=None, **unknown_fields): - """Annotations : typing.Mapping[str, str] - entity : str - """ - annotations_ = annotations - entity_ = entity - - # Validate arguments against known Juju API types. - if annotations_ is not None and not isinstance(annotations_, dict): - raise Exception( - f"Expected annotations_ to be a Mapping, received: {type(annotations_)}" - ) - - if entity_ is not None and not isinstance(entity_, (bytes, str)): - raise Exception(f"Expected entity_ to be a str, received: {type(entity_)}") - - self.annotations = annotations_ - self.entity = entity_ - self.unknown_fields = unknown_fields - - -class EntityMetrics(Type): - _toSchema = {"error": "error", "metrics": "metrics"} - _toPy = {"error": "error", "metrics": "metrics"} - - def __init__(self, error=None, metrics=None, **unknown_fields): - """Error : Error - metrics : typing.Sequence[~MetricResult] - """ - error_ = Error.from_json(error) if error else None - metrics_ = [MetricResult.from_json(o) for o in metrics or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if metrics_ is not None and not isinstance(metrics_, (bytes, str, list)): - raise Exception( - f"Expected metrics_ to be a Sequence, received: {type(metrics_)}" - ) - - self.error = error_ - self.metrics = metrics_ - self.unknown_fields = unknown_fields - - -class EntityPassword(Type): - _toSchema = {"password": "password", "tag": "tag"} - _toPy = {"password": "password", "tag": "tag"} - - def __init__(self, password=None, tag=None, **unknown_fields): - """Password : str - tag : str - """ - password_ = password - tag_ = tag - - # Validate arguments against known Juju API types. - if password_ is not None and not isinstance(password_, (bytes, str)): - raise Exception( - f"Expected password_ to be a str, received: {type(password_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.password = password_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class EntityPasswords(Type): - _toSchema = {"changes": "changes"} - _toPy = {"changes": "changes"} - - def __init__(self, changes=None, **unknown_fields): - """Changes : typing.Sequence[~EntityPassword]""" - changes_ = [EntityPassword.from_json(o) for o in changes or []] - - # Validate arguments against known Juju API types. - if changes_ is not None and not isinstance(changes_, (bytes, str, list)): - raise Exception( - f"Expected changes_ to be a Sequence, received: {type(changes_)}" - ) - - self.changes = changes_ - self.unknown_fields = unknown_fields - - -class EntityStatus(Type): - _toSchema = {"data": "data", "info": "info", "since": "since", "status": "status"} - _toPy = {"data": "data", "info": "info", "since": "since", "status": "status"} - - def __init__(self, data=None, info=None, since=None, status=None, **unknown_fields): - """Data : typing.Mapping[str, typing.Any] - info : str - since : str - status : str - """ - data_ = data - info_ = info - since_ = since - status_ = status - - # Validate arguments against known Juju API types. - if data_ is not None and not isinstance(data_, dict): - raise Exception(f"Expected data_ to be a Mapping, received: {type(data_)}") - - if info_ is not None and not isinstance(info_, (bytes, str)): - raise Exception(f"Expected info_ to be a str, received: {type(info_)}") - - if since_ is not None and not isinstance(since_, (bytes, str)): - raise Exception(f"Expected since_ to be a str, received: {type(since_)}") - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - self.data = data_ - self.info = info_ - self.since = since_ - self.status = status_ - self.unknown_fields = unknown_fields - - -class Error(Type): - _toSchema = {"code": "code", "info": "info", "message": "message"} - _toPy = {"code": "code", "info": "info", "message": "message"} - - def __init__(self, code=None, info=None, message=None, **unknown_fields): - """Code : str - info : typing.Mapping[str, typing.Any] - message : str - """ - code_ = code - info_ = info - message_ = message - - # Validate arguments against known Juju API types. - if code_ is not None and not isinstance(code_, (bytes, str)): - raise Exception(f"Expected code_ to be a str, received: {type(code_)}") - - if info_ is not None and not isinstance(info_, dict): - raise Exception(f"Expected info_ to be a Mapping, received: {type(info_)}") - - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - self.code = code_ - self.info = info_ - self.message = message_ - self.unknown_fields = unknown_fields - - -class ErrorResult(Type): - _toSchema = {"error": "error"} - _toPy = {"error": "error"} - - def __init__(self, error=None, **unknown_fields): - """Error : Error""" - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.error = error_ - self.unknown_fields = unknown_fields - - -class ErrorResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ErrorResult]""" - results_ = [ErrorResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ExportBundleParams(Type): - _toSchema = { - "include_charm_defaults": "include-charm-defaults", - "include_series": "include-series", - } - _toPy = { - "include-charm-defaults": "include_charm_defaults", - "include-series": "include_series", - } - - def __init__( - self, include_charm_defaults=None, include_series=None, **unknown_fields - ): - """include_charm_defaults : bool - include_series : bool - """ - include_charm_defaults_ = include_charm_defaults - include_series_ = include_series - - # Validate arguments against known Juju API types. - if include_charm_defaults_ is not None and not isinstance( - include_charm_defaults_, bool - ): - raise Exception( - f"Expected include_charm_defaults_ to be a bool, received: {type(include_charm_defaults_)}" - ) - - if include_series_ is not None and not isinstance(include_series_, bool): - raise Exception( - f"Expected include_series_ to be a bool, received: {type(include_series_)}" - ) - - self.include_charm_defaults = include_charm_defaults_ - self.include_series = include_series_ - self.unknown_fields = unknown_fields - - -class ExposedEndpoint(Type): - _toSchema = { - "expose_to_cidrs": "expose-to-cidrs", - "expose_to_spaces": "expose-to-spaces", - } - _toPy = { - "expose-to-cidrs": "expose_to_cidrs", - "expose-to-spaces": "expose_to_spaces", - } - - def __init__(self, expose_to_cidrs=None, expose_to_spaces=None, **unknown_fields): - """expose_to_cidrs : typing.Sequence[str] - expose_to_spaces : typing.Sequence[str] - """ - expose_to_cidrs_ = expose_to_cidrs - expose_to_spaces_ = expose_to_spaces - - # Validate arguments against known Juju API types. - if expose_to_cidrs_ is not None and not isinstance( - expose_to_cidrs_, (bytes, str, list) - ): - raise Exception( - f"Expected expose_to_cidrs_ to be a Sequence, received: {type(expose_to_cidrs_)}" - ) - - if expose_to_spaces_ is not None and not isinstance( - expose_to_spaces_, (bytes, str, list) - ): - raise Exception( - f"Expected expose_to_spaces_ to be a Sequence, received: {type(expose_to_spaces_)}" - ) - - self.expose_to_cidrs = expose_to_cidrs_ - self.expose_to_spaces = expose_to_spaces_ - self.unknown_fields = unknown_fields - - -class ExpressionTree(Type): - _toSchema = {"expression": "Expression"} - _toPy = {"Expression": "expression"} - - def __init__(self, expression=None, **unknown_fields): - """Expression : Any""" - expression_ = expression - - # Validate arguments against known Juju API types. - self.expression = expression_ - self.unknown_fields = unknown_fields - - -class ExternalControllerInfo(Type): - _toSchema = { - "addrs": "addrs", - "ca_cert": "ca-cert", - "controller_alias": "controller-alias", - "controller_tag": "controller-tag", - } - _toPy = { - "addrs": "addrs", - "ca-cert": "ca_cert", - "controller-alias": "controller_alias", - "controller-tag": "controller_tag", - } - - def __init__( - self, - addrs=None, - ca_cert=None, - controller_alias=None, - controller_tag=None, - **unknown_fields, - ): - """Addrs : typing.Sequence[str] - ca_cert : str - controller_alias : str - controller_tag : str - """ - addrs_ = addrs - ca_cert_ = ca_cert - controller_alias_ = controller_alias - controller_tag_ = controller_tag - - # Validate arguments against known Juju API types. - if addrs_ is not None and not isinstance(addrs_, (bytes, str, list)): - raise Exception( - f"Expected addrs_ to be a Sequence, received: {type(addrs_)}" - ) - - if ca_cert_ is not None and not isinstance(ca_cert_, (bytes, str)): - raise Exception( - f"Expected ca_cert_ to be a str, received: {type(ca_cert_)}" - ) - - if controller_alias_ is not None and not isinstance( - controller_alias_, (bytes, str) - ): - raise Exception( - f"Expected controller_alias_ to be a str, received: {type(controller_alias_)}" - ) - - if controller_tag_ is not None and not isinstance( - controller_tag_, (bytes, str) - ): - raise Exception( - f"Expected controller_tag_ to be a str, received: {type(controller_tag_)}" - ) - - self.addrs = addrs_ - self.ca_cert = ca_cert_ - self.controller_alias = controller_alias_ - self.controller_tag = controller_tag_ - self.unknown_fields = unknown_fields - - -class FilesystemAttachmentDetails(Type): - _toSchema = { - "filesystemattachmentinfo": "FilesystemAttachmentInfo", - "life": "life", - "mount_point": "mount-point", - "read_only": "read-only", - } - _toPy = { - "FilesystemAttachmentInfo": "filesystemattachmentinfo", - "life": "life", - "mount-point": "mount_point", - "read-only": "read_only", - } - - def __init__( - self, - filesystemattachmentinfo=None, - life=None, - mount_point=None, - read_only=None, - **unknown_fields, - ): - """Filesystemattachmentinfo : FilesystemAttachmentInfo - life : str - mount_point : str - read_only : bool - """ - filesystemattachmentinfo_ = ( - FilesystemAttachmentInfo.from_json(filesystemattachmentinfo) - if filesystemattachmentinfo - else None - ) - life_ = life - mount_point_ = mount_point - read_only_ = read_only - - # Validate arguments against known Juju API types. - if filesystemattachmentinfo_ is not None and not isinstance( - filesystemattachmentinfo_, (dict, FilesystemAttachmentInfo) - ): - raise Exception( - f"Expected filesystemattachmentinfo_ to be a FilesystemAttachmentInfo, received: {type(filesystemattachmentinfo_)}" - ) - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if mount_point_ is not None and not isinstance(mount_point_, (bytes, str)): - raise Exception( - f"Expected mount_point_ to be a str, received: {type(mount_point_)}" - ) - - if read_only_ is not None and not isinstance(read_only_, bool): - raise Exception( - f"Expected read_only_ to be a bool, received: {type(read_only_)}" - ) - - self.filesystemattachmentinfo = filesystemattachmentinfo_ - self.life = life_ - self.mount_point = mount_point_ - self.read_only = read_only_ - self.unknown_fields = unknown_fields - - -class FilesystemAttachmentInfo(Type): - _toSchema = {"mount_point": "mount-point", "read_only": "read-only"} - _toPy = {"mount-point": "mount_point", "read-only": "read_only"} - - def __init__(self, mount_point=None, read_only=None, **unknown_fields): - """mount_point : str - read_only : bool - """ - mount_point_ = mount_point - read_only_ = read_only - - # Validate arguments against known Juju API types. - if mount_point_ is not None and not isinstance(mount_point_, (bytes, str)): - raise Exception( - f"Expected mount_point_ to be a str, received: {type(mount_point_)}" - ) - - if read_only_ is not None and not isinstance(read_only_, bool): - raise Exception( - f"Expected read_only_ to be a bool, received: {type(read_only_)}" - ) - - self.mount_point = mount_point_ - self.read_only = read_only_ - self.unknown_fields = unknown_fields - - -class FilesystemDetails(Type): - _toSchema = { - "filesystem_tag": "filesystem-tag", - "info": "info", - "life": "life", - "machine_attachments": "machine-attachments", - "status": "status", - "storage": "storage", - "unit_attachments": "unit-attachments", - "volume_tag": "volume-tag", - } - _toPy = { - "filesystem-tag": "filesystem_tag", - "info": "info", - "life": "life", - "machine-attachments": "machine_attachments", - "status": "status", - "storage": "storage", - "unit-attachments": "unit_attachments", - "volume-tag": "volume_tag", - } - - def __init__( - self, - filesystem_tag=None, - info=None, - life=None, - machine_attachments=None, - status=None, - storage=None, - unit_attachments=None, - volume_tag=None, - **unknown_fields, - ): - """filesystem_tag : str - info : FilesystemInfo - life : str - machine_attachments : typing.Mapping[str, ~FilesystemAttachmentDetails] - status : EntityStatus - storage : StorageDetails - unit_attachments : typing.Mapping[str, ~FilesystemAttachmentDetails] - volume_tag : str - """ - filesystem_tag_ = filesystem_tag - info_ = FilesystemInfo.from_json(info) if info else None - life_ = life - machine_attachments_ = { - k: FilesystemAttachmentDetails.from_json(v) - for k, v in (machine_attachments or dict()).items() - } - status_ = EntityStatus.from_json(status) if status else None - storage_ = StorageDetails.from_json(storage) if storage else None - unit_attachments_ = { - k: FilesystemAttachmentDetails.from_json(v) - for k, v in (unit_attachments or dict()).items() - } - volume_tag_ = volume_tag - - # Validate arguments against known Juju API types. - if filesystem_tag_ is not None and not isinstance( - filesystem_tag_, (bytes, str) - ): - raise Exception( - f"Expected filesystem_tag_ to be a str, received: {type(filesystem_tag_)}" - ) - - if info_ is not None and not isinstance(info_, (dict, FilesystemInfo)): - raise Exception( - f"Expected info_ to be a FilesystemInfo, received: {type(info_)}" - ) - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if machine_attachments_ is not None and not isinstance( - machine_attachments_, dict - ): - raise Exception( - f"Expected machine_attachments_ to be a Mapping, received: {type(machine_attachments_)}" - ) - - if status_ is not None and not isinstance(status_, (dict, EntityStatus)): - raise Exception( - f"Expected status_ to be a EntityStatus, received: {type(status_)}" - ) - - if storage_ is not None and not isinstance(storage_, (dict, StorageDetails)): - raise Exception( - f"Expected storage_ to be a StorageDetails, received: {type(storage_)}" - ) - - if unit_attachments_ is not None and not isinstance(unit_attachments_, dict): - raise Exception( - f"Expected unit_attachments_ to be a Mapping, received: {type(unit_attachments_)}" - ) - - if volume_tag_ is not None and not isinstance(volume_tag_, (bytes, str)): - raise Exception( - f"Expected volume_tag_ to be a str, received: {type(volume_tag_)}" - ) - - self.filesystem_tag = filesystem_tag_ - self.info = info_ - self.life = life_ - self.machine_attachments = machine_attachments_ - self.status = status_ - self.storage = storage_ - self.unit_attachments = unit_attachments_ - self.volume_tag = volume_tag_ - self.unknown_fields = unknown_fields - - -class FilesystemDetailsListResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : typing.Sequence[~FilesystemDetails] - """ - error_ = Error.from_json(error) if error else None - result_ = [FilesystemDetails.from_json(o) for o in result or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (bytes, str, list)): - raise Exception( - f"Expected result_ to be a Sequence, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class FilesystemDetailsListResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~FilesystemDetailsListResult]""" - results_ = [FilesystemDetailsListResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class FilesystemFilter(Type): - _toSchema = {"machines": "machines"} - _toPy = {"machines": "machines"} - - def __init__(self, machines=None, **unknown_fields): - """Machines : typing.Sequence[str]""" - machines_ = machines - - # Validate arguments against known Juju API types. - if machines_ is not None and not isinstance(machines_, (bytes, str, list)): - raise Exception( - f"Expected machines_ to be a Sequence, received: {type(machines_)}" - ) - - self.machines = machines_ - self.unknown_fields = unknown_fields - - -class FilesystemFilters(Type): - _toSchema = {"filters": "filters"} - _toPy = {"filters": "filters"} - - def __init__(self, filters=None, **unknown_fields): - """Filters : typing.Sequence[~FilesystemFilter]""" - filters_ = [FilesystemFilter.from_json(o) for o in filters or []] - - # Validate arguments against known Juju API types. - if filters_ is not None and not isinstance(filters_, (bytes, str, list)): - raise Exception( - f"Expected filters_ to be a Sequence, received: {type(filters_)}" - ) - - self.filters = filters_ - self.unknown_fields = unknown_fields - - -class FilesystemInfo(Type): - _toSchema = {"filesystem_id": "filesystem-id", "pool": "pool", "size": "size"} - _toPy = {"filesystem-id": "filesystem_id", "pool": "pool", "size": "size"} - - def __init__(self, filesystem_id=None, pool=None, size=None, **unknown_fields): - """filesystem_id : str - pool : str - size : int - """ - filesystem_id_ = filesystem_id - pool_ = pool - size_ = size - - # Validate arguments against known Juju API types. - if filesystem_id_ is not None and not isinstance(filesystem_id_, (bytes, str)): - raise Exception( - f"Expected filesystem_id_ to be a str, received: {type(filesystem_id_)}" - ) - - if pool_ is not None and not isinstance(pool_, (bytes, str)): - raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - self.filesystem_id = filesystem_id_ - self.pool = pool_ - self.size = size_ - self.unknown_fields = unknown_fields - - -class FindToolsParams(Type): - _toSchema = { - "agentstream": "agentstream", - "arch": "arch", - "major": "major", - "number": "number", - "os_type": "os-type", - } - _toPy = { - "agentstream": "agentstream", - "arch": "arch", - "major": "major", - "number": "number", - "os-type": "os_type", - } - - def __init__( - self, - agentstream=None, - arch=None, - major=None, - number=None, - os_type=None, - **unknown_fields, - ): - """Agentstream : str - arch : str - major : int - number : Number - os_type : str - """ - agentstream_ = agentstream - arch_ = arch - major_ = major - number_ = Number.from_json(number) if number else None - os_type_ = os_type - - # Validate arguments against known Juju API types. - if agentstream_ is not None and not isinstance(agentstream_, (bytes, str)): - raise Exception( - f"Expected agentstream_ to be a str, received: {type(agentstream_)}" - ) - - if arch_ is not None and not isinstance(arch_, (bytes, str)): - raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") - - if major_ is not None and not isinstance(major_, int): - raise Exception(f"Expected major_ to be a int, received: {type(major_)}") - - if number_ is not None and not isinstance(number_, (dict, Number)): - raise Exception( - f"Expected number_ to be a Number, received: {type(number_)}" - ) - - if os_type_ is not None and not isinstance(os_type_, (bytes, str)): - raise Exception( - f"Expected os_type_ to be a str, received: {type(os_type_)}" - ) - - self.agentstream = agentstream_ - self.arch = arch_ - self.major = major_ - self.number = number_ - self.os_type = os_type_ - self.unknown_fields = unknown_fields - - -class FindToolsResult(Type): - _toSchema = {"error": "error", "list_": "list"} - _toPy = {"error": "error", "list": "list_"} - - def __init__(self, error=None, list_=None, **unknown_fields): - """Error : Error - list_ : typing.Sequence[~Tools] - """ - error_ = Error.from_json(error) if error else None - list__ = [Tools.from_json(o) for o in list_ or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if list__ is not None and not isinstance(list__, (bytes, str, list)): - raise Exception( - f"Expected list__ to be a Sequence, received: {type(list__)}" - ) - - self.error = error_ - self.list_ = list__ - self.unknown_fields = unknown_fields - - -class FirewallRule(Type): - _toSchema = {"known_service": "known-service", "whitelist_cidrs": "whitelist-cidrs"} - _toPy = {"known-service": "known_service", "whitelist-cidrs": "whitelist_cidrs"} - - def __init__(self, known_service=None, whitelist_cidrs=None, **unknown_fields): - """known_service : str - whitelist_cidrs : typing.Sequence[str] - """ - known_service_ = known_service - whitelist_cidrs_ = whitelist_cidrs - - # Validate arguments against known Juju API types. - if known_service_ is not None and not isinstance(known_service_, (bytes, str)): - raise Exception( - f"Expected known_service_ to be a str, received: {type(known_service_)}" - ) - - if whitelist_cidrs_ is not None and not isinstance( - whitelist_cidrs_, (bytes, str, list) - ): - raise Exception( - f"Expected whitelist_cidrs_ to be a Sequence, received: {type(whitelist_cidrs_)}" - ) - - self.known_service = known_service_ - self.whitelist_cidrs = whitelist_cidrs_ - self.unknown_fields = unknown_fields - - -class FirewallRuleArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~FirewallRule]""" - args_ = [FirewallRule.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class FullStatus(Type): - _toSchema = { - "applications": "applications", - "branches": "branches", - "controller_timestamp": "controller-timestamp", - "filesystems": "filesystems", - "machines": "machines", - "model": "model", - "offers": "offers", - "relations": "relations", - "remote_applications": "remote-applications", - "storage": "storage", - "volumes": "volumes", - } - _toPy = { - "applications": "applications", - "branches": "branches", - "controller-timestamp": "controller_timestamp", - "filesystems": "filesystems", - "machines": "machines", - "model": "model", - "offers": "offers", - "relations": "relations", - "remote-applications": "remote_applications", - "storage": "storage", - "volumes": "volumes", - } - - def __init__( - self, - applications=None, - branches=None, - controller_timestamp=None, - filesystems=None, - machines=None, - model=None, - offers=None, - relations=None, - remote_applications=None, - storage=None, - volumes=None, - **unknown_fields, - ): - """Applications : typing.Mapping[str, ~ApplicationStatus] - branches : typing.Mapping[str, ~BranchStatus] - controller_timestamp : str - filesystems : typing.Sequence[~FilesystemDetails] - machines : typing.Mapping[str, ~MachineStatus] - model : ModelStatusInfo - offers : typing.Mapping[str, ~ApplicationOfferStatus] - relations : typing.Sequence[~RelationStatus] - remote_applications : typing.Mapping[str, ~RemoteApplicationStatus] - storage : typing.Sequence[~StorageDetails] - volumes : typing.Sequence[~VolumeDetails] - """ - applications_ = { - k: ApplicationStatus.from_json(v) - for k, v in (applications or dict()).items() - } - branches_ = { - k: BranchStatus.from_json(v) for k, v in (branches or dict()).items() - } - controller_timestamp_ = controller_timestamp - filesystems_ = [FilesystemDetails.from_json(o) for o in filesystems or []] - machines_ = { - k: MachineStatus.from_json(v) for k, v in (machines or dict()).items() - } - model_ = ModelStatusInfo.from_json(model) if model else None - offers_ = { - k: ApplicationOfferStatus.from_json(v) - for k, v in (offers or dict()).items() - } - relations_ = [RelationStatus.from_json(o) for o in relations or []] - remote_applications_ = { - k: RemoteApplicationStatus.from_json(v) - for k, v in (remote_applications or dict()).items() - } - storage_ = [StorageDetails.from_json(o) for o in storage or []] - volumes_ = [VolumeDetails.from_json(o) for o in volumes or []] - - # Validate arguments against known Juju API types. - if applications_ is not None and not isinstance(applications_, dict): - raise Exception( - f"Expected applications_ to be a Mapping, received: {type(applications_)}" - ) - - if branches_ is not None and not isinstance(branches_, dict): - raise Exception( - f"Expected branches_ to be a Mapping, received: {type(branches_)}" - ) - - if controller_timestamp_ is not None and not isinstance( - controller_timestamp_, (bytes, str) - ): - raise Exception( - f"Expected controller_timestamp_ to be a str, received: {type(controller_timestamp_)}" - ) - - if filesystems_ is not None and not isinstance( - filesystems_, (bytes, str, list) - ): - raise Exception( - f"Expected filesystems_ to be a Sequence, received: {type(filesystems_)}" - ) - - if machines_ is not None and not isinstance(machines_, dict): - raise Exception( - f"Expected machines_ to be a Mapping, received: {type(machines_)}" - ) - - if model_ is not None and not isinstance(model_, (dict, ModelStatusInfo)): - raise Exception( - f"Expected model_ to be a ModelStatusInfo, received: {type(model_)}" - ) - - if offers_ is not None and not isinstance(offers_, dict): - raise Exception( - f"Expected offers_ to be a Mapping, received: {type(offers_)}" - ) - - if relations_ is not None and not isinstance(relations_, (bytes, str, list)): - raise Exception( - f"Expected relations_ to be a Sequence, received: {type(relations_)}" - ) - - if remote_applications_ is not None and not isinstance( - remote_applications_, dict - ): - raise Exception( - f"Expected remote_applications_ to be a Mapping, received: {type(remote_applications_)}" - ) - - if storage_ is not None and not isinstance(storage_, (bytes, str, list)): - raise Exception( - f"Expected storage_ to be a Sequence, received: {type(storage_)}" - ) - - if volumes_ is not None and not isinstance(volumes_, (bytes, str, list)): - raise Exception( - f"Expected volumes_ to be a Sequence, received: {type(volumes_)}" - ) - - self.applications = applications_ - self.branches = branches_ - self.controller_timestamp = controller_timestamp_ - self.filesystems = filesystems_ - self.machines = machines_ - self.model = model_ - self.offers = offers_ - self.relations = relations_ - self.remote_applications = remote_applications_ - self.storage = storage_ - self.volumes = volumes_ - self.unknown_fields = unknown_fields - - -class Generation(Type): - _toSchema = { - "applications": "applications", - "branch": "branch", - "completed": "completed", - "completed_by": "completed-by", - "created": "created", - "created_by": "created-by", - "generation_id": "generation-id", - } - _toPy = { - "applications": "applications", - "branch": "branch", - "completed": "completed", - "completed-by": "completed_by", - "created": "created", - "created-by": "created_by", - "generation-id": "generation_id", - } - - def __init__( - self, - applications=None, - branch=None, - completed=None, - completed_by=None, - created=None, - created_by=None, - generation_id=None, - **unknown_fields, - ): - """Applications : typing.Sequence[~GenerationApplication] - branch : str - completed : int - completed_by : str - created : int - created_by : str - generation_id : int - """ - applications_ = [GenerationApplication.from_json(o) for o in applications or []] - branch_ = branch - completed_ = completed - completed_by_ = completed_by - created_ = created - created_by_ = created_by - generation_id_ = generation_id - - # Validate arguments against known Juju API types. - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - if branch_ is not None and not isinstance(branch_, (bytes, str)): - raise Exception(f"Expected branch_ to be a str, received: {type(branch_)}") - - if completed_ is not None and not isinstance(completed_, int): - raise Exception( - f"Expected completed_ to be a int, received: {type(completed_)}" - ) - - if completed_by_ is not None and not isinstance(completed_by_, (bytes, str)): - raise Exception( - f"Expected completed_by_ to be a str, received: {type(completed_by_)}" - ) - - if created_ is not None and not isinstance(created_, int): - raise Exception( - f"Expected created_ to be a int, received: {type(created_)}" - ) - - if created_by_ is not None and not isinstance(created_by_, (bytes, str)): - raise Exception( - f"Expected created_by_ to be a str, received: {type(created_by_)}" - ) - - if generation_id_ is not None and not isinstance(generation_id_, int): - raise Exception( - f"Expected generation_id_ to be a int, received: {type(generation_id_)}" - ) - - self.applications = applications_ - self.branch = branch_ - self.completed = completed_ - self.completed_by = completed_by_ - self.created = created_ - self.created_by = created_by_ - self.generation_id = generation_id_ - self.unknown_fields = unknown_fields - - -class GenerationApplication(Type): - _toSchema = { - "application": "application", - "config": "config", - "pending": "pending", - "progress": "progress", - "tracking": "tracking", - } - _toPy = { - "application": "application", - "config": "config", - "pending": "pending", - "progress": "progress", - "tracking": "tracking", - } - - def __init__( - self, - application=None, - config=None, - pending=None, - progress=None, - tracking=None, - **unknown_fields, - ): - """Application : str - config : typing.Mapping[str, typing.Any] - pending : typing.Sequence[str] - progress : str - tracking : typing.Sequence[str] - """ - application_ = application - config_ = config - pending_ = pending - progress_ = progress - tracking_ = tracking - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if pending_ is not None and not isinstance(pending_, (bytes, str, list)): - raise Exception( - f"Expected pending_ to be a Sequence, received: {type(pending_)}" - ) - - if progress_ is not None and not isinstance(progress_, (bytes, str)): - raise Exception( - f"Expected progress_ to be a str, received: {type(progress_)}" - ) - - if tracking_ is not None and not isinstance(tracking_, (bytes, str, list)): - raise Exception( - f"Expected tracking_ to be a Sequence, received: {type(tracking_)}" - ) - - self.application = application_ - self.config = config_ - self.pending = pending_ - self.progress = progress_ - self.tracking = tracking_ - self.unknown_fields = unknown_fields - - -class GenerationId(Type): - _toSchema = {"generation_id": "generation-id"} - _toPy = {"generation-id": "generation_id"} - - def __init__(self, generation_id=None, **unknown_fields): - """generation_id : int""" - generation_id_ = generation_id - - # Validate arguments against known Juju API types. - if generation_id_ is not None and not isinstance(generation_id_, int): - raise Exception( - f"Expected generation_id_ to be a int, received: {type(generation_id_)}" - ) - - self.generation_id = generation_id_ - self.unknown_fields = unknown_fields - - -class GenerationResult(Type): - _toSchema = {"error": "error", "generation": "generation"} - _toPy = {"error": "error", "generation": "generation"} - - def __init__(self, error=None, generation=None, **unknown_fields): - """Error : Error - generation : Generation - """ - error_ = Error.from_json(error) if error else None - generation_ = Generation.from_json(generation) if generation else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if generation_ is not None and not isinstance(generation_, (dict, Generation)): - raise Exception( - f"Expected generation_ to be a Generation, received: {type(generation_)}" - ) - - self.error = error_ - self.generation = generation_ - self.unknown_fields = unknown_fields - - -class GetConstraintsResults(Type): - _toSchema = {"constraints": "constraints"} - _toPy = {"constraints": "constraints"} - - def __init__(self, constraints=None, **unknown_fields): - """Constraints : Value""" - constraints_ = Value.from_json(constraints) if constraints else None - - # Validate arguments against known Juju API types. - if constraints_ is not None and not isinstance(constraints_, (dict, Value)): - raise Exception( - f"Expected constraints_ to be a Value, received: {type(constraints_)}" - ) - - self.constraints = constraints_ - self.unknown_fields = unknown_fields - - -class GrantRevokeUserSecretArg(Type): - _toSchema = {"applications": "applications", "label": "label", "uri": "uri"} - _toPy = {"applications": "applications", "label": "label", "uri": "uri"} - - def __init__(self, applications=None, label=None, uri=None, **unknown_fields): - """Applications : typing.Sequence[str] - label : str - uri : str - """ - applications_ = applications - label_ = label - uri_ = uri - - # Validate arguments against known Juju API types. - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - if label_ is not None and not isinstance(label_, (bytes, str)): - raise Exception(f"Expected label_ to be a str, received: {type(label_)}") - - if uri_ is not None and not isinstance(uri_, (bytes, str)): - raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") - - self.applications = applications_ - self.label = label_ - self.uri = uri_ - self.unknown_fields = unknown_fields - - -class HardwareCharacteristics(Type): - _toSchema = { - "arch": "arch", - "availability_zone": "availability-zone", - "cpu_cores": "cpu-cores", - "cpu_power": "cpu-power", - "mem": "mem", - "root_disk": "root-disk", - "root_disk_source": "root-disk-source", - "tags": "tags", - "virt_type": "virt-type", - } - _toPy = { - "arch": "arch", - "availability-zone": "availability_zone", - "cpu-cores": "cpu_cores", - "cpu-power": "cpu_power", - "mem": "mem", - "root-disk": "root_disk", - "root-disk-source": "root_disk_source", - "tags": "tags", - "virt-type": "virt_type", - } - - def __init__( - self, - arch=None, - availability_zone=None, - cpu_cores=None, - cpu_power=None, - mem=None, - root_disk=None, - root_disk_source=None, - tags=None, - virt_type=None, - **unknown_fields, - ): - """Arch : str - availability_zone : str - cpu_cores : int - cpu_power : int - mem : int - root_disk : int - root_disk_source : str - tags : typing.Sequence[str] - virt_type : str - """ - arch_ = arch - availability_zone_ = availability_zone - cpu_cores_ = cpu_cores - cpu_power_ = cpu_power - mem_ = mem - root_disk_ = root_disk - root_disk_source_ = root_disk_source - tags_ = tags - virt_type_ = virt_type - - # Validate arguments against known Juju API types. - if arch_ is not None and not isinstance(arch_, (bytes, str)): - raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") - - if availability_zone_ is not None and not isinstance( - availability_zone_, (bytes, str) - ): - raise Exception( - f"Expected availability_zone_ to be a str, received: {type(availability_zone_)}" - ) - - if cpu_cores_ is not None and not isinstance(cpu_cores_, int): - raise Exception( - f"Expected cpu_cores_ to be a int, received: {type(cpu_cores_)}" - ) - - if cpu_power_ is not None and not isinstance(cpu_power_, int): - raise Exception( - f"Expected cpu_power_ to be a int, received: {type(cpu_power_)}" - ) - - if mem_ is not None and not isinstance(mem_, int): - raise Exception(f"Expected mem_ to be a int, received: {type(mem_)}") - - if root_disk_ is not None and not isinstance(root_disk_, int): - raise Exception( - f"Expected root_disk_ to be a int, received: {type(root_disk_)}" - ) - - if root_disk_source_ is not None and not isinstance( - root_disk_source_, (bytes, str) - ): - raise Exception( - f"Expected root_disk_source_ to be a str, received: {type(root_disk_source_)}" - ) - - if tags_ is not None and not isinstance(tags_, (bytes, str, list)): - raise Exception(f"Expected tags_ to be a Sequence, received: {type(tags_)}") - - if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): - raise Exception( - f"Expected virt_type_ to be a str, received: {type(virt_type_)}" - ) - - self.arch = arch_ - self.availability_zone = availability_zone_ - self.cpu_cores = cpu_cores_ - self.cpu_power = cpu_power_ - self.mem = mem_ - self.root_disk = root_disk_ - self.root_disk_source = root_disk_source_ - self.tags = tags_ - self.virt_type = virt_type_ - self.unknown_fields = unknown_fields - - -class History(Type): - _toSchema = {"error": "error", "statuses": "statuses"} - _toPy = {"error": "error", "statuses": "statuses"} - - def __init__(self, error=None, statuses=None, **unknown_fields): - """Error : Error - statuses : typing.Sequence[~DetailedStatus] - """ - error_ = Error.from_json(error) if error else None - statuses_ = [DetailedStatus.from_json(o) for o in statuses or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if statuses_ is not None and not isinstance(statuses_, (bytes, str, list)): - raise Exception( - f"Expected statuses_ to be a Sequence, received: {type(statuses_)}" - ) - - self.error = error_ - self.statuses = statuses_ - self.unknown_fields = unknown_fields - - -class HostPort(Type): - _toSchema = { - "address": "Address", - "cidr": "cidr", - "config_type": "config-type", - "is_secondary": "is-secondary", - "port": "port", - "scope": "scope", - "space_id": "space-id", - "space_name": "space-name", - "type_": "type", - "value": "value", - } - _toPy = { - "Address": "address", - "cidr": "cidr", - "config-type": "config_type", - "is-secondary": "is_secondary", - "port": "port", - "scope": "scope", - "space-id": "space_id", - "space-name": "space_name", - "type": "type_", - "value": "value", - } - - def __init__( - self, - address=None, - cidr=None, - config_type=None, - is_secondary=None, - port=None, - scope=None, - space_id=None, - space_name=None, - type_=None, - value=None, - **unknown_fields, - ): - """Address : Address - cidr : str - config_type : str - is_secondary : bool - port : int - scope : str - space_id : str - space_name : str - type_ : str - value : str - """ - address_ = Address.from_json(address) if address else None - cidr_ = cidr - config_type_ = config_type - is_secondary_ = is_secondary - port_ = port - scope_ = scope - space_id_ = space_id - space_name_ = space_name - type__ = type_ - value_ = value - - # Validate arguments against known Juju API types. - if address_ is not None and not isinstance(address_, (dict, Address)): - raise Exception( - f"Expected address_ to be a Address, received: {type(address_)}" - ) - - if cidr_ is not None and not isinstance(cidr_, (bytes, str)): - raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") - - if config_type_ is not None and not isinstance(config_type_, (bytes, str)): - raise Exception( - f"Expected config_type_ to be a str, received: {type(config_type_)}" - ) - - if is_secondary_ is not None and not isinstance(is_secondary_, bool): - raise Exception( - f"Expected is_secondary_ to be a bool, received: {type(is_secondary_)}" - ) - - if port_ is not None and not isinstance(port_, int): - raise Exception(f"Expected port_ to be a int, received: {type(port_)}") - - if scope_ is not None and not isinstance(scope_, (bytes, str)): - raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") - - if space_id_ is not None and not isinstance(space_id_, (bytes, str)): - raise Exception( - f"Expected space_id_ to be a str, received: {type(space_id_)}" - ) - - if space_name_ is not None and not isinstance(space_name_, (bytes, str)): - raise Exception( - f"Expected space_name_ to be a str, received: {type(space_name_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - if value_ is not None and not isinstance(value_, (bytes, str)): - raise Exception(f"Expected value_ to be a str, received: {type(value_)}") - - self.address = address_ - self.cidr = cidr_ - self.config_type = config_type_ - self.is_secondary = is_secondary_ - self.port = port_ - self.scope = scope_ - self.space_id = space_id_ - self.space_name = space_name_ - self.type_ = type__ - self.value = value_ - self.unknown_fields = unknown_fields - - -class HostedModelConfig(Type): - _toSchema = { - "cloud_spec": "cloud-spec", - "config": "config", - "error": "error", - "name": "name", - "owner": "owner", - } - _toPy = { - "cloud-spec": "cloud_spec", - "config": "config", - "error": "error", - "name": "name", - "owner": "owner", - } - - def __init__( - self, - cloud_spec=None, - config=None, - error=None, - name=None, - owner=None, - **unknown_fields, - ): - """cloud_spec : CloudSpec - config : typing.Mapping[str, typing.Any] - error : Error - name : str - owner : str - """ - cloud_spec_ = CloudSpec.from_json(cloud_spec) if cloud_spec else None - config_ = config - error_ = Error.from_json(error) if error else None - name_ = name - owner_ = owner - - # Validate arguments against known Juju API types. - if cloud_spec_ is not None and not isinstance(cloud_spec_, (dict, CloudSpec)): - raise Exception( - f"Expected cloud_spec_ to be a CloudSpec, received: {type(cloud_spec_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if owner_ is not None and not isinstance(owner_, (bytes, str)): - raise Exception(f"Expected owner_ to be a str, received: {type(owner_)}") - - self.cloud_spec = cloud_spec_ - self.config = config_ - self.error = error_ - self.name = name_ - self.owner = owner_ - self.unknown_fields = unknown_fields - - -class HostedModelConfigsResults(Type): - _toSchema = {"models": "models"} - _toPy = {"models": "models"} - - def __init__(self, models=None, **unknown_fields): - """Models : typing.Sequence[~HostedModelConfig]""" - models_ = [HostedModelConfig.from_json(o) for o in models or []] - - # Validate arguments against known Juju API types. - if models_ is not None and not isinstance(models_, (bytes, str, list)): - raise Exception( - f"Expected models_ to be a Sequence, received: {type(models_)}" - ) - - self.models = models_ - self.unknown_fields = unknown_fields - - -class ImageMetadataFilter(Type): - _toSchema = { - "arches": "arches", - "region": "region", - "root_storage_type": "root-storage-type", - "stream": "stream", - "versions": "versions", - "virt_type": "virt-type", - } - _toPy = { - "arches": "arches", - "region": "region", - "root-storage-type": "root_storage_type", - "stream": "stream", - "versions": "versions", - "virt-type": "virt_type", - } - - def __init__( - self, - arches=None, - region=None, - root_storage_type=None, - stream=None, - versions=None, - virt_type=None, - **unknown_fields, - ): - """Arches : typing.Sequence[str] - region : str - root_storage_type : str - stream : str - versions : typing.Sequence[str] - virt_type : str - """ - arches_ = arches - region_ = region - root_storage_type_ = root_storage_type - stream_ = stream - versions_ = versions - virt_type_ = virt_type - - # Validate arguments against known Juju API types. - if arches_ is not None and not isinstance(arches_, (bytes, str, list)): - raise Exception( - f"Expected arches_ to be a Sequence, received: {type(arches_)}" - ) - - if region_ is not None and not isinstance(region_, (bytes, str)): - raise Exception(f"Expected region_ to be a str, received: {type(region_)}") - - if root_storage_type_ is not None and not isinstance( - root_storage_type_, (bytes, str) - ): - raise Exception( - f"Expected root_storage_type_ to be a str, received: {type(root_storage_type_)}" - ) - - if stream_ is not None and not isinstance(stream_, (bytes, str)): - raise Exception(f"Expected stream_ to be a str, received: {type(stream_)}") - - if versions_ is not None and not isinstance(versions_, (bytes, str, list)): - raise Exception( - f"Expected versions_ to be a Sequence, received: {type(versions_)}" - ) - - if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): - raise Exception( - f"Expected virt_type_ to be a str, received: {type(virt_type_)}" - ) - - self.arches = arches_ - self.region = region_ - self.root_storage_type = root_storage_type_ - self.stream = stream_ - self.versions = versions_ - self.virt_type = virt_type_ - self.unknown_fields = unknown_fields - - -class ImportStorageDetails(Type): - _toSchema = {"storage_tag": "storage-tag"} - _toPy = {"storage-tag": "storage_tag"} - - def __init__(self, storage_tag=None, **unknown_fields): - """storage_tag : str""" - storage_tag_ = storage_tag - - # Validate arguments against known Juju API types. - if storage_tag_ is not None and not isinstance(storage_tag_, (bytes, str)): - raise Exception( - f"Expected storage_tag_ to be a str, received: {type(storage_tag_)}" - ) - - self.storage_tag = storage_tag_ - self.unknown_fields = unknown_fields - - -class ImportStorageParams(Type): - _toSchema = { - "kind": "kind", - "pool": "pool", - "provider_id": "provider-id", - "storage_name": "storage-name", - } - _toPy = { - "kind": "kind", - "pool": "pool", - "provider-id": "provider_id", - "storage-name": "storage_name", - } - - def __init__( - self, - kind=None, - pool=None, - provider_id=None, - storage_name=None, - **unknown_fields, - ): - """Kind : int - pool : str - provider_id : str - storage_name : str - """ - kind_ = kind - pool_ = pool - provider_id_ = provider_id - storage_name_ = storage_name - - # Validate arguments against known Juju API types. - if kind_ is not None and not isinstance(kind_, int): - raise Exception(f"Expected kind_ to be a int, received: {type(kind_)}") - - if pool_ is not None and not isinstance(pool_, (bytes, str)): - raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if storage_name_ is not None and not isinstance(storage_name_, (bytes, str)): - raise Exception( - f"Expected storage_name_ to be a str, received: {type(storage_name_)}" - ) - - self.kind = kind_ - self.pool = pool_ - self.provider_id = provider_id_ - self.storage_name = storage_name_ - self.unknown_fields = unknown_fields - - -class ImportStorageResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : ImportStorageDetails - """ - error_ = Error.from_json(error) if error else None - result_ = ImportStorageDetails.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance( - result_, (dict, ImportStorageDetails) - ): - raise Exception( - f"Expected result_ to be a ImportStorageDetails, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class ImportStorageResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ImportStorageResult]""" - results_ = [ImportStorageResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class InitiateMigrationArgs(Type): - _toSchema = {"specs": "specs"} - _toPy = {"specs": "specs"} - - def __init__(self, specs=None, **unknown_fields): - """Specs : typing.Sequence[~MigrationSpec]""" - specs_ = [MigrationSpec.from_json(o) for o in specs or []] - - # Validate arguments against known Juju API types. - if specs_ is not None and not isinstance(specs_, (bytes, str, list)): - raise Exception( - f"Expected specs_ to be a Sequence, received: {type(specs_)}" - ) - - self.specs = specs_ - self.unknown_fields = unknown_fields - - -class InitiateMigrationResult(Type): - _toSchema = { - "error": "error", - "migration_id": "migration-id", - "model_tag": "model-tag", - } - _toPy = {"error": "error", "migration-id": "migration_id", "model-tag": "model_tag"} - - def __init__(self, error=None, migration_id=None, model_tag=None, **unknown_fields): - """Error : Error - migration_id : str - model_tag : str - """ - error_ = Error.from_json(error) if error else None - migration_id_ = migration_id - model_tag_ = model_tag - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if migration_id_ is not None and not isinstance(migration_id_, (bytes, str)): - raise Exception( - f"Expected migration_id_ to be a str, received: {type(migration_id_)}" - ) - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - self.error = error_ - self.migration_id = migration_id_ - self.model_tag = model_tag_ - self.unknown_fields = unknown_fields - - -class InitiateMigrationResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~InitiateMigrationResult]""" - results_ = [InitiateMigrationResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class InstanceType(Type): - _toSchema = { - "arches": "arches", - "cost": "cost", - "cpu_cores": "cpu-cores", - "memory": "memory", - "name": "name", - "root_disk": "root-disk", - "virt_type": "virt-type", - } - _toPy = { - "arches": "arches", - "cost": "cost", - "cpu-cores": "cpu_cores", - "memory": "memory", - "name": "name", - "root-disk": "root_disk", - "virt-type": "virt_type", - } - - def __init__( - self, - arches=None, - cost=None, - cpu_cores=None, - memory=None, - name=None, - root_disk=None, - virt_type=None, - **unknown_fields, - ): - """Arches : typing.Sequence[str] - cost : int - cpu_cores : int - memory : int - name : str - root_disk : int - virt_type : str - """ - arches_ = arches - cost_ = cost - cpu_cores_ = cpu_cores - memory_ = memory - name_ = name - root_disk_ = root_disk - virt_type_ = virt_type - - # Validate arguments against known Juju API types. - if arches_ is not None and not isinstance(arches_, (bytes, str, list)): - raise Exception( - f"Expected arches_ to be a Sequence, received: {type(arches_)}" - ) - - if cost_ is not None and not isinstance(cost_, int): - raise Exception(f"Expected cost_ to be a int, received: {type(cost_)}") - - if cpu_cores_ is not None and not isinstance(cpu_cores_, int): - raise Exception( - f"Expected cpu_cores_ to be a int, received: {type(cpu_cores_)}" - ) - - if memory_ is not None and not isinstance(memory_, int): - raise Exception(f"Expected memory_ to be a int, received: {type(memory_)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if root_disk_ is not None and not isinstance(root_disk_, int): - raise Exception( - f"Expected root_disk_ to be a int, received: {type(root_disk_)}" - ) - - if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): - raise Exception( - f"Expected virt_type_ to be a str, received: {type(virt_type_)}" - ) - - self.arches = arches_ - self.cost = cost_ - self.cpu_cores = cpu_cores_ - self.memory = memory_ - self.name = name_ - self.root_disk = root_disk_ - self.virt_type = virt_type_ - self.unknown_fields = unknown_fields - - -class InstanceTypesResult(Type): - _toSchema = { - "cost_currency": "cost-currency", - "cost_divisor": "cost-divisor", - "cost_unit": "cost-unit", - "error": "error", - "instance_types": "instance-types", - } - _toPy = { - "cost-currency": "cost_currency", - "cost-divisor": "cost_divisor", - "cost-unit": "cost_unit", - "error": "error", - "instance-types": "instance_types", - } - - def __init__( - self, - cost_currency=None, - cost_divisor=None, - cost_unit=None, - error=None, - instance_types=None, - **unknown_fields, - ): - """cost_currency : str - cost_divisor : int - cost_unit : str - error : Error - instance_types : typing.Sequence[~InstanceType] - """ - cost_currency_ = cost_currency - cost_divisor_ = cost_divisor - cost_unit_ = cost_unit - error_ = Error.from_json(error) if error else None - instance_types_ = [InstanceType.from_json(o) for o in instance_types or []] - - # Validate arguments against known Juju API types. - if cost_currency_ is not None and not isinstance(cost_currency_, (bytes, str)): - raise Exception( - f"Expected cost_currency_ to be a str, received: {type(cost_currency_)}" - ) - - if cost_divisor_ is not None and not isinstance(cost_divisor_, int): - raise Exception( - f"Expected cost_divisor_ to be a int, received: {type(cost_divisor_)}" - ) - - if cost_unit_ is not None and not isinstance(cost_unit_, (bytes, str)): - raise Exception( - f"Expected cost_unit_ to be a str, received: {type(cost_unit_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if instance_types_ is not None and not isinstance( - instance_types_, (bytes, str, list) - ): - raise Exception( - f"Expected instance_types_ to be a Sequence, received: {type(instance_types_)}" - ) - - self.cost_currency = cost_currency_ - self.cost_divisor = cost_divisor_ - self.cost_unit = cost_unit_ - self.error = error_ - self.instance_types = instance_types_ - self.unknown_fields = unknown_fields - - -class InstanceTypesResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~InstanceTypesResult]""" - results_ = [InstanceTypesResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class IntResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : int - """ - error_ = Error.from_json(error) if error else None - result_ = result - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, int): - raise Exception(f"Expected result_ to be a int, received: {type(result_)}") - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class InvalidateCredentialArg(Type): - _toSchema = {"reason": "reason"} - _toPy = {"reason": "reason"} - - def __init__(self, reason=None, **unknown_fields): - """Reason : str""" - reason_ = reason - - # Validate arguments against known Juju API types. - if reason_ is not None and not isinstance(reason_, (bytes, str)): - raise Exception(f"Expected reason_ to be a str, received: {type(reason_)}") - - self.reason = reason_ - self.unknown_fields = unknown_fields - - -class IsMeteredResult(Type): - _toSchema = {"metered": "metered"} - _toPy = {"metered": "metered"} - - def __init__(self, metered=None, **unknown_fields): - """Metered : bool""" - metered_ = metered - - # Validate arguments against known Juju API types. - if metered_ is not None and not isinstance(metered_, bool): - raise Exception( - f"Expected metered_ to be a bool, received: {type(metered_)}" - ) - - self.metered = metered_ - self.unknown_fields = unknown_fields - - -class LXDProfile(Type): - _toSchema = {"config": "config", "description": "description", "devices": "devices"} - _toPy = {"config": "config", "description": "description", "devices": "devices"} - - def __init__(self, config=None, description=None, devices=None, **unknown_fields): - """Config : typing.Mapping[str, str] - description : str - devices : typing.Mapping[str, typing.Any] - """ - config_ = config - description_ = description - devices_ = devices - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if devices_ is not None and not isinstance(devices_, dict): - raise Exception( - f"Expected devices_ to be a Mapping, received: {type(devices_)}" - ) - - self.config = config_ - self.description = description_ - self.devices = devices_ - self.unknown_fields = unknown_fields - - -class ListCloudImageMetadataResult(Type): - _toSchema = {"result": "result"} - _toPy = {"result": "result"} - - def __init__(self, result=None, **unknown_fields): - """Result : typing.Sequence[~CloudImageMetadata]""" - result_ = [CloudImageMetadata.from_json(o) for o in result or []] - - # Validate arguments against known Juju API types. - if result_ is not None and not isinstance(result_, (bytes, str, list)): - raise Exception( - f"Expected result_ to be a Sequence, received: {type(result_)}" - ) - - self.result = result_ - self.unknown_fields = unknown_fields - - -class ListCloudInfo(Type): - _toSchema = {"clouddetails": "CloudDetails", "user_access": "user-access"} - _toPy = {"CloudDetails": "clouddetails", "user-access": "user_access"} - - def __init__(self, clouddetails=None, user_access=None, **unknown_fields): - """Clouddetails : CloudDetails - user_access : str - """ - clouddetails_ = CloudDetails.from_json(clouddetails) if clouddetails else None - user_access_ = user_access - - # Validate arguments against known Juju API types. - if clouddetails_ is not None and not isinstance( - clouddetails_, (dict, CloudDetails) - ): - raise Exception( - f"Expected clouddetails_ to be a CloudDetails, received: {type(clouddetails_)}" - ) - - if user_access_ is not None and not isinstance(user_access_, (bytes, str)): - raise Exception( - f"Expected user_access_ to be a str, received: {type(user_access_)}" - ) - - self.clouddetails = clouddetails_ - self.user_access = user_access_ - self.unknown_fields = unknown_fields - - -class ListCloudInfoResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : ListCloudInfo - """ - error_ = Error.from_json(error) if error else None - result_ = ListCloudInfo.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, ListCloudInfo)): - raise Exception( - f"Expected result_ to be a ListCloudInfo, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class ListCloudInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ListCloudInfoResult]""" - results_ = [ListCloudInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ListCloudsRequest(Type): - _toSchema = {"all_": "all", "user_tag": "user-tag"} - _toPy = {"all": "all_", "user-tag": "user_tag"} - - def __init__(self, all_=None, user_tag=None, **unknown_fields): - """all_ : bool - user_tag : str - """ - all__ = all_ - user_tag_ = user_tag - - # Validate arguments against known Juju API types. - if all__ is not None and not isinstance(all__, bool): - raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") - - if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): - raise Exception( - f"Expected user_tag_ to be a str, received: {type(user_tag_)}" - ) - - self.all_ = all__ - self.user_tag = user_tag_ - self.unknown_fields = unknown_fields - - -class ListFirewallRulesResults(Type): - _toSchema = {"rules": "Rules"} - _toPy = {"Rules": "rules"} - - def __init__(self, rules=None, **unknown_fields): - """Rules : typing.Sequence[~FirewallRule]""" - rules_ = [FirewallRule.from_json(o) for o in rules or []] - - # Validate arguments against known Juju API types. - if rules_ is not None and not isinstance(rules_, (bytes, str, list)): - raise Exception( - f"Expected rules_ to be a Sequence, received: {type(rules_)}" - ) - - self.rules = rules_ - self.unknown_fields = unknown_fields - - -class ListResourcesArgs(Type): - _toSchema = {"entities": "entities"} - _toPy = {"entities": "entities"} - - def __init__(self, entities=None, **unknown_fields): - """Entities : typing.Sequence[~Entity]""" - entities_ = [Entity.from_json(o) for o in entities or []] - - # Validate arguments against known Juju API types. - if entities_ is not None and not isinstance(entities_, (bytes, str, list)): - raise Exception( - f"Expected entities_ to be a Sequence, received: {type(entities_)}" - ) - - self.entities = entities_ - self.unknown_fields = unknown_fields - - -class ListSSHKeys(Type): - _toSchema = {"entities": "entities", "mode": "mode"} - _toPy = {"entities": "entities", "mode": "mode"} - - def __init__(self, entities=None, mode=None, **unknown_fields): - """Entities : Entities - mode : bool - """ - entities_ = Entities.from_json(entities) if entities else None - mode_ = mode - - # Validate arguments against known Juju API types. - if entities_ is not None and not isinstance(entities_, (dict, Entities)): - raise Exception( - f"Expected entities_ to be a Entities, received: {type(entities_)}" - ) - - if mode_ is not None and not isinstance(mode_, bool): - raise Exception(f"Expected mode_ to be a bool, received: {type(mode_)}") - - self.entities = entities_ - self.mode = mode_ - self.unknown_fields = unknown_fields - - -class ListSecretBackendsArgs(Type): - _toSchema = {"names": "names", "reveal": "reveal"} - _toPy = {"names": "names", "reveal": "reveal"} - - def __init__(self, names=None, reveal=None, **unknown_fields): - """Names : typing.Sequence[str] - reveal : bool - """ - names_ = names - reveal_ = reveal - - # Validate arguments against known Juju API types. - if names_ is not None and not isinstance(names_, (bytes, str, list)): - raise Exception( - f"Expected names_ to be a Sequence, received: {type(names_)}" - ) - - if reveal_ is not None and not isinstance(reveal_, bool): - raise Exception(f"Expected reveal_ to be a bool, received: {type(reveal_)}") - - self.names = names_ - self.reveal = reveal_ - self.unknown_fields = unknown_fields - - -class ListSecretBackendsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~SecretBackendResult]""" - results_ = [SecretBackendResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ListSecretResult(Type): - _toSchema = { - "access": "access", - "create_time": "create-time", - "description": "description", - "label": "label", - "latest_expire_time": "latest-expire-time", - "latest_revision": "latest-revision", - "latest_revision_checksum": "latest-revision-checksum", - "next_rotate_time": "next-rotate-time", - "owner_tag": "owner-tag", - "revisions": "revisions", - "rotate_policy": "rotate-policy", - "update_time": "update-time", - "uri": "uri", - "value": "value", - "version": "version", - } - _toPy = { - "access": "access", - "create-time": "create_time", - "description": "description", - "label": "label", - "latest-expire-time": "latest_expire_time", - "latest-revision": "latest_revision", - "latest-revision-checksum": "latest_revision_checksum", - "next-rotate-time": "next_rotate_time", - "owner-tag": "owner_tag", - "revisions": "revisions", - "rotate-policy": "rotate_policy", - "update-time": "update_time", - "uri": "uri", - "value": "value", - "version": "version", - } - - def __init__( - self, - access=None, - create_time=None, - description=None, - label=None, - latest_expire_time=None, - latest_revision=None, - latest_revision_checksum=None, - next_rotate_time=None, - owner_tag=None, - revisions=None, - rotate_policy=None, - update_time=None, - uri=None, - value=None, - version=None, - **unknown_fields, - ): - """Access : typing.Sequence[~AccessInfo] - create_time : str - description : str - label : str - latest_expire_time : str - latest_revision : int - latest_revision_checksum : str - next_rotate_time : str - owner_tag : str - revisions : typing.Sequence[~SecretRevision] - rotate_policy : str - update_time : str - uri : str - value : SecretValueResult - version : int - """ - access_ = [AccessInfo.from_json(o) for o in access or []] - create_time_ = create_time - description_ = description - label_ = label - latest_expire_time_ = latest_expire_time - latest_revision_ = latest_revision - latest_revision_checksum_ = latest_revision_checksum - next_rotate_time_ = next_rotate_time - owner_tag_ = owner_tag - revisions_ = [SecretRevision.from_json(o) for o in revisions or []] - rotate_policy_ = rotate_policy - update_time_ = update_time - uri_ = uri - value_ = SecretValueResult.from_json(value) if value else None - version_ = version - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str, list)): - raise Exception( - f"Expected access_ to be a Sequence, received: {type(access_)}" - ) - - if create_time_ is not None and not isinstance(create_time_, (bytes, str)): - raise Exception( - f"Expected create_time_ to be a str, received: {type(create_time_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if label_ is not None and not isinstance(label_, (bytes, str)): - raise Exception(f"Expected label_ to be a str, received: {type(label_)}") - - if latest_expire_time_ is not None and not isinstance( - latest_expire_time_, (bytes, str) - ): - raise Exception( - f"Expected latest_expire_time_ to be a str, received: {type(latest_expire_time_)}" - ) - - if latest_revision_ is not None and not isinstance(latest_revision_, int): - raise Exception( - f"Expected latest_revision_ to be a int, received: {type(latest_revision_)}" - ) - - if latest_revision_checksum_ is not None and not isinstance( - latest_revision_checksum_, (bytes, str) - ): - raise Exception( - f"Expected latest_revision_checksum_ to be a str, received: {type(latest_revision_checksum_)}" - ) - - if next_rotate_time_ is not None and not isinstance( - next_rotate_time_, (bytes, str) - ): - raise Exception( - f"Expected next_rotate_time_ to be a str, received: {type(next_rotate_time_)}" - ) - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - if revisions_ is not None and not isinstance(revisions_, (bytes, str, list)): - raise Exception( - f"Expected revisions_ to be a Sequence, received: {type(revisions_)}" - ) - - if rotate_policy_ is not None and not isinstance(rotate_policy_, (bytes, str)): - raise Exception( - f"Expected rotate_policy_ to be a str, received: {type(rotate_policy_)}" - ) - - if update_time_ is not None and not isinstance(update_time_, (bytes, str)): - raise Exception( - f"Expected update_time_ to be a str, received: {type(update_time_)}" - ) - - if uri_ is not None and not isinstance(uri_, (bytes, str)): - raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") - - if value_ is not None and not isinstance(value_, (dict, SecretValueResult)): - raise Exception( - f"Expected value_ to be a SecretValueResult, received: {type(value_)}" - ) - - if version_ is not None and not isinstance(version_, int): - raise Exception( - f"Expected version_ to be a int, received: {type(version_)}" - ) - - self.access = access_ - self.create_time = create_time_ - self.description = description_ - self.label = label_ - self.latest_expire_time = latest_expire_time_ - self.latest_revision = latest_revision_ - self.latest_revision_checksum = latest_revision_checksum_ - self.next_rotate_time = next_rotate_time_ - self.owner_tag = owner_tag_ - self.revisions = revisions_ - self.rotate_policy = rotate_policy_ - self.update_time = update_time_ - self.uri = uri_ - self.value = value_ - self.version = version_ - self.unknown_fields = unknown_fields - - -class ListSecretResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ListSecretResult]""" - results_ = [ListSecretResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ListSecretsArgs(Type): - _toSchema = {"filter_": "filter", "show_secrets": "show-secrets"} - _toPy = {"filter": "filter_", "show-secrets": "show_secrets"} - - def __init__(self, filter_=None, show_secrets=None, **unknown_fields): - """filter_ : SecretsFilter - show_secrets : bool - """ - filter__ = SecretsFilter.from_json(filter_) if filter_ else None - show_secrets_ = show_secrets - - # Validate arguments against known Juju API types. - if filter__ is not None and not isinstance(filter__, (dict, SecretsFilter)): - raise Exception( - f"Expected filter__ to be a SecretsFilter, received: {type(filter__)}" - ) - - if show_secrets_ is not None and not isinstance(show_secrets_, bool): - raise Exception( - f"Expected show_secrets_ to be a bool, received: {type(show_secrets_)}" - ) - - self.filter_ = filter__ - self.show_secrets = show_secrets_ - self.unknown_fields = unknown_fields - - -class ListSpacesResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~Space]""" - results_ = [Space.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ListSubnetsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~Subnet]""" - results_ = [Subnet.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class LoginRequest(Type): - _toSchema = { - "auth_tag": "auth-tag", - "bakery_version": "bakery-version", - "cli_args": "cli-args", - "client_version": "client-version", - "credentials": "credentials", - "macaroons": "macaroons", - "nonce": "nonce", - "token": "token", - "user_data": "user-data", - } - _toPy = { - "auth-tag": "auth_tag", - "bakery-version": "bakery_version", - "cli-args": "cli_args", - "client-version": "client_version", - "credentials": "credentials", - "macaroons": "macaroons", - "nonce": "nonce", - "token": "token", - "user-data": "user_data", - } - - def __init__( - self, - auth_tag=None, - bakery_version=None, - cli_args=None, - client_version=None, - credentials=None, - macaroons=None, - nonce=None, - token=None, - user_data=None, - **unknown_fields, - ): - """auth_tag : str - bakery_version : int - cli_args : str - client_version : str - credentials : str - macaroons : typing.Sequence[~Macaroon] - nonce : str - token : str - user_data : str - """ - auth_tag_ = auth_tag - bakery_version_ = bakery_version - cli_args_ = cli_args - client_version_ = client_version - credentials_ = credentials - macaroons_ = [Macaroon.from_json(o) for o in macaroons or []] - nonce_ = nonce - token_ = token - user_data_ = user_data - - # Validate arguments against known Juju API types. - if auth_tag_ is not None and not isinstance(auth_tag_, (bytes, str)): - raise Exception( - f"Expected auth_tag_ to be a str, received: {type(auth_tag_)}" - ) - - if bakery_version_ is not None and not isinstance(bakery_version_, int): - raise Exception( - f"Expected bakery_version_ to be a int, received: {type(bakery_version_)}" - ) - - if cli_args_ is not None and not isinstance(cli_args_, (bytes, str)): - raise Exception( - f"Expected cli_args_ to be a str, received: {type(cli_args_)}" - ) - - if client_version_ is not None and not isinstance( - client_version_, (bytes, str) - ): - raise Exception( - f"Expected client_version_ to be a str, received: {type(client_version_)}" - ) - - if credentials_ is not None and not isinstance(credentials_, (bytes, str)): - raise Exception( - f"Expected credentials_ to be a str, received: {type(credentials_)}" - ) - - if macaroons_ is not None and not isinstance(macaroons_, (bytes, str, list)): - raise Exception( - f"Expected macaroons_ to be a Sequence, received: {type(macaroons_)}" - ) - - if nonce_ is not None and not isinstance(nonce_, (bytes, str)): - raise Exception(f"Expected nonce_ to be a str, received: {type(nonce_)}") - - if token_ is not None and not isinstance(token_, (bytes, str)): - raise Exception(f"Expected token_ to be a str, received: {type(token_)}") - - if user_data_ is not None and not isinstance(user_data_, (bytes, str)): - raise Exception( - f"Expected user_data_ to be a str, received: {type(user_data_)}" - ) - - self.auth_tag = auth_tag_ - self.bakery_version = bakery_version_ - self.cli_args = cli_args_ - self.client_version = client_version_ - self.credentials = credentials_ - self.macaroons = macaroons_ - self.nonce = nonce_ - self.token = token_ - self.user_data = user_data_ - self.unknown_fields = unknown_fields - - -class LoginResult(Type): - _toSchema = { - "bakery_discharge_required": "bakery-discharge-required", - "controller_tag": "controller-tag", - "discharge_required": "discharge-required", - "discharge_required_error": "discharge-required-error", - "facades": "facades", - "model_tag": "model-tag", - "public_dns_name": "public-dns-name", - "server_version": "server-version", - "servers": "servers", - "user_info": "user-info", - } - _toPy = { - "bakery-discharge-required": "bakery_discharge_required", - "controller-tag": "controller_tag", - "discharge-required": "discharge_required", - "discharge-required-error": "discharge_required_error", - "facades": "facades", - "model-tag": "model_tag", - "public-dns-name": "public_dns_name", - "server-version": "server_version", - "servers": "servers", - "user-info": "user_info", - } - - def __init__( - self, - bakery_discharge_required=None, - controller_tag=None, - discharge_required=None, - discharge_required_error=None, - facades=None, - model_tag=None, - public_dns_name=None, - server_version=None, - servers=None, - user_info=None, - **unknown_fields, - ): - """bakery_discharge_required : Macaroon - controller_tag : str - discharge_required : Macaroon - discharge_required_error : str - facades : typing.Sequence[~FacadeVersions] - model_tag : str - public_dns_name : str - server_version : str - servers : typing.Sequence[~HostPort] - user_info : AuthUserInfo - """ - bakery_discharge_required_ = ( - Macaroon.from_json(bakery_discharge_required) - if bakery_discharge_required - else None - ) - controller_tag_ = controller_tag - discharge_required_ = ( - Macaroon.from_json(discharge_required) if discharge_required else None - ) - discharge_required_error_ = discharge_required_error - facades_ = [FacadeVersions.from_json(o) for o in facades or []] - model_tag_ = model_tag - public_dns_name_ = public_dns_name - server_version_ = server_version - servers_ = [HostPort.from_json(o) for o in servers or []] - user_info_ = AuthUserInfo.from_json(user_info) if user_info else None - - # Validate arguments against known Juju API types. - if bakery_discharge_required_ is not None and not isinstance( - bakery_discharge_required_, (dict, Macaroon) - ): - raise Exception( - f"Expected bakery_discharge_required_ to be a Macaroon, received: {type(bakery_discharge_required_)}" - ) - - if controller_tag_ is not None and not isinstance( - controller_tag_, (bytes, str) - ): - raise Exception( - f"Expected controller_tag_ to be a str, received: {type(controller_tag_)}" - ) - - if discharge_required_ is not None and not isinstance( - discharge_required_, (dict, Macaroon) - ): - raise Exception( - f"Expected discharge_required_ to be a Macaroon, received: {type(discharge_required_)}" - ) - - if discharge_required_error_ is not None and not isinstance( - discharge_required_error_, (bytes, str) - ): - raise Exception( - f"Expected discharge_required_error_ to be a str, received: {type(discharge_required_error_)}" - ) - - if facades_ is not None and not isinstance(facades_, (bytes, str, list)): - raise Exception( - f"Expected facades_ to be a Sequence, received: {type(facades_)}" - ) - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - if public_dns_name_ is not None and not isinstance( - public_dns_name_, (bytes, str) - ): - raise Exception( - f"Expected public_dns_name_ to be a str, received: {type(public_dns_name_)}" - ) - - if server_version_ is not None and not isinstance( - server_version_, (bytes, str) - ): - raise Exception( - f"Expected server_version_ to be a str, received: {type(server_version_)}" - ) - - if servers_ is not None and not isinstance(servers_, (bytes, str, list)): - raise Exception( - f"Expected servers_ to be a Sequence, received: {type(servers_)}" - ) - - if user_info_ is not None and not isinstance(user_info_, (dict, AuthUserInfo)): - raise Exception( - f"Expected user_info_ to be a AuthUserInfo, received: {type(user_info_)}" - ) - - self.bakery_discharge_required = bakery_discharge_required_ - self.controller_tag = controller_tag_ - self.discharge_required = discharge_required_ - self.discharge_required_error = discharge_required_error_ - self.facades = facades_ - self.model_tag = model_tag_ - self.public_dns_name = public_dns_name_ - self.server_version = server_version_ - self.servers = servers_ - self.user_info = user_info_ - self.unknown_fields = unknown_fields - - -class Macaroon(Type): - _toSchema = {} - _toPy = {} - - def __init__(self, **unknown_fields): - """ """ - self.unknown_fields = unknown_fields - - -class MachineHardware(Type): - _toSchema = { - "arch": "arch", - "availability_zone": "availability-zone", - "cores": "cores", - "cpu_power": "cpu-power", - "mem": "mem", - "root_disk": "root-disk", - "tags": "tags", - "virt_type": "virt-type", - } - _toPy = { - "arch": "arch", - "availability-zone": "availability_zone", - "cores": "cores", - "cpu-power": "cpu_power", - "mem": "mem", - "root-disk": "root_disk", - "tags": "tags", - "virt-type": "virt_type", - } - - def __init__( - self, - arch=None, - availability_zone=None, - cores=None, - cpu_power=None, - mem=None, - root_disk=None, - tags=None, - virt_type=None, - **unknown_fields, - ): - """Arch : str - availability_zone : str - cores : int - cpu_power : int - mem : int - root_disk : int - tags : typing.Sequence[str] - virt_type : str - """ - arch_ = arch - availability_zone_ = availability_zone - cores_ = cores - cpu_power_ = cpu_power - mem_ = mem - root_disk_ = root_disk - tags_ = tags - virt_type_ = virt_type - - # Validate arguments against known Juju API types. - if arch_ is not None and not isinstance(arch_, (bytes, str)): - raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") - - if availability_zone_ is not None and not isinstance( - availability_zone_, (bytes, str) - ): - raise Exception( - f"Expected availability_zone_ to be a str, received: {type(availability_zone_)}" - ) - - if cores_ is not None and not isinstance(cores_, int): - raise Exception(f"Expected cores_ to be a int, received: {type(cores_)}") - - if cpu_power_ is not None and not isinstance(cpu_power_, int): - raise Exception( - f"Expected cpu_power_ to be a int, received: {type(cpu_power_)}" - ) - - if mem_ is not None and not isinstance(mem_, int): - raise Exception(f"Expected mem_ to be a int, received: {type(mem_)}") - - if root_disk_ is not None and not isinstance(root_disk_, int): - raise Exception( - f"Expected root_disk_ to be a int, received: {type(root_disk_)}" - ) - - if tags_ is not None and not isinstance(tags_, (bytes, str, list)): - raise Exception(f"Expected tags_ to be a Sequence, received: {type(tags_)}") - - if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): - raise Exception( - f"Expected virt_type_ to be a str, received: {type(virt_type_)}" - ) - - self.arch = arch_ - self.availability_zone = availability_zone_ - self.cores = cores_ - self.cpu_power = cpu_power_ - self.mem = mem_ - self.root_disk = root_disk_ - self.tags = tags_ - self.virt_type = virt_type_ - self.unknown_fields = unknown_fields - - -class MachineStatus(Type): - _toSchema = { - "agent_status": "agent-status", - "base": "base", - "constraints": "constraints", - "containers": "containers", - "display_name": "display-name", - "dns_name": "dns-name", - "hardware": "hardware", - "has_vote": "has-vote", - "hostname": "hostname", - "id_": "id", - "instance_id": "instance-id", - "instance_status": "instance-status", - "ip_addresses": "ip-addresses", - "jobs": "jobs", - "lxd_profiles": "lxd-profiles", - "modification_status": "modification-status", - "network_interfaces": "network-interfaces", - "primary_controller_machine": "primary-controller-machine", - "wants_vote": "wants-vote", - } - _toPy = { - "agent-status": "agent_status", - "base": "base", - "constraints": "constraints", - "containers": "containers", - "display-name": "display_name", - "dns-name": "dns_name", - "hardware": "hardware", - "has-vote": "has_vote", - "hostname": "hostname", - "id": "id_", - "instance-id": "instance_id", - "instance-status": "instance_status", - "ip-addresses": "ip_addresses", - "jobs": "jobs", - "lxd-profiles": "lxd_profiles", - "modification-status": "modification_status", - "network-interfaces": "network_interfaces", - "primary-controller-machine": "primary_controller_machine", - "wants-vote": "wants_vote", - } - - def __init__( - self, - agent_status=None, - base=None, - constraints=None, - containers=None, - display_name=None, - dns_name=None, - hardware=None, - has_vote=None, - hostname=None, - id_=None, - instance_id=None, - instance_status=None, - ip_addresses=None, - jobs=None, - lxd_profiles=None, - modification_status=None, - network_interfaces=None, - primary_controller_machine=None, - wants_vote=None, - **unknown_fields, - ): - """agent_status : DetailedStatus - base : Base - constraints : str - containers : typing.Mapping[str, ~MachineStatus] - display_name : str - dns_name : str - hardware : str - has_vote : bool - hostname : str - id_ : str - instance_id : str - instance_status : DetailedStatus - ip_addresses : typing.Sequence[str] - jobs : typing.Sequence[str] - lxd_profiles : typing.Mapping[str, ~LXDProfile] - modification_status : DetailedStatus - network_interfaces : typing.Mapping[str, ~NetworkInterface] - primary_controller_machine : bool - wants_vote : bool - """ - agent_status_ = DetailedStatus.from_json(agent_status) if agent_status else None - base_ = Base.from_json(base) if base else None - constraints_ = constraints - containers_ = { - k: MachineStatus.from_json(v) for k, v in (containers or dict()).items() - } - display_name_ = display_name - dns_name_ = dns_name - hardware_ = hardware - has_vote_ = has_vote - hostname_ = hostname - id__ = id_ - instance_id_ = instance_id - instance_status_ = ( - DetailedStatus.from_json(instance_status) if instance_status else None - ) - ip_addresses_ = ip_addresses - jobs_ = jobs - lxd_profiles_ = { - k: LXDProfile.from_json(v) for k, v in (lxd_profiles or dict()).items() - } - modification_status_ = ( - DetailedStatus.from_json(modification_status) - if modification_status - else None - ) - network_interfaces_ = { - k: NetworkInterface.from_json(v) - for k, v in (network_interfaces or dict()).items() - } - primary_controller_machine_ = primary_controller_machine - wants_vote_ = wants_vote - - # Validate arguments against known Juju API types. - if agent_status_ is not None and not isinstance( - agent_status_, (dict, DetailedStatus) - ): - raise Exception( - f"Expected agent_status_ to be a DetailedStatus, received: {type(agent_status_)}" - ) - - if base_ is not None and not isinstance(base_, (dict, Base)): - raise Exception(f"Expected base_ to be a Base, received: {type(base_)}") - - if constraints_ is not None and not isinstance(constraints_, (bytes, str)): - raise Exception( - f"Expected constraints_ to be a str, received: {type(constraints_)}" - ) - - if containers_ is not None and not isinstance(containers_, dict): - raise Exception( - f"Expected containers_ to be a Mapping, received: {type(containers_)}" - ) - - if display_name_ is not None and not isinstance(display_name_, (bytes, str)): - raise Exception( - f"Expected display_name_ to be a str, received: {type(display_name_)}" - ) - - if dns_name_ is not None and not isinstance(dns_name_, (bytes, str)): - raise Exception( - f"Expected dns_name_ to be a str, received: {type(dns_name_)}" - ) - - if hardware_ is not None and not isinstance(hardware_, (bytes, str)): - raise Exception( - f"Expected hardware_ to be a str, received: {type(hardware_)}" - ) - - if has_vote_ is not None and not isinstance(has_vote_, bool): - raise Exception( - f"Expected has_vote_ to be a bool, received: {type(has_vote_)}" - ) - - if hostname_ is not None and not isinstance(hostname_, (bytes, str)): - raise Exception( - f"Expected hostname_ to be a str, received: {type(hostname_)}" - ) - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if instance_id_ is not None and not isinstance(instance_id_, (bytes, str)): - raise Exception( - f"Expected instance_id_ to be a str, received: {type(instance_id_)}" - ) - - if instance_status_ is not None and not isinstance( - instance_status_, (dict, DetailedStatus) - ): - raise Exception( - f"Expected instance_status_ to be a DetailedStatus, received: {type(instance_status_)}" - ) - - if ip_addresses_ is not None and not isinstance( - ip_addresses_, (bytes, str, list) - ): - raise Exception( - f"Expected ip_addresses_ to be a Sequence, received: {type(ip_addresses_)}" - ) - - if jobs_ is not None and not isinstance(jobs_, (bytes, str, list)): - raise Exception(f"Expected jobs_ to be a Sequence, received: {type(jobs_)}") - - if lxd_profiles_ is not None and not isinstance(lxd_profiles_, dict): - raise Exception( - f"Expected lxd_profiles_ to be a Mapping, received: {type(lxd_profiles_)}" - ) - - if modification_status_ is not None and not isinstance( - modification_status_, (dict, DetailedStatus) - ): - raise Exception( - f"Expected modification_status_ to be a DetailedStatus, received: {type(modification_status_)}" - ) - - if network_interfaces_ is not None and not isinstance( - network_interfaces_, dict - ): - raise Exception( - f"Expected network_interfaces_ to be a Mapping, received: {type(network_interfaces_)}" - ) - - if primary_controller_machine_ is not None and not isinstance( - primary_controller_machine_, bool - ): - raise Exception( - f"Expected primary_controller_machine_ to be a bool, received: {type(primary_controller_machine_)}" - ) - - if wants_vote_ is not None and not isinstance(wants_vote_, bool): - raise Exception( - f"Expected wants_vote_ to be a bool, received: {type(wants_vote_)}" - ) - - self.agent_status = agent_status_ - self.base = base_ - self.constraints = constraints_ - self.containers = containers_ - self.display_name = display_name_ - self.dns_name = dns_name_ - self.hardware = hardware_ - self.has_vote = has_vote_ - self.hostname = hostname_ - self.id_ = id__ - self.instance_id = instance_id_ - self.instance_status = instance_status_ - self.ip_addresses = ip_addresses_ - self.jobs = jobs_ - self.lxd_profiles = lxd_profiles_ - self.modification_status = modification_status_ - self.network_interfaces = network_interfaces_ - self.primary_controller_machine = primary_controller_machine_ - self.wants_vote = wants_vote_ - self.unknown_fields = unknown_fields - - -class MapResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : typing.Mapping[str, typing.Any] - """ - error_ = Error.from_json(error) if error else None - result_ = result - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, dict): - raise Exception( - f"Expected result_ to be a Mapping, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class MapResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~MapResult]""" - results_ = [MapResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class MetadataImageIds(Type): - _toSchema = {"image_ids": "image-ids"} - _toPy = {"image-ids": "image_ids"} - - def __init__(self, image_ids=None, **unknown_fields): - """image_ids : typing.Sequence[str]""" - image_ids_ = image_ids - - # Validate arguments against known Juju API types. - if image_ids_ is not None and not isinstance(image_ids_, (bytes, str, list)): - raise Exception( - f"Expected image_ids_ to be a Sequence, received: {type(image_ids_)}" - ) - - self.image_ids = image_ids_ - self.unknown_fields = unknown_fields - - -class MetadataSaveParams(Type): - _toSchema = {"metadata": "metadata"} - _toPy = {"metadata": "metadata"} - - def __init__(self, metadata=None, **unknown_fields): - """Metadata : typing.Sequence[~CloudImageMetadataList]""" - metadata_ = [CloudImageMetadataList.from_json(o) for o in metadata or []] - - # Validate arguments against known Juju API types. - if metadata_ is not None and not isinstance(metadata_, (bytes, str, list)): - raise Exception( - f"Expected metadata_ to be a Sequence, received: {type(metadata_)}" - ) - - self.metadata = metadata_ - self.unknown_fields = unknown_fields - - -class MeterStatus(Type): - _toSchema = {"color": "color", "message": "message"} - _toPy = {"color": "color", "message": "message"} - - def __init__(self, color=None, message=None, **unknown_fields): - """Color : str - message : str - """ - color_ = color - message_ = message - - # Validate arguments against known Juju API types. - if color_ is not None and not isinstance(color_, (bytes, str)): - raise Exception(f"Expected color_ to be a str, received: {type(color_)}") - - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - self.color = color_ - self.message = message_ - self.unknown_fields = unknown_fields - - -class MeterStatusParam(Type): - _toSchema = {"code": "code", "info": "info", "tag": "tag"} - _toPy = {"code": "code", "info": "info", "tag": "tag"} - - def __init__(self, code=None, info=None, tag=None, **unknown_fields): - """Code : str - info : str - tag : str - """ - code_ = code - info_ = info - tag_ = tag - - # Validate arguments against known Juju API types. - if code_ is not None and not isinstance(code_, (bytes, str)): - raise Exception(f"Expected code_ to be a str, received: {type(code_)}") - - if info_ is not None and not isinstance(info_, (bytes, str)): - raise Exception(f"Expected info_ to be a str, received: {type(info_)}") - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.code = code_ - self.info = info_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class MeterStatusParams(Type): - _toSchema = {"statues": "statues"} - _toPy = {"statues": "statues"} - - def __init__(self, statues=None, **unknown_fields): - """Statues : typing.Sequence[~MeterStatusParam]""" - statues_ = [MeterStatusParam.from_json(o) for o in statues or []] - - # Validate arguments against known Juju API types. - if statues_ is not None and not isinstance(statues_, (bytes, str, list)): - raise Exception( - f"Expected statues_ to be a Sequence, received: {type(statues_)}" - ) - - self.statues = statues_ - self.unknown_fields = unknown_fields - - -class MetricResult(Type): - _toSchema = { - "key": "key", - "labels": "labels", - "time": "time", - "unit": "unit", - "value": "value", - } - _toPy = { - "key": "key", - "labels": "labels", - "time": "time", - "unit": "unit", - "value": "value", - } - - def __init__( - self, key=None, labels=None, time=None, unit=None, value=None, **unknown_fields - ): - """Key : str - labels : typing.Mapping[str, str] - time : str - unit : str - value : str - """ - key_ = key - labels_ = labels - time_ = time - unit_ = unit - value_ = value - - # Validate arguments against known Juju API types. - if key_ is not None and not isinstance(key_, (bytes, str)): - raise Exception(f"Expected key_ to be a str, received: {type(key_)}") - - if labels_ is not None and not isinstance(labels_, dict): - raise Exception( - f"Expected labels_ to be a Mapping, received: {type(labels_)}" - ) - - if time_ is not None and not isinstance(time_, (bytes, str)): - raise Exception(f"Expected time_ to be a str, received: {type(time_)}") - - if unit_ is not None and not isinstance(unit_, (bytes, str)): - raise Exception(f"Expected unit_ to be a str, received: {type(unit_)}") - - if value_ is not None and not isinstance(value_, (bytes, str)): - raise Exception(f"Expected value_ to be a str, received: {type(value_)}") - - self.key = key_ - self.labels = labels_ - self.time = time_ - self.unit = unit_ - self.value = value_ - self.unknown_fields = unknown_fields - - -class MetricResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~EntityMetrics]""" - results_ = [EntityMetrics.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class MigrationSpec(Type): - _toSchema = {"model_tag": "model-tag", "target_info": "target-info"} - _toPy = {"model-tag": "model_tag", "target-info": "target_info"} - - def __init__(self, model_tag=None, target_info=None, **unknown_fields): - """model_tag : str - target_info : MigrationTargetInfo - """ - model_tag_ = model_tag - target_info_ = ( - MigrationTargetInfo.from_json(target_info) if target_info else None - ) - - # Validate arguments against known Juju API types. - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - if target_info_ is not None and not isinstance( - target_info_, (dict, MigrationTargetInfo) - ): - raise Exception( - f"Expected target_info_ to be a MigrationTargetInfo, received: {type(target_info_)}" - ) - - self.model_tag = model_tag_ - self.target_info = target_info_ - self.unknown_fields = unknown_fields - - -class MigrationTargetInfo(Type): - _toSchema = { - "addrs": "addrs", - "auth_tag": "auth-tag", - "ca_cert": "ca-cert", - "controller_alias": "controller-alias", - "controller_tag": "controller-tag", - "macaroons": "macaroons", - "password": "password", - } - _toPy = { - "addrs": "addrs", - "auth-tag": "auth_tag", - "ca-cert": "ca_cert", - "controller-alias": "controller_alias", - "controller-tag": "controller_tag", - "macaroons": "macaroons", - "password": "password", - } - - def __init__( - self, - addrs=None, - auth_tag=None, - ca_cert=None, - controller_alias=None, - controller_tag=None, - macaroons=None, - password=None, - **unknown_fields, - ): - """Addrs : typing.Sequence[str] - auth_tag : str - ca_cert : str - controller_alias : str - controller_tag : str - macaroons : str - password : str - """ - addrs_ = addrs - auth_tag_ = auth_tag - ca_cert_ = ca_cert - controller_alias_ = controller_alias - controller_tag_ = controller_tag - macaroons_ = macaroons - password_ = password - - # Validate arguments against known Juju API types. - if addrs_ is not None and not isinstance(addrs_, (bytes, str, list)): - raise Exception( - f"Expected addrs_ to be a Sequence, received: {type(addrs_)}" - ) - - if auth_tag_ is not None and not isinstance(auth_tag_, (bytes, str)): - raise Exception( - f"Expected auth_tag_ to be a str, received: {type(auth_tag_)}" - ) - - if ca_cert_ is not None and not isinstance(ca_cert_, (bytes, str)): - raise Exception( - f"Expected ca_cert_ to be a str, received: {type(ca_cert_)}" - ) - - if controller_alias_ is not None and not isinstance( - controller_alias_, (bytes, str) - ): - raise Exception( - f"Expected controller_alias_ to be a str, received: {type(controller_alias_)}" - ) - - if controller_tag_ is not None and not isinstance( - controller_tag_, (bytes, str) - ): - raise Exception( - f"Expected controller_tag_ to be a str, received: {type(controller_tag_)}" - ) - - if macaroons_ is not None and not isinstance(macaroons_, (bytes, str)): - raise Exception( - f"Expected macaroons_ to be a str, received: {type(macaroons_)}" - ) - - if password_ is not None and not isinstance(password_, (bytes, str)): - raise Exception( - f"Expected password_ to be a str, received: {type(password_)}" - ) - - self.addrs = addrs_ - self.auth_tag = auth_tag_ - self.ca_cert = ca_cert_ - self.controller_alias = controller_alias_ - self.controller_tag = controller_tag_ - self.macaroons = macaroons_ - self.password = password_ - self.unknown_fields = unknown_fields - - -class Model(Type): - _toSchema = { - "name": "name", - "owner_tag": "owner-tag", - "type_": "type", - "uuid": "uuid", - } - _toPy = {"name": "name", "owner-tag": "owner_tag", "type": "type_", "uuid": "uuid"} - - def __init__( - self, name=None, owner_tag=None, type_=None, uuid=None, **unknown_fields - ): - """Name : str - owner_tag : str - type_ : str - uuid : str - """ - name_ = name - owner_tag_ = owner_tag - type__ = type_ - uuid_ = uuid - - # Validate arguments against known Juju API types. - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - if uuid_ is not None and not isinstance(uuid_, (bytes, str)): - raise Exception(f"Expected uuid_ to be a str, received: {type(uuid_)}") - - self.name = name_ - self.owner_tag = owner_tag_ - self.type_ = type__ - self.uuid = uuid_ - self.unknown_fields = unknown_fields - - -class ModelAccess(Type): - _toSchema = {"access": "access", "model": "model"} - _toPy = {"access": "access", "model": "model"} - - def __init__(self, access=None, model=None, **unknown_fields): - """Access : str - model : str - """ - access_ = access - model_ = model - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if model_ is not None and not isinstance(model_, (bytes, str)): - raise Exception(f"Expected model_ to be a str, received: {type(model_)}") - - self.access = access_ - self.model = model_ - self.unknown_fields = unknown_fields - - -class ModelApplicationInfo(Type): - _toSchema = {"name": "name"} - _toPy = {"name": "name"} - - def __init__(self, name=None, **unknown_fields): - """Name : str""" - name_ = name - - # Validate arguments against known Juju API types. - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - self.name = name_ - self.unknown_fields = unknown_fields - - -class ModelBlockInfo(Type): - _toSchema = { - "blocks": "blocks", - "model_uuid": "model-uuid", - "name": "name", - "owner_tag": "owner-tag", - } - _toPy = { - "blocks": "blocks", - "model-uuid": "model_uuid", - "name": "name", - "owner-tag": "owner_tag", - } - - def __init__( - self, blocks=None, model_uuid=None, name=None, owner_tag=None, **unknown_fields - ): - """Blocks : typing.Sequence[str] - model_uuid : str - name : str - owner_tag : str - """ - blocks_ = blocks - model_uuid_ = model_uuid - name_ = name - owner_tag_ = owner_tag - - # Validate arguments against known Juju API types. - if blocks_ is not None and not isinstance(blocks_, (bytes, str, list)): - raise Exception( - f"Expected blocks_ to be a Sequence, received: {type(blocks_)}" - ) - - if model_uuid_ is not None and not isinstance(model_uuid_, (bytes, str)): - raise Exception( - f"Expected model_uuid_ to be a str, received: {type(model_uuid_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - self.blocks = blocks_ - self.model_uuid = model_uuid_ - self.name = name_ - self.owner_tag = owner_tag_ - self.unknown_fields = unknown_fields - - -class ModelBlockInfoList(Type): - _toSchema = {"models": "models"} - _toPy = {"models": "models"} - - def __init__(self, models=None, **unknown_fields): - """Models : typing.Sequence[~ModelBlockInfo]""" - models_ = [ModelBlockInfo.from_json(o) for o in models or []] - - # Validate arguments against known Juju API types. - if models_ is not None and not isinstance(models_, (bytes, str, list)): - raise Exception( - f"Expected models_ to be a Sequence, received: {type(models_)}" - ) - - self.models = models_ - self.unknown_fields = unknown_fields - - -class ModelConfigResults(Type): - _toSchema = {"config": "config"} - _toPy = {"config": "config"} - - def __init__(self, config=None, **unknown_fields): - """Config : typing.Mapping[str, ~ConfigValue]""" - config_ = {k: ConfigValue.from_json(v) for k, v in (config or dict()).items()} - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - self.config = config_ - self.unknown_fields = unknown_fields - - -class ModelCreateArgs(Type): - _toSchema = { - "cloud_tag": "cloud-tag", - "config": "config", - "credential": "credential", - "name": "name", - "owner_tag": "owner-tag", - "region": "region", - } - _toPy = { - "cloud-tag": "cloud_tag", - "config": "config", - "credential": "credential", - "name": "name", - "owner-tag": "owner_tag", - "region": "region", - } - - def __init__( - self, - cloud_tag=None, - config=None, - credential=None, - name=None, - owner_tag=None, - region=None, - **unknown_fields, - ): - """cloud_tag : str - config : typing.Mapping[str, typing.Any] - credential : str - name : str - owner_tag : str - region : str - """ - cloud_tag_ = cloud_tag - config_ = config - credential_ = credential - name_ = name - owner_tag_ = owner_tag - region_ = region - - # Validate arguments against known Juju API types. - if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): - raise Exception( - f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if credential_ is not None and not isinstance(credential_, (bytes, str)): - raise Exception( - f"Expected credential_ to be a str, received: {type(credential_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - if region_ is not None and not isinstance(region_, (bytes, str)): - raise Exception(f"Expected region_ to be a str, received: {type(region_)}") - - self.cloud_tag = cloud_tag_ - self.config = config_ - self.credential = credential_ - self.name = name_ - self.owner_tag = owner_tag_ - self.region = region_ - self.unknown_fields = unknown_fields - - -class ModelDefaultValues(Type): - _toSchema = { - "cloud_region": "cloud-region", - "cloud_tag": "cloud-tag", - "config": "config", - } - _toPy = { - "cloud-region": "cloud_region", - "cloud-tag": "cloud_tag", - "config": "config", - } - - def __init__( - self, cloud_region=None, cloud_tag=None, config=None, **unknown_fields - ): - """cloud_region : str - cloud_tag : str - config : typing.Mapping[str, typing.Any] - """ - cloud_region_ = cloud_region - cloud_tag_ = cloud_tag - config_ = config - - # Validate arguments against known Juju API types. - if cloud_region_ is not None and not isinstance(cloud_region_, (bytes, str)): - raise Exception( - f"Expected cloud_region_ to be a str, received: {type(cloud_region_)}" - ) - - if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): - raise Exception( - f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - self.cloud_region = cloud_region_ - self.cloud_tag = cloud_tag_ - self.config = config_ - self.unknown_fields = unknown_fields - - -class ModelDefaults(Type): - _toSchema = {"controller": "controller", "default": "default", "regions": "regions"} - _toPy = {"controller": "controller", "default": "default", "regions": "regions"} - - def __init__(self, controller=None, default=None, regions=None, **unknown_fields): - """Controller : Any - default : Any - regions : typing.Sequence[~RegionDefaults] - """ - controller_ = controller - default_ = default - regions_ = [RegionDefaults.from_json(o) for o in regions or []] - - # Validate arguments against known Juju API types. - if regions_ is not None and not isinstance(regions_, (bytes, str, list)): - raise Exception( - f"Expected regions_ to be a Sequence, received: {type(regions_)}" - ) - - self.controller = controller_ - self.default = default_ - self.regions = regions_ - self.unknown_fields = unknown_fields - - -class ModelDefaultsResult(Type): - _toSchema = {"config": "config", "error": "error"} - _toPy = {"config": "config", "error": "error"} - - def __init__(self, config=None, error=None, **unknown_fields): - """Config : typing.Mapping[str, ~ModelDefaults] - error : Error - """ - config_ = {k: ModelDefaults.from_json(v) for k, v in (config or dict()).items()} - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.config = config_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class ModelDefaultsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ModelDefaultsResult]""" - results_ = [ModelDefaultsResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ModelEntityCount(Type): - _toSchema = {"count": "count", "entity": "entity"} - _toPy = {"count": "count", "entity": "entity"} - - def __init__(self, count=None, entity=None, **unknown_fields): - """Count : int - entity : str - """ - count_ = count - entity_ = entity - - # Validate arguments against known Juju API types. - if count_ is not None and not isinstance(count_, int): - raise Exception(f"Expected count_ to be a int, received: {type(count_)}") - - if entity_ is not None and not isinstance(entity_, (bytes, str)): - raise Exception(f"Expected entity_ to be a str, received: {type(entity_)}") - - self.count = count_ - self.entity = entity_ - self.unknown_fields = unknown_fields - - -class ModelFilesystemInfo(Type): - _toSchema = { - "detachable": "detachable", - "id_": "id", - "message": "message", - "provider_id": "provider-id", - "status": "status", - } - _toPy = { - "detachable": "detachable", - "id": "id_", - "message": "message", - "provider-id": "provider_id", - "status": "status", - } - - def __init__( - self, - detachable=None, - id_=None, - message=None, - provider_id=None, - status=None, - **unknown_fields, - ): - """Detachable : bool - id_ : str - message : str - provider_id : str - status : str - """ - detachable_ = detachable - id__ = id_ - message_ = message - provider_id_ = provider_id - status_ = status - - # Validate arguments against known Juju API types. - if detachable_ is not None and not isinstance(detachable_, bool): - raise Exception( - f"Expected detachable_ to be a bool, received: {type(detachable_)}" - ) - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - self.detachable = detachable_ - self.id_ = id__ - self.message = message_ - self.provider_id = provider_id_ - self.status = status_ - self.unknown_fields = unknown_fields - - -class ModelInfo(Type): - _toSchema = { - "agent_version": "agent-version", - "cloud_credential_tag": "cloud-credential-tag", - "cloud_credential_validity": "cloud-credential-validity", - "cloud_region": "cloud-region", - "cloud_tag": "cloud-tag", - "controller_uuid": "controller-uuid", - "default_base": "default-base", - "default_series": "default-series", - "is_controller": "is-controller", - "life": "life", - "machines": "machines", - "migration": "migration", - "name": "name", - "owner_tag": "owner-tag", - "provider_type": "provider-type", - "secret_backends": "secret-backends", - "sla": "sla", - "status": "status", - "supported_features": "supported-features", - "type_": "type", - "users": "users", - "uuid": "uuid", - } - _toPy = { - "agent-version": "agent_version", - "cloud-credential-tag": "cloud_credential_tag", - "cloud-credential-validity": "cloud_credential_validity", - "cloud-region": "cloud_region", - "cloud-tag": "cloud_tag", - "controller-uuid": "controller_uuid", - "default-base": "default_base", - "default-series": "default_series", - "is-controller": "is_controller", - "life": "life", - "machines": "machines", - "migration": "migration", - "name": "name", - "owner-tag": "owner_tag", - "provider-type": "provider_type", - "secret-backends": "secret_backends", - "sla": "sla", - "status": "status", - "supported-features": "supported_features", - "type": "type_", - "users": "users", - "uuid": "uuid", - } - - def __init__( - self, - agent_version=None, - cloud_credential_tag=None, - cloud_credential_validity=None, - cloud_region=None, - cloud_tag=None, - controller_uuid=None, - default_base=None, - default_series=None, - is_controller=None, - life=None, - machines=None, - migration=None, - name=None, - owner_tag=None, - provider_type=None, - secret_backends=None, - sla=None, - status=None, - supported_features=None, - type_=None, - users=None, - uuid=None, - **unknown_fields, - ): - """agent_version : Number - cloud_credential_tag : str - cloud_credential_validity : bool - cloud_region : str - cloud_tag : str - controller_uuid : str - default_base : str - default_series : str - is_controller : bool - life : str - machines : typing.Sequence[~ModelMachineInfo] - migration : ModelMigrationStatus - name : str - owner_tag : str - provider_type : str - secret_backends : typing.Sequence[~SecretBackendResult] - sla : ModelSLAInfo - status : EntityStatus - supported_features : typing.Sequence[~SupportedFeature] - type_ : str - users : typing.Sequence[~ModelUserInfo] - uuid : str - """ - agent_version_ = Number.from_json(agent_version) if agent_version else None - cloud_credential_tag_ = cloud_credential_tag - cloud_credential_validity_ = cloud_credential_validity - cloud_region_ = cloud_region - cloud_tag_ = cloud_tag - controller_uuid_ = controller_uuid - default_base_ = default_base - default_series_ = default_series - is_controller_ = is_controller - life_ = life - machines_ = [ModelMachineInfo.from_json(o) for o in machines or []] - migration_ = ModelMigrationStatus.from_json(migration) if migration else None - name_ = name - owner_tag_ = owner_tag - provider_type_ = provider_type - secret_backends_ = [ - SecretBackendResult.from_json(o) for o in secret_backends or [] - ] - sla_ = ModelSLAInfo.from_json(sla) if sla else None - status_ = EntityStatus.from_json(status) if status else None - supported_features_ = [ - SupportedFeature.from_json(o) for o in supported_features or [] - ] - type__ = type_ - users_ = [ModelUserInfo.from_json(o) for o in users or []] - uuid_ = uuid - - # Validate arguments against known Juju API types. - if agent_version_ is not None and not isinstance( - agent_version_, (dict, Number) - ): - raise Exception( - f"Expected agent_version_ to be a Number, received: {type(agent_version_)}" - ) - - if cloud_credential_tag_ is not None and not isinstance( - cloud_credential_tag_, (bytes, str) - ): - raise Exception( - f"Expected cloud_credential_tag_ to be a str, received: {type(cloud_credential_tag_)}" - ) - - if cloud_credential_validity_ is not None and not isinstance( - cloud_credential_validity_, bool - ): - raise Exception( - f"Expected cloud_credential_validity_ to be a bool, received: {type(cloud_credential_validity_)}" - ) - - if cloud_region_ is not None and not isinstance(cloud_region_, (bytes, str)): - raise Exception( - f"Expected cloud_region_ to be a str, received: {type(cloud_region_)}" - ) - - if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): - raise Exception( - f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" - ) - - if controller_uuid_ is not None and not isinstance( - controller_uuid_, (bytes, str) - ): - raise Exception( - f"Expected controller_uuid_ to be a str, received: {type(controller_uuid_)}" - ) - - if default_base_ is not None and not isinstance(default_base_, (bytes, str)): - raise Exception( - f"Expected default_base_ to be a str, received: {type(default_base_)}" - ) - - if default_series_ is not None and not isinstance( - default_series_, (bytes, str) - ): - raise Exception( - f"Expected default_series_ to be a str, received: {type(default_series_)}" - ) - - if is_controller_ is not None and not isinstance(is_controller_, bool): - raise Exception( - f"Expected is_controller_ to be a bool, received: {type(is_controller_)}" - ) - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if machines_ is not None and not isinstance(machines_, (bytes, str, list)): - raise Exception( - f"Expected machines_ to be a Sequence, received: {type(machines_)}" - ) - - if migration_ is not None and not isinstance( - migration_, (dict, ModelMigrationStatus) - ): - raise Exception( - f"Expected migration_ to be a ModelMigrationStatus, received: {type(migration_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - if provider_type_ is not None and not isinstance(provider_type_, (bytes, str)): - raise Exception( - f"Expected provider_type_ to be a str, received: {type(provider_type_)}" - ) - - if secret_backends_ is not None and not isinstance( - secret_backends_, (bytes, str, list) - ): - raise Exception( - f"Expected secret_backends_ to be a Sequence, received: {type(secret_backends_)}" - ) - - if sla_ is not None and not isinstance(sla_, (dict, ModelSLAInfo)): - raise Exception( - f"Expected sla_ to be a ModelSLAInfo, received: {type(sla_)}" - ) - - if status_ is not None and not isinstance(status_, (dict, EntityStatus)): - raise Exception( - f"Expected status_ to be a EntityStatus, received: {type(status_)}" - ) - - if supported_features_ is not None and not isinstance( - supported_features_, (bytes, str, list) - ): - raise Exception( - f"Expected supported_features_ to be a Sequence, received: {type(supported_features_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - if users_ is not None and not isinstance(users_, (bytes, str, list)): - raise Exception( - f"Expected users_ to be a Sequence, received: {type(users_)}" - ) - - if uuid_ is not None and not isinstance(uuid_, (bytes, str)): - raise Exception(f"Expected uuid_ to be a str, received: {type(uuid_)}") - - self.agent_version = agent_version_ - self.cloud_credential_tag = cloud_credential_tag_ - self.cloud_credential_validity = cloud_credential_validity_ - self.cloud_region = cloud_region_ - self.cloud_tag = cloud_tag_ - self.controller_uuid = controller_uuid_ - self.default_base = default_base_ - self.default_series = default_series_ - self.is_controller = is_controller_ - self.life = life_ - self.machines = machines_ - self.migration = migration_ - self.name = name_ - self.owner_tag = owner_tag_ - self.provider_type = provider_type_ - self.secret_backends = secret_backends_ - self.sla = sla_ - self.status = status_ - self.supported_features = supported_features_ - self.type_ = type__ - self.users = users_ - self.uuid = uuid_ - self.unknown_fields = unknown_fields - - -class ModelInfoResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : ModelInfo - """ - error_ = Error.from_json(error) if error else None - result_ = ModelInfo.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, ModelInfo)): - raise Exception( - f"Expected result_ to be a ModelInfo, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class ModelInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ModelInfoResult]""" - results_ = [ModelInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ModelInstanceTypesConstraint(Type): - _toSchema = {"value": "value"} - _toPy = {"value": "value"} - - def __init__(self, value=None, **unknown_fields): - """Value : Value""" - value_ = Value.from_json(value) if value else None - - # Validate arguments against known Juju API types. - if value_ is not None and not isinstance(value_, (dict, Value)): - raise Exception(f"Expected value_ to be a Value, received: {type(value_)}") - - self.value = value_ - self.unknown_fields = unknown_fields - - -class ModelInstanceTypesConstraints(Type): - _toSchema = {"constraints": "constraints"} - _toPy = {"constraints": "constraints"} - - def __init__(self, constraints=None, **unknown_fields): - """Constraints : typing.Sequence[~ModelInstanceTypesConstraint]""" - constraints_ = [ - ModelInstanceTypesConstraint.from_json(o) for o in constraints or [] - ] - - # Validate arguments against known Juju API types. - if constraints_ is not None and not isinstance( - constraints_, (bytes, str, list) - ): - raise Exception( - f"Expected constraints_ to be a Sequence, received: {type(constraints_)}" - ) - - self.constraints = constraints_ - self.unknown_fields = unknown_fields - - -class ModelMachineInfo(Type): - _toSchema = { - "display_name": "display-name", - "ha_primary": "ha-primary", - "hardware": "hardware", - "has_vote": "has-vote", - "id_": "id", - "instance_id": "instance-id", - "message": "message", - "status": "status", - "wants_vote": "wants-vote", - } - _toPy = { - "display-name": "display_name", - "ha-primary": "ha_primary", - "hardware": "hardware", - "has-vote": "has_vote", - "id": "id_", - "instance-id": "instance_id", - "message": "message", - "status": "status", - "wants-vote": "wants_vote", - } - - def __init__( - self, - display_name=None, - ha_primary=None, - hardware=None, - has_vote=None, - id_=None, - instance_id=None, - message=None, - status=None, - wants_vote=None, - **unknown_fields, - ): - """display_name : str - ha_primary : bool - hardware : MachineHardware - has_vote : bool - id_ : str - instance_id : str - message : str - status : str - wants_vote : bool - """ - display_name_ = display_name - ha_primary_ = ha_primary - hardware_ = MachineHardware.from_json(hardware) if hardware else None - has_vote_ = has_vote - id__ = id_ - instance_id_ = instance_id - message_ = message - status_ = status - wants_vote_ = wants_vote - - # Validate arguments against known Juju API types. - if display_name_ is not None and not isinstance(display_name_, (bytes, str)): - raise Exception( - f"Expected display_name_ to be a str, received: {type(display_name_)}" - ) - - if ha_primary_ is not None and not isinstance(ha_primary_, bool): - raise Exception( - f"Expected ha_primary_ to be a bool, received: {type(ha_primary_)}" - ) - - if hardware_ is not None and not isinstance(hardware_, (dict, MachineHardware)): - raise Exception( - f"Expected hardware_ to be a MachineHardware, received: {type(hardware_)}" - ) - - if has_vote_ is not None and not isinstance(has_vote_, bool): - raise Exception( - f"Expected has_vote_ to be a bool, received: {type(has_vote_)}" - ) - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if instance_id_ is not None and not isinstance(instance_id_, (bytes, str)): - raise Exception( - f"Expected instance_id_ to be a str, received: {type(instance_id_)}" - ) - - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - if wants_vote_ is not None and not isinstance(wants_vote_, bool): - raise Exception( - f"Expected wants_vote_ to be a bool, received: {type(wants_vote_)}" - ) - - self.display_name = display_name_ - self.ha_primary = ha_primary_ - self.hardware = hardware_ - self.has_vote = has_vote_ - self.id_ = id__ - self.instance_id = instance_id_ - self.message = message_ - self.status = status_ - self.wants_vote = wants_vote_ - self.unknown_fields = unknown_fields - - -class ModelMigrationStatus(Type): - _toSchema = {"end": "end", "start": "start", "status": "status"} - _toPy = {"end": "end", "start": "start", "status": "status"} - - def __init__(self, end=None, start=None, status=None, **unknown_fields): - """End : str - start : str - status : str - """ - end_ = end - start_ = start - status_ = status - - # Validate arguments against known Juju API types. - if end_ is not None and not isinstance(end_, (bytes, str)): - raise Exception(f"Expected end_ to be a str, received: {type(end_)}") - - if start_ is not None and not isinstance(start_, (bytes, str)): - raise Exception(f"Expected start_ to be a str, received: {type(start_)}") - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - self.end = end_ - self.start = start_ - self.status = status_ - self.unknown_fields = unknown_fields - - -class ModelParam(Type): - _toSchema = {"model_tag": "model-tag"} - _toPy = {"model-tag": "model_tag"} - - def __init__(self, model_tag=None, **unknown_fields): - """model_tag : str""" - model_tag_ = model_tag - - # Validate arguments against known Juju API types. - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - self.model_tag = model_tag_ - self.unknown_fields = unknown_fields - - -class ModelSLA(Type): - _toSchema = { - "creds": "creds", - "level": "level", - "modelslainfo": "ModelSLAInfo", - "owner": "owner", - } - _toPy = { - "ModelSLAInfo": "modelslainfo", - "creds": "creds", - "level": "level", - "owner": "owner", - } - - def __init__( - self, modelslainfo=None, creds=None, level=None, owner=None, **unknown_fields - ): - """Modelslainfo : ModelSLAInfo - creds : typing.Sequence[int] - level : str - owner : str - """ - modelslainfo_ = ModelSLAInfo.from_json(modelslainfo) if modelslainfo else None - creds_ = creds - level_ = level - owner_ = owner - - # Validate arguments against known Juju API types. - if modelslainfo_ is not None and not isinstance( - modelslainfo_, (dict, ModelSLAInfo) - ): - raise Exception( - f"Expected modelslainfo_ to be a ModelSLAInfo, received: {type(modelslainfo_)}" - ) - - if creds_ is not None and not isinstance(creds_, (bytes, str, list)): - raise Exception( - f"Expected creds_ to be a Sequence, received: {type(creds_)}" - ) - - if level_ is not None and not isinstance(level_, (bytes, str)): - raise Exception(f"Expected level_ to be a str, received: {type(level_)}") - - if owner_ is not None and not isinstance(owner_, (bytes, str)): - raise Exception(f"Expected owner_ to be a str, received: {type(owner_)}") - - self.modelslainfo = modelslainfo_ - self.creds = creds_ - self.level = level_ - self.owner = owner_ - self.unknown_fields = unknown_fields - - -class ModelSLAInfo(Type): - _toSchema = {"level": "level", "owner": "owner"} - _toPy = {"level": "level", "owner": "owner"} - - def __init__(self, level=None, owner=None, **unknown_fields): - """Level : str - owner : str - """ - level_ = level - owner_ = owner - - # Validate arguments against known Juju API types. - if level_ is not None and not isinstance(level_, (bytes, str)): - raise Exception(f"Expected level_ to be a str, received: {type(level_)}") - - if owner_ is not None and not isinstance(owner_, (bytes, str)): - raise Exception(f"Expected owner_ to be a str, received: {type(owner_)}") - - self.level = level_ - self.owner = owner_ - self.unknown_fields = unknown_fields - - -class ModelSequencesResult(Type): - _toSchema = {"sequences": "sequences"} - _toPy = {"sequences": "sequences"} - - def __init__(self, sequences=None, **unknown_fields): - """Sequences : typing.Mapping[str, int]""" - sequences_ = sequences - - # Validate arguments against known Juju API types. - if sequences_ is not None and not isinstance(sequences_, dict): - raise Exception( - f"Expected sequences_ to be a Mapping, received: {type(sequences_)}" - ) - - self.sequences = sequences_ - self.unknown_fields = unknown_fields - - -class ModelSet(Type): - _toSchema = {"config": "config"} - _toPy = {"config": "config"} - - def __init__(self, config=None, **unknown_fields): - """Config : typing.Mapping[str, typing.Any]""" - config_ = config - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - self.config = config_ - self.unknown_fields = unknown_fields - - -class ModelStatus(Type): - _toSchema = { - "application_count": "application-count", - "applications": "applications", - "error": "error", - "filesystems": "filesystems", - "hosted_machine_count": "hosted-machine-count", - "life": "life", - "machines": "machines", - "model_tag": "model-tag", - "owner_tag": "owner-tag", - "type_": "type", - "unit_count": "unit-count", - "volumes": "volumes", - } - _toPy = { - "application-count": "application_count", - "applications": "applications", - "error": "error", - "filesystems": "filesystems", - "hosted-machine-count": "hosted_machine_count", - "life": "life", - "machines": "machines", - "model-tag": "model_tag", - "owner-tag": "owner_tag", - "type": "type_", - "unit-count": "unit_count", - "volumes": "volumes", - } - - def __init__( - self, - application_count=None, - applications=None, - error=None, - filesystems=None, - hosted_machine_count=None, - life=None, - machines=None, - model_tag=None, - owner_tag=None, - type_=None, - unit_count=None, - volumes=None, - **unknown_fields, - ): - """application_count : int - applications : typing.Sequence[~ModelApplicationInfo] - error : Error - filesystems : typing.Sequence[~ModelFilesystemInfo] - hosted_machine_count : int - life : str - machines : typing.Sequence[~ModelMachineInfo] - model_tag : str - owner_tag : str - type_ : str - unit_count : int - volumes : typing.Sequence[~ModelVolumeInfo] - """ - application_count_ = application_count - applications_ = [ModelApplicationInfo.from_json(o) for o in applications or []] - error_ = Error.from_json(error) if error else None - filesystems_ = [ModelFilesystemInfo.from_json(o) for o in filesystems or []] - hosted_machine_count_ = hosted_machine_count - life_ = life - machines_ = [ModelMachineInfo.from_json(o) for o in machines or []] - model_tag_ = model_tag - owner_tag_ = owner_tag - type__ = type_ - unit_count_ = unit_count - volumes_ = [ModelVolumeInfo.from_json(o) for o in volumes or []] - - # Validate arguments against known Juju API types. - if application_count_ is not None and not isinstance(application_count_, int): - raise Exception( - f"Expected application_count_ to be a int, received: {type(application_count_)}" - ) - - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if filesystems_ is not None and not isinstance( - filesystems_, (bytes, str, list) - ): - raise Exception( - f"Expected filesystems_ to be a Sequence, received: {type(filesystems_)}" - ) - - if hosted_machine_count_ is not None and not isinstance( - hosted_machine_count_, int - ): - raise Exception( - f"Expected hosted_machine_count_ to be a int, received: {type(hosted_machine_count_)}" - ) - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if machines_ is not None and not isinstance(machines_, (bytes, str, list)): - raise Exception( - f"Expected machines_ to be a Sequence, received: {type(machines_)}" - ) - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - if unit_count_ is not None and not isinstance(unit_count_, int): - raise Exception( - f"Expected unit_count_ to be a int, received: {type(unit_count_)}" - ) - - if volumes_ is not None and not isinstance(volumes_, (bytes, str, list)): - raise Exception( - f"Expected volumes_ to be a Sequence, received: {type(volumes_)}" - ) - - self.application_count = application_count_ - self.applications = applications_ - self.error = error_ - self.filesystems = filesystems_ - self.hosted_machine_count = hosted_machine_count_ - self.life = life_ - self.machines = machines_ - self.model_tag = model_tag_ - self.owner_tag = owner_tag_ - self.type_ = type__ - self.unit_count = unit_count_ - self.volumes = volumes_ - self.unknown_fields = unknown_fields - - -class ModelStatusInfo(Type): - _toSchema = { - "available_version": "available-version", - "cloud_tag": "cloud-tag", - "meter_status": "meter-status", - "model_status": "model-status", - "name": "name", - "region": "region", - "sla": "sla", - "type_": "type", - "version": "version", - } - _toPy = { - "available-version": "available_version", - "cloud-tag": "cloud_tag", - "meter-status": "meter_status", - "model-status": "model_status", - "name": "name", - "region": "region", - "sla": "sla", - "type": "type_", - "version": "version", - } - - def __init__( - self, - available_version=None, - cloud_tag=None, - meter_status=None, - model_status=None, - name=None, - region=None, - sla=None, - type_=None, - version=None, - **unknown_fields, - ): - """available_version : str - cloud_tag : str - meter_status : MeterStatus - model_status : DetailedStatus - name : str - region : str - sla : str - type_ : str - version : str - """ - available_version_ = available_version - cloud_tag_ = cloud_tag - meter_status_ = MeterStatus.from_json(meter_status) if meter_status else None - model_status_ = DetailedStatus.from_json(model_status) if model_status else None - name_ = name - region_ = region - sla_ = sla - type__ = type_ - version_ = version - - # Validate arguments against known Juju API types. - if available_version_ is not None and not isinstance( - available_version_, (bytes, str) - ): - raise Exception( - f"Expected available_version_ to be a str, received: {type(available_version_)}" - ) - - if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): - raise Exception( - f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" - ) - - if meter_status_ is not None and not isinstance( - meter_status_, (dict, MeterStatus) - ): - raise Exception( - f"Expected meter_status_ to be a MeterStatus, received: {type(meter_status_)}" - ) - - if model_status_ is not None and not isinstance( - model_status_, (dict, DetailedStatus) - ): - raise Exception( - f"Expected model_status_ to be a DetailedStatus, received: {type(model_status_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if region_ is not None and not isinstance(region_, (bytes, str)): - raise Exception(f"Expected region_ to be a str, received: {type(region_)}") - - if sla_ is not None and not isinstance(sla_, (bytes, str)): - raise Exception(f"Expected sla_ to be a str, received: {type(sla_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - if version_ is not None and not isinstance(version_, (bytes, str)): - raise Exception( - f"Expected version_ to be a str, received: {type(version_)}" - ) - - self.available_version = available_version_ - self.cloud_tag = cloud_tag_ - self.meter_status = meter_status_ - self.model_status = model_status_ - self.name = name_ - self.region = region_ - self.sla = sla_ - self.type_ = type__ - self.version = version_ - self.unknown_fields = unknown_fields - - -class ModelStatusResults(Type): - _toSchema = {"models": "models"} - _toPy = {"models": "models"} - - def __init__(self, models=None, **unknown_fields): - """Models : typing.Sequence[~ModelStatus]""" - models_ = [ModelStatus.from_json(o) for o in models or []] - - # Validate arguments against known Juju API types. - if models_ is not None and not isinstance(models_, (bytes, str, list)): - raise Exception( - f"Expected models_ to be a Sequence, received: {type(models_)}" - ) - - self.models = models_ - self.unknown_fields = unknown_fields - - -class ModelSummariesRequest(Type): - _toSchema = {"all_": "all", "user_tag": "user-tag"} - _toPy = {"all": "all_", "user-tag": "user_tag"} - - def __init__(self, all_=None, user_tag=None, **unknown_fields): - """all_ : bool - user_tag : str - """ - all__ = all_ - user_tag_ = user_tag - - # Validate arguments against known Juju API types. - if all__ is not None and not isinstance(all__, bool): - raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") - - if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): - raise Exception( - f"Expected user_tag_ to be a str, received: {type(user_tag_)}" - ) - - self.all_ = all__ - self.user_tag = user_tag_ - self.unknown_fields = unknown_fields - - -class ModelSummary(Type): - _toSchema = { - "agent_version": "agent-version", - "cloud_credential_tag": "cloud-credential-tag", - "cloud_region": "cloud-region", - "cloud_tag": "cloud-tag", - "controller_uuid": "controller-uuid", - "counts": "counts", - "default_series": "default-series", - "is_controller": "is-controller", - "last_connection": "last-connection", - "life": "life", - "migration": "migration", - "name": "name", - "owner_tag": "owner-tag", - "provider_type": "provider-type", - "sla": "sla", - "status": "status", - "type_": "type", - "user_access": "user-access", - "uuid": "uuid", - } - _toPy = { - "agent-version": "agent_version", - "cloud-credential-tag": "cloud_credential_tag", - "cloud-region": "cloud_region", - "cloud-tag": "cloud_tag", - "controller-uuid": "controller_uuid", - "counts": "counts", - "default-series": "default_series", - "is-controller": "is_controller", - "last-connection": "last_connection", - "life": "life", - "migration": "migration", - "name": "name", - "owner-tag": "owner_tag", - "provider-type": "provider_type", - "sla": "sla", - "status": "status", - "type": "type_", - "user-access": "user_access", - "uuid": "uuid", - } - - def __init__( - self, - agent_version=None, - cloud_credential_tag=None, - cloud_region=None, - cloud_tag=None, - controller_uuid=None, - counts=None, - default_series=None, - is_controller=None, - last_connection=None, - life=None, - migration=None, - name=None, - owner_tag=None, - provider_type=None, - sla=None, - status=None, - type_=None, - user_access=None, - uuid=None, - **unknown_fields, - ): - """agent_version : Number - cloud_credential_tag : str - cloud_region : str - cloud_tag : str - controller_uuid : str - counts : typing.Sequence[~ModelEntityCount] - default_series : str - is_controller : bool - last_connection : str - life : str - migration : ModelMigrationStatus - name : str - owner_tag : str - provider_type : str - sla : ModelSLAInfo - status : EntityStatus - type_ : str - user_access : str - uuid : str - """ - agent_version_ = Number.from_json(agent_version) if agent_version else None - cloud_credential_tag_ = cloud_credential_tag - cloud_region_ = cloud_region - cloud_tag_ = cloud_tag - controller_uuid_ = controller_uuid - counts_ = [ModelEntityCount.from_json(o) for o in counts or []] - default_series_ = default_series - is_controller_ = is_controller - last_connection_ = last_connection - life_ = life - migration_ = ModelMigrationStatus.from_json(migration) if migration else None - name_ = name - owner_tag_ = owner_tag - provider_type_ = provider_type - sla_ = ModelSLAInfo.from_json(sla) if sla else None - status_ = EntityStatus.from_json(status) if status else None - type__ = type_ - user_access_ = user_access - uuid_ = uuid - - # Validate arguments against known Juju API types. - if agent_version_ is not None and not isinstance( - agent_version_, (dict, Number) - ): - raise Exception( - f"Expected agent_version_ to be a Number, received: {type(agent_version_)}" - ) - - if cloud_credential_tag_ is not None and not isinstance( - cloud_credential_tag_, (bytes, str) - ): - raise Exception( - f"Expected cloud_credential_tag_ to be a str, received: {type(cloud_credential_tag_)}" - ) - - if cloud_region_ is not None and not isinstance(cloud_region_, (bytes, str)): - raise Exception( - f"Expected cloud_region_ to be a str, received: {type(cloud_region_)}" - ) - - if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): - raise Exception( - f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" - ) - - if controller_uuid_ is not None and not isinstance( - controller_uuid_, (bytes, str) - ): - raise Exception( - f"Expected controller_uuid_ to be a str, received: {type(controller_uuid_)}" - ) - - if counts_ is not None and not isinstance(counts_, (bytes, str, list)): - raise Exception( - f"Expected counts_ to be a Sequence, received: {type(counts_)}" - ) - - if default_series_ is not None and not isinstance( - default_series_, (bytes, str) - ): - raise Exception( - f"Expected default_series_ to be a str, received: {type(default_series_)}" - ) - - if is_controller_ is not None and not isinstance(is_controller_, bool): - raise Exception( - f"Expected is_controller_ to be a bool, received: {type(is_controller_)}" - ) - - if last_connection_ is not None and not isinstance( - last_connection_, (bytes, str) - ): - raise Exception( - f"Expected last_connection_ to be a str, received: {type(last_connection_)}" - ) - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if migration_ is not None and not isinstance( - migration_, (dict, ModelMigrationStatus) - ): - raise Exception( - f"Expected migration_ to be a ModelMigrationStatus, received: {type(migration_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - if provider_type_ is not None and not isinstance(provider_type_, (bytes, str)): - raise Exception( - f"Expected provider_type_ to be a str, received: {type(provider_type_)}" - ) - - if sla_ is not None and not isinstance(sla_, (dict, ModelSLAInfo)): - raise Exception( - f"Expected sla_ to be a ModelSLAInfo, received: {type(sla_)}" - ) - - if status_ is not None and not isinstance(status_, (dict, EntityStatus)): - raise Exception( - f"Expected status_ to be a EntityStatus, received: {type(status_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - if user_access_ is not None and not isinstance(user_access_, (bytes, str)): - raise Exception( - f"Expected user_access_ to be a str, received: {type(user_access_)}" - ) - - if uuid_ is not None and not isinstance(uuid_, (bytes, str)): - raise Exception(f"Expected uuid_ to be a str, received: {type(uuid_)}") - - self.agent_version = agent_version_ - self.cloud_credential_tag = cloud_credential_tag_ - self.cloud_region = cloud_region_ - self.cloud_tag = cloud_tag_ - self.controller_uuid = controller_uuid_ - self.counts = counts_ - self.default_series = default_series_ - self.is_controller = is_controller_ - self.last_connection = last_connection_ - self.life = life_ - self.migration = migration_ - self.name = name_ - self.owner_tag = owner_tag_ - self.provider_type = provider_type_ - self.sla = sla_ - self.status = status_ - self.type_ = type__ - self.user_access = user_access_ - self.uuid = uuid_ - self.unknown_fields = unknown_fields - - -class ModelSummaryResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : ModelSummary - """ - error_ = Error.from_json(error) if error else None - result_ = ModelSummary.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, ModelSummary)): - raise Exception( - f"Expected result_ to be a ModelSummary, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class ModelSummaryResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ModelSummaryResult]""" - results_ = [ModelSummaryResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ModelTag(Type): - _toSchema = {} - _toPy = {} - - def __init__(self, **unknown_fields): - """ """ - self.unknown_fields = unknown_fields - - -class ModelUnset(Type): - _toSchema = {"keys": "keys"} - _toPy = {"keys": "keys"} - - def __init__(self, keys=None, **unknown_fields): - """Keys : typing.Sequence[str]""" - keys_ = keys - - # Validate arguments against known Juju API types. - if keys_ is not None and not isinstance(keys_, (bytes, str, list)): - raise Exception(f"Expected keys_ to be a Sequence, received: {type(keys_)}") - - self.keys = keys_ - self.unknown_fields = unknown_fields - - -class ModelUnsetKeys(Type): - _toSchema = { - "cloud_region": "cloud-region", - "cloud_tag": "cloud-tag", - "keys": "keys", - } - _toPy = {"cloud-region": "cloud_region", "cloud-tag": "cloud_tag", "keys": "keys"} - - def __init__(self, cloud_region=None, cloud_tag=None, keys=None, **unknown_fields): - """cloud_region : str - cloud_tag : str - keys : typing.Sequence[str] - """ - cloud_region_ = cloud_region - cloud_tag_ = cloud_tag - keys_ = keys - - # Validate arguments against known Juju API types. - if cloud_region_ is not None and not isinstance(cloud_region_, (bytes, str)): - raise Exception( - f"Expected cloud_region_ to be a str, received: {type(cloud_region_)}" - ) - - if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): - raise Exception( - f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" - ) - - if keys_ is not None and not isinstance(keys_, (bytes, str, list)): - raise Exception(f"Expected keys_ to be a Sequence, received: {type(keys_)}") - - self.cloud_region = cloud_region_ - self.cloud_tag = cloud_tag_ - self.keys = keys_ - self.unknown_fields = unknown_fields - - -class ModelUserInfo(Type): - _toSchema = { - "access": "access", - "display_name": "display-name", - "last_connection": "last-connection", - "model_tag": "model-tag", - "user": "user", - } - _toPy = { - "access": "access", - "display-name": "display_name", - "last-connection": "last_connection", - "model-tag": "model_tag", - "user": "user", - } - - def __init__( - self, - access=None, - display_name=None, - last_connection=None, - model_tag=None, - user=None, - **unknown_fields, - ): - """Access : str - display_name : str - last_connection : str - model_tag : str - user : str - """ - access_ = access - display_name_ = display_name - last_connection_ = last_connection - model_tag_ = model_tag - user_ = user - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if display_name_ is not None and not isinstance(display_name_, (bytes, str)): - raise Exception( - f"Expected display_name_ to be a str, received: {type(display_name_)}" - ) - - if last_connection_ is not None and not isinstance( - last_connection_, (bytes, str) - ): - raise Exception( - f"Expected last_connection_ to be a str, received: {type(last_connection_)}" - ) - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - if user_ is not None and not isinstance(user_, (bytes, str)): - raise Exception(f"Expected user_ to be a str, received: {type(user_)}") - - self.access = access_ - self.display_name = display_name_ - self.last_connection = last_connection_ - self.model_tag = model_tag_ - self.user = user_ - self.unknown_fields = unknown_fields - - -class ModelUserInfoResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : ModelUserInfo - """ - error_ = Error.from_json(error) if error else None - result_ = ModelUserInfo.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, ModelUserInfo)): - raise Exception( - f"Expected result_ to be a ModelUserInfo, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class ModelUserInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ModelUserInfoResult]""" - results_ = [ModelUserInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ModelVolumeInfo(Type): - _toSchema = { - "detachable": "detachable", - "id_": "id", - "message": "message", - "provider_id": "provider-id", - "status": "status", - } - _toPy = { - "detachable": "detachable", - "id": "id_", - "message": "message", - "provider-id": "provider_id", - "status": "status", - } - - def __init__( - self, - detachable=None, - id_=None, - message=None, - provider_id=None, - status=None, - **unknown_fields, - ): - """Detachable : bool - id_ : str - message : str - provider_id : str - status : str - """ - detachable_ = detachable - id__ = id_ - message_ = message - provider_id_ = provider_id - status_ = status - - # Validate arguments against known Juju API types. - if detachable_ is not None and not isinstance(detachable_, bool): - raise Exception( - f"Expected detachable_ to be a bool, received: {type(detachable_)}" - ) - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - self.detachable = detachable_ - self.id_ = id__ - self.message = message_ - self.provider_id = provider_id_ - self.status = status_ - self.unknown_fields = unknown_fields - - -class ModifyCloudAccess(Type): - _toSchema = { - "access": "access", - "action": "action", - "cloud_tag": "cloud-tag", - "user_tag": "user-tag", - } - _toPy = { - "access": "access", - "action": "action", - "cloud-tag": "cloud_tag", - "user-tag": "user_tag", - } - - def __init__( - self, access=None, action=None, cloud_tag=None, user_tag=None, **unknown_fields - ): - """Access : str - action : str - cloud_tag : str - user_tag : str - """ - access_ = access - action_ = action - cloud_tag_ = cloud_tag - user_tag_ = user_tag - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if action_ is not None and not isinstance(action_, (bytes, str)): - raise Exception(f"Expected action_ to be a str, received: {type(action_)}") - - if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): - raise Exception( - f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" - ) - - if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): - raise Exception( - f"Expected user_tag_ to be a str, received: {type(user_tag_)}" - ) - - self.access = access_ - self.action = action_ - self.cloud_tag = cloud_tag_ - self.user_tag = user_tag_ - self.unknown_fields = unknown_fields - - -class ModifyCloudAccessRequest(Type): - _toSchema = {"changes": "changes"} - _toPy = {"changes": "changes"} - - def __init__(self, changes=None, **unknown_fields): - """Changes : typing.Sequence[~ModifyCloudAccess]""" - changes_ = [ModifyCloudAccess.from_json(o) for o in changes or []] - - # Validate arguments against known Juju API types. - if changes_ is not None and not isinstance(changes_, (bytes, str, list)): - raise Exception( - f"Expected changes_ to be a Sequence, received: {type(changes_)}" - ) - - self.changes = changes_ - self.unknown_fields = unknown_fields - - -class ModifyControllerAccess(Type): - _toSchema = {"access": "access", "action": "action", "user_tag": "user-tag"} - _toPy = {"access": "access", "action": "action", "user-tag": "user_tag"} - - def __init__(self, access=None, action=None, user_tag=None, **unknown_fields): - """Access : str - action : str - user_tag : str - """ - access_ = access - action_ = action - user_tag_ = user_tag - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if action_ is not None and not isinstance(action_, (bytes, str)): - raise Exception(f"Expected action_ to be a str, received: {type(action_)}") - - if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): - raise Exception( - f"Expected user_tag_ to be a str, received: {type(user_tag_)}" - ) - - self.access = access_ - self.action = action_ - self.user_tag = user_tag_ - self.unknown_fields = unknown_fields - - -class ModifyControllerAccessRequest(Type): - _toSchema = {"changes": "changes"} - _toPy = {"changes": "changes"} - - def __init__(self, changes=None, **unknown_fields): - """Changes : typing.Sequence[~ModifyControllerAccess]""" - changes_ = [ModifyControllerAccess.from_json(o) for o in changes or []] - - # Validate arguments against known Juju API types. - if changes_ is not None and not isinstance(changes_, (bytes, str, list)): - raise Exception( - f"Expected changes_ to be a Sequence, received: {type(changes_)}" - ) - - self.changes = changes_ - self.unknown_fields = unknown_fields - - -class ModifyModelAccess(Type): - _toSchema = { - "access": "access", - "action": "action", - "model_tag": "model-tag", - "user_tag": "user-tag", - } - _toPy = { - "access": "access", - "action": "action", - "model-tag": "model_tag", - "user-tag": "user_tag", - } - - def __init__( - self, access=None, action=None, model_tag=None, user_tag=None, **unknown_fields - ): - """Access : str - action : str - model_tag : str - user_tag : str - """ - access_ = access - action_ = action - model_tag_ = model_tag - user_tag_ = user_tag - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if action_ is not None and not isinstance(action_, (bytes, str)): - raise Exception(f"Expected action_ to be a str, received: {type(action_)}") - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): - raise Exception( - f"Expected user_tag_ to be a str, received: {type(user_tag_)}" - ) - - self.access = access_ - self.action = action_ - self.model_tag = model_tag_ - self.user_tag = user_tag_ - self.unknown_fields = unknown_fields - - -class ModifyModelAccessRequest(Type): - _toSchema = {"changes": "changes"} - _toPy = {"changes": "changes"} - - def __init__(self, changes=None, **unknown_fields): - """Changes : typing.Sequence[~ModifyModelAccess]""" - changes_ = [ModifyModelAccess.from_json(o) for o in changes or []] - - # Validate arguments against known Juju API types. - if changes_ is not None and not isinstance(changes_, (bytes, str, list)): - raise Exception( - f"Expected changes_ to be a Sequence, received: {type(changes_)}" - ) - - self.changes = changes_ - self.unknown_fields = unknown_fields - - -class ModifyOfferAccess(Type): - _toSchema = { - "access": "access", - "action": "action", - "offer_url": "offer-url", - "user_tag": "user-tag", - } - _toPy = { - "access": "access", - "action": "action", - "offer-url": "offer_url", - "user-tag": "user_tag", - } - - def __init__( - self, access=None, action=None, offer_url=None, user_tag=None, **unknown_fields - ): - """Access : str - action : str - offer_url : str - user_tag : str - """ - access_ = access - action_ = action - offer_url_ = offer_url - user_tag_ = user_tag - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if action_ is not None and not isinstance(action_, (bytes, str)): - raise Exception(f"Expected action_ to be a str, received: {type(action_)}") - - if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): - raise Exception( - f"Expected offer_url_ to be a str, received: {type(offer_url_)}" - ) - - if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): - raise Exception( - f"Expected user_tag_ to be a str, received: {type(user_tag_)}" - ) - - self.access = access_ - self.action = action_ - self.offer_url = offer_url_ - self.user_tag = user_tag_ - self.unknown_fields = unknown_fields - - -class ModifyOfferAccessRequest(Type): - _toSchema = {"changes": "changes"} - _toPy = {"changes": "changes"} - - def __init__(self, changes=None, **unknown_fields): - """Changes : typing.Sequence[~ModifyOfferAccess]""" - changes_ = [ModifyOfferAccess.from_json(o) for o in changes or []] - - # Validate arguments against known Juju API types. - if changes_ is not None and not isinstance(changes_, (bytes, str, list)): - raise Exception( - f"Expected changes_ to be a Sequence, received: {type(changes_)}" - ) - - self.changes = changes_ - self.unknown_fields = unknown_fields - - -class ModifyUserSSHKeys(Type): - _toSchema = {"ssh_keys": "ssh-keys", "user": "user"} - _toPy = {"ssh-keys": "ssh_keys", "user": "user"} - - def __init__(self, ssh_keys=None, user=None, **unknown_fields): - """ssh_keys : typing.Sequence[str] - user : str - """ - ssh_keys_ = ssh_keys - user_ = user - - # Validate arguments against known Juju API types. - if ssh_keys_ is not None and not isinstance(ssh_keys_, (bytes, str, list)): - raise Exception( - f"Expected ssh_keys_ to be a Sequence, received: {type(ssh_keys_)}" - ) - - if user_ is not None and not isinstance(user_, (bytes, str)): - raise Exception(f"Expected user_ to be a str, received: {type(user_)}") - - self.ssh_keys = ssh_keys_ - self.user = user_ - self.unknown_fields = unknown_fields - - -class MoveSubnetsParam(Type): - _toSchema = {"force": "force", "space_tag": "space-tag", "subnets": "subnets"} - _toPy = {"force": "force", "space-tag": "space_tag", "subnets": "subnets"} - - def __init__(self, force=None, space_tag=None, subnets=None, **unknown_fields): - """Force : bool - space_tag : str - subnets : typing.Sequence[str] - """ - force_ = force - space_tag_ = space_tag - subnets_ = subnets - - # Validate arguments against known Juju API types. - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): - raise Exception( - f"Expected space_tag_ to be a str, received: {type(space_tag_)}" - ) - - if subnets_ is not None and not isinstance(subnets_, (bytes, str, list)): - raise Exception( - f"Expected subnets_ to be a Sequence, received: {type(subnets_)}" - ) - - self.force = force_ - self.space_tag = space_tag_ - self.subnets = subnets_ - self.unknown_fields = unknown_fields - - -class MoveSubnetsParams(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~MoveSubnetsParam]""" - args_ = [MoveSubnetsParam.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class MoveSubnetsResult(Type): - _toSchema = { - "error": "error", - "moved_subnets": "moved-subnets", - "new_space": "new-space", - } - _toPy = { - "error": "error", - "moved-subnets": "moved_subnets", - "new-space": "new_space", - } - - def __init__( - self, error=None, moved_subnets=None, new_space=None, **unknown_fields - ): - """Error : Error - moved_subnets : typing.Sequence[~MovedSubnet] - new_space : str - """ - error_ = Error.from_json(error) if error else None - moved_subnets_ = [MovedSubnet.from_json(o) for o in moved_subnets or []] - new_space_ = new_space - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if moved_subnets_ is not None and not isinstance( - moved_subnets_, (bytes, str, list) - ): - raise Exception( - f"Expected moved_subnets_ to be a Sequence, received: {type(moved_subnets_)}" - ) - - if new_space_ is not None and not isinstance(new_space_, (bytes, str)): - raise Exception( - f"Expected new_space_ to be a str, received: {type(new_space_)}" - ) - - self.error = error_ - self.moved_subnets = moved_subnets_ - self.new_space = new_space_ - self.unknown_fields = unknown_fields - - -class MoveSubnetsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~MoveSubnetsResult]""" - results_ = [MoveSubnetsResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class MovedSubnet(Type): - _toSchema = {"cidr": "cidr", "old_space": "old-space", "subnet": "subnet"} - _toPy = {"cidr": "cidr", "old-space": "old_space", "subnet": "subnet"} - - def __init__(self, cidr=None, old_space=None, subnet=None, **unknown_fields): - """Cidr : str - old_space : str - subnet : str - """ - cidr_ = cidr - old_space_ = old_space - subnet_ = subnet - - # Validate arguments against known Juju API types. - if cidr_ is not None and not isinstance(cidr_, (bytes, str)): - raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") - - if old_space_ is not None and not isinstance(old_space_, (bytes, str)): - raise Exception( - f"Expected old_space_ to be a str, received: {type(old_space_)}" - ) - - if subnet_ is not None and not isinstance(subnet_, (bytes, str)): - raise Exception(f"Expected subnet_ to be a str, received: {type(subnet_)}") - - self.cidr = cidr_ - self.old_space = old_space_ - self.subnet = subnet_ - self.unknown_fields = unknown_fields - - -class NetworkInterface(Type): - _toSchema = { - "dns_nameservers": "dns-nameservers", - "gateway": "gateway", - "ip_addresses": "ip-addresses", - "is_up": "is-up", - "mac_address": "mac-address", - "space": "space", - } - _toPy = { - "dns-nameservers": "dns_nameservers", - "gateway": "gateway", - "ip-addresses": "ip_addresses", - "is-up": "is_up", - "mac-address": "mac_address", - "space": "space", - } - - def __init__( - self, - dns_nameservers=None, - gateway=None, - ip_addresses=None, - is_up=None, - mac_address=None, - space=None, - **unknown_fields, - ): - """dns_nameservers : typing.Sequence[str] - gateway : str - ip_addresses : typing.Sequence[str] - is_up : bool - mac_address : str - space : str - """ - dns_nameservers_ = dns_nameservers - gateway_ = gateway - ip_addresses_ = ip_addresses - is_up_ = is_up - mac_address_ = mac_address - space_ = space - - # Validate arguments against known Juju API types. - if dns_nameservers_ is not None and not isinstance( - dns_nameservers_, (bytes, str, list) - ): - raise Exception( - f"Expected dns_nameservers_ to be a Sequence, received: {type(dns_nameservers_)}" - ) - - if gateway_ is not None and not isinstance(gateway_, (bytes, str)): - raise Exception( - f"Expected gateway_ to be a str, received: {type(gateway_)}" - ) - - if ip_addresses_ is not None and not isinstance( - ip_addresses_, (bytes, str, list) - ): - raise Exception( - f"Expected ip_addresses_ to be a Sequence, received: {type(ip_addresses_)}" - ) - - if is_up_ is not None and not isinstance(is_up_, bool): - raise Exception(f"Expected is_up_ to be a bool, received: {type(is_up_)}") - - if mac_address_ is not None and not isinstance(mac_address_, (bytes, str)): - raise Exception( - f"Expected mac_address_ to be a str, received: {type(mac_address_)}" - ) - - if space_ is not None and not isinstance(space_, (bytes, str)): - raise Exception(f"Expected space_ to be a str, received: {type(space_)}") - - self.dns_nameservers = dns_nameservers_ - self.gateway = gateway_ - self.ip_addresses = ip_addresses_ - self.is_up = is_up_ - self.mac_address = mac_address_ - self.space = space_ - self.unknown_fields = unknown_fields - - -class NotifyWatchResult(Type): - _toSchema = {"error": "error", "notifywatcherid": "NotifyWatcherId"} - _toPy = {"NotifyWatcherId": "notifywatcherid", "error": "error"} - - def __init__(self, notifywatcherid=None, error=None, **unknown_fields): - """Notifywatcherid : str - error : Error - """ - notifywatcherid_ = notifywatcherid - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if notifywatcherid_ is not None and not isinstance( - notifywatcherid_, (bytes, str) - ): - raise Exception( - f"Expected notifywatcherid_ to be a str, received: {type(notifywatcherid_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.notifywatcherid = notifywatcherid_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class NotifyWatchResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~NotifyWatchResult]""" - results_ = [NotifyWatchResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class Number(Type): - _toSchema = { - "build": "Build", - "major": "Major", - "minor": "Minor", - "patch": "Patch", - "tag": "Tag", - } - _toPy = { - "Build": "build", - "Major": "major", - "Minor": "minor", - "Patch": "patch", - "Tag": "tag", - } - - def __init__( - self, build=None, major=None, minor=None, patch=None, tag=None, **unknown_fields - ): - """Build : int - major : int - minor : int - patch : int - tag : str - """ - build_ = build - major_ = major - minor_ = minor - patch_ = patch - tag_ = tag - - # Validate arguments against known Juju API types. - if build_ is not None and not isinstance(build_, int): - raise Exception(f"Expected build_ to be a int, received: {type(build_)}") - - if major_ is not None and not isinstance(major_, int): - raise Exception(f"Expected major_ to be a int, received: {type(major_)}") - - if minor_ is not None and not isinstance(minor_, int): - raise Exception(f"Expected minor_ to be a int, received: {type(minor_)}") - - if patch_ is not None and not isinstance(patch_, int): - raise Exception(f"Expected patch_ to be a int, received: {type(patch_)}") - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.build = build_ - self.major = major_ - self.minor = minor_ - self.patch = patch_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class OfferConnection(Type): - _toSchema = { - "endpoint": "endpoint", - "ingress_subnets": "ingress-subnets", - "relation_id": "relation-id", - "source_model_tag": "source-model-tag", - "status": "status", - "username": "username", - } - _toPy = { - "endpoint": "endpoint", - "ingress-subnets": "ingress_subnets", - "relation-id": "relation_id", - "source-model-tag": "source_model_tag", - "status": "status", - "username": "username", - } - - def __init__( - self, - endpoint=None, - ingress_subnets=None, - relation_id=None, - source_model_tag=None, - status=None, - username=None, - **unknown_fields, - ): - """Endpoint : str - ingress_subnets : typing.Sequence[str] - relation_id : int - source_model_tag : str - status : EntityStatus - username : str - """ - endpoint_ = endpoint - ingress_subnets_ = ingress_subnets - relation_id_ = relation_id - source_model_tag_ = source_model_tag - status_ = EntityStatus.from_json(status) if status else None - username_ = username - - # Validate arguments against known Juju API types. - if endpoint_ is not None and not isinstance(endpoint_, (bytes, str)): - raise Exception( - f"Expected endpoint_ to be a str, received: {type(endpoint_)}" - ) - - if ingress_subnets_ is not None and not isinstance( - ingress_subnets_, (bytes, str, list) - ): - raise Exception( - f"Expected ingress_subnets_ to be a Sequence, received: {type(ingress_subnets_)}" - ) - - if relation_id_ is not None and not isinstance(relation_id_, int): - raise Exception( - f"Expected relation_id_ to be a int, received: {type(relation_id_)}" - ) - - if source_model_tag_ is not None and not isinstance( - source_model_tag_, (bytes, str) - ): - raise Exception( - f"Expected source_model_tag_ to be a str, received: {type(source_model_tag_)}" - ) - - if status_ is not None and not isinstance(status_, (dict, EntityStatus)): - raise Exception( - f"Expected status_ to be a EntityStatus, received: {type(status_)}" - ) - - if username_ is not None and not isinstance(username_, (bytes, str)): - raise Exception( - f"Expected username_ to be a str, received: {type(username_)}" - ) - - self.endpoint = endpoint_ - self.ingress_subnets = ingress_subnets_ - self.relation_id = relation_id_ - self.source_model_tag = source_model_tag_ - self.status = status_ - self.username = username_ - self.unknown_fields = unknown_fields - - -class OfferFilter(Type): - _toSchema = { - "allowed_users": "allowed-users", - "application_description": "application-description", - "application_name": "application-name", - "application_user": "application-user", - "connected_users": "connected-users", - "endpoints": "endpoints", - "model_name": "model-name", - "offer_name": "offer-name", - "owner_name": "owner-name", - } - _toPy = { - "allowed-users": "allowed_users", - "application-description": "application_description", - "application-name": "application_name", - "application-user": "application_user", - "connected-users": "connected_users", - "endpoints": "endpoints", - "model-name": "model_name", - "offer-name": "offer_name", - "owner-name": "owner_name", - } - - def __init__( - self, - allowed_users=None, - application_description=None, - application_name=None, - application_user=None, - connected_users=None, - endpoints=None, - model_name=None, - offer_name=None, - owner_name=None, - **unknown_fields, - ): - """allowed_users : typing.Sequence[str] - application_description : str - application_name : str - application_user : str - connected_users : typing.Sequence[str] - endpoints : typing.Sequence[~EndpointFilterAttributes] - model_name : str - offer_name : str - owner_name : str - """ - allowed_users_ = allowed_users - application_description_ = application_description - application_name_ = application_name - application_user_ = application_user - connected_users_ = connected_users - endpoints_ = [EndpointFilterAttributes.from_json(o) for o in endpoints or []] - model_name_ = model_name - offer_name_ = offer_name - owner_name_ = owner_name - - # Validate arguments against known Juju API types. - if allowed_users_ is not None and not isinstance( - allowed_users_, (bytes, str, list) - ): - raise Exception( - f"Expected allowed_users_ to be a Sequence, received: {type(allowed_users_)}" - ) - - if application_description_ is not None and not isinstance( - application_description_, (bytes, str) - ): - raise Exception( - f"Expected application_description_ to be a str, received: {type(application_description_)}" - ) - - if application_name_ is not None and not isinstance( - application_name_, (bytes, str) - ): - raise Exception( - f"Expected application_name_ to be a str, received: {type(application_name_)}" - ) - - if application_user_ is not None and not isinstance( - application_user_, (bytes, str) - ): - raise Exception( - f"Expected application_user_ to be a str, received: {type(application_user_)}" - ) - - if connected_users_ is not None and not isinstance( - connected_users_, (bytes, str, list) - ): - raise Exception( - f"Expected connected_users_ to be a Sequence, received: {type(connected_users_)}" - ) - - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if model_name_ is not None and not isinstance(model_name_, (bytes, str)): - raise Exception( - f"Expected model_name_ to be a str, received: {type(model_name_)}" - ) - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if owner_name_ is not None and not isinstance(owner_name_, (bytes, str)): - raise Exception( - f"Expected owner_name_ to be a str, received: {type(owner_name_)}" - ) - - self.allowed_users = allowed_users_ - self.application_description = application_description_ - self.application_name = application_name_ - self.application_user = application_user_ - self.connected_users = connected_users_ - self.endpoints = endpoints_ - self.model_name = model_name_ - self.offer_name = offer_name_ - self.owner_name = owner_name_ - self.unknown_fields = unknown_fields - - -class OfferFilters(Type): - _toSchema = {"filters": "Filters"} - _toPy = {"Filters": "filters"} - - def __init__(self, filters=None, **unknown_fields): - """Filters : typing.Sequence[~OfferFilter]""" - filters_ = [OfferFilter.from_json(o) for o in filters or []] - - # Validate arguments against known Juju API types. - if filters_ is not None and not isinstance(filters_, (bytes, str, list)): - raise Exception( - f"Expected filters_ to be a Sequence, received: {type(filters_)}" - ) - - self.filters = filters_ - self.unknown_fields = unknown_fields - - -class OfferURLs(Type): - _toSchema = {"bakery_version": "bakery-version", "offer_urls": "offer-urls"} - _toPy = {"bakery-version": "bakery_version", "offer-urls": "offer_urls"} - - def __init__(self, bakery_version=None, offer_urls=None, **unknown_fields): - """bakery_version : int - offer_urls : typing.Sequence[str] - """ - bakery_version_ = bakery_version - offer_urls_ = offer_urls - - # Validate arguments against known Juju API types. - if bakery_version_ is not None and not isinstance(bakery_version_, int): - raise Exception( - f"Expected bakery_version_ to be a int, received: {type(bakery_version_)}" - ) - - if offer_urls_ is not None and not isinstance(offer_urls_, (bytes, str, list)): - raise Exception( - f"Expected offer_urls_ to be a Sequence, received: {type(offer_urls_)}" - ) - - self.bakery_version = bakery_version_ - self.offer_urls = offer_urls_ - self.unknown_fields = unknown_fields - - -class OfferUserDetails(Type): - _toSchema = {"access": "access", "display_name": "display-name", "user": "user"} - _toPy = {"access": "access", "display-name": "display_name", "user": "user"} - - def __init__(self, access=None, display_name=None, user=None, **unknown_fields): - """Access : str - display_name : str - user : str - """ - access_ = access - display_name_ = display_name - user_ = user - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if display_name_ is not None and not isinstance(display_name_, (bytes, str)): - raise Exception( - f"Expected display_name_ to be a str, received: {type(display_name_)}" - ) - - if user_ is not None and not isinstance(user_, (bytes, str)): - raise Exception(f"Expected user_ to be a str, received: {type(user_)}") - - self.access = access_ - self.display_name = display_name_ - self.user = user_ - self.unknown_fields = unknown_fields - - -class OperationQueryArgs(Type): - _toSchema = { - "actions": "actions", - "applications": "applications", - "limit": "limit", - "machines": "machines", - "offset": "offset", - "status": "status", - "units": "units", - } - _toPy = { - "actions": "actions", - "applications": "applications", - "limit": "limit", - "machines": "machines", - "offset": "offset", - "status": "status", - "units": "units", - } - - def __init__( - self, - actions=None, - applications=None, - limit=None, - machines=None, - offset=None, - status=None, - units=None, - **unknown_fields, - ): - """Actions : typing.Sequence[str] - applications : typing.Sequence[str] - limit : int - machines : typing.Sequence[str] - offset : int - status : typing.Sequence[str] - units : typing.Sequence[str] - """ - actions_ = actions - applications_ = applications - limit_ = limit - machines_ = machines - offset_ = offset - status_ = status - units_ = units - - # Validate arguments against known Juju API types. - if actions_ is not None and not isinstance(actions_, (bytes, str, list)): - raise Exception( - f"Expected actions_ to be a Sequence, received: {type(actions_)}" - ) - - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - if limit_ is not None and not isinstance(limit_, int): - raise Exception(f"Expected limit_ to be a int, received: {type(limit_)}") - - if machines_ is not None and not isinstance(machines_, (bytes, str, list)): - raise Exception( - f"Expected machines_ to be a Sequence, received: {type(machines_)}" - ) - - if offset_ is not None and not isinstance(offset_, int): - raise Exception(f"Expected offset_ to be a int, received: {type(offset_)}") - - if status_ is not None and not isinstance(status_, (bytes, str, list)): - raise Exception( - f"Expected status_ to be a Sequence, received: {type(status_)}" - ) - - if units_ is not None and not isinstance(units_, (bytes, str, list)): - raise Exception( - f"Expected units_ to be a Sequence, received: {type(units_)}" - ) - - self.actions = actions_ - self.applications = applications_ - self.limit = limit_ - self.machines = machines_ - self.offset = offset_ - self.status = status_ - self.units = units_ - self.unknown_fields = unknown_fields - - -class OperationResult(Type): - _toSchema = { - "actions": "actions", - "completed": "completed", - "enqueued": "enqueued", - "error": "error", - "fail": "fail", - "operation": "operation", - "started": "started", - "status": "status", - "summary": "summary", - } - _toPy = { - "actions": "actions", - "completed": "completed", - "enqueued": "enqueued", - "error": "error", - "fail": "fail", - "operation": "operation", - "started": "started", - "status": "status", - "summary": "summary", - } - - def __init__( - self, - actions=None, - completed=None, - enqueued=None, - error=None, - fail=None, - operation=None, - started=None, - status=None, - summary=None, - **unknown_fields, - ): - """Actions : typing.Sequence[~ActionResult] - completed : str - enqueued : str - error : Error - fail : str - operation : str - started : str - status : str - summary : str - """ - actions_ = [ActionResult.from_json(o) for o in actions or []] - completed_ = completed - enqueued_ = enqueued - error_ = Error.from_json(error) if error else None - fail_ = fail - operation_ = operation - started_ = started - status_ = status - summary_ = summary - - # Validate arguments against known Juju API types. - if actions_ is not None and not isinstance(actions_, (bytes, str, list)): - raise Exception( - f"Expected actions_ to be a Sequence, received: {type(actions_)}" - ) - - if completed_ is not None and not isinstance(completed_, (bytes, str)): - raise Exception( - f"Expected completed_ to be a str, received: {type(completed_)}" - ) - - if enqueued_ is not None and not isinstance(enqueued_, (bytes, str)): - raise Exception( - f"Expected enqueued_ to be a str, received: {type(enqueued_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if fail_ is not None and not isinstance(fail_, (bytes, str)): - raise Exception(f"Expected fail_ to be a str, received: {type(fail_)}") - - if operation_ is not None and not isinstance(operation_, (bytes, str)): - raise Exception( - f"Expected operation_ to be a str, received: {type(operation_)}" - ) - - if started_ is not None and not isinstance(started_, (bytes, str)): - raise Exception( - f"Expected started_ to be a str, received: {type(started_)}" - ) - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - if summary_ is not None and not isinstance(summary_, (bytes, str)): - raise Exception( - f"Expected summary_ to be a str, received: {type(summary_)}" - ) - - self.actions = actions_ - self.completed = completed_ - self.enqueued = enqueued_ - self.error = error_ - self.fail = fail_ - self.operation = operation_ - self.started = started_ - self.status = status_ - self.summary = summary_ - self.unknown_fields = unknown_fields - - -class OperationResults(Type): - _toSchema = {"results": "results", "truncated": "truncated"} - _toPy = {"results": "results", "truncated": "truncated"} - - def __init__(self, results=None, truncated=None, **unknown_fields): - """Results : typing.Sequence[~OperationResult] - truncated : bool - """ - results_ = [OperationResult.from_json(o) for o in results or []] - truncated_ = truncated - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - if truncated_ is not None and not isinstance(truncated_, bool): - raise Exception( - f"Expected truncated_ to be a bool, received: {type(truncated_)}" - ) - - self.results = results_ - self.truncated = truncated_ - self.unknown_fields = unknown_fields - - -class Payload(Type): - _toSchema = { - "class_": "class", - "id_": "id", - "labels": "labels", - "machine": "machine", - "status": "status", - "type_": "type", - "unit": "unit", - } - _toPy = { - "class": "class_", - "id": "id_", - "labels": "labels", - "machine": "machine", - "status": "status", - "type": "type_", - "unit": "unit", - } - - def __init__( - self, - class_=None, - id_=None, - labels=None, - machine=None, - status=None, - type_=None, - unit=None, - **unknown_fields, - ): - """class_ : str - id_ : str - labels : typing.Sequence[str] - machine : str - status : str - type_ : str - unit : str - """ - class__ = class_ - id__ = id_ - labels_ = labels - machine_ = machine - status_ = status - type__ = type_ - unit_ = unit - - # Validate arguments against known Juju API types. - if class__ is not None and not isinstance(class__, (bytes, str)): - raise Exception(f"Expected class__ to be a str, received: {type(class__)}") - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if labels_ is not None and not isinstance(labels_, (bytes, str, list)): - raise Exception( - f"Expected labels_ to be a Sequence, received: {type(labels_)}" - ) - - if machine_ is not None and not isinstance(machine_, (bytes, str)): - raise Exception( - f"Expected machine_ to be a str, received: {type(machine_)}" - ) - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - if unit_ is not None and not isinstance(unit_, (bytes, str)): - raise Exception(f"Expected unit_ to be a str, received: {type(unit_)}") - - self.class_ = class__ - self.id_ = id__ - self.labels = labels_ - self.machine = machine_ - self.status = status_ - self.type_ = type__ - self.unit = unit_ - self.unknown_fields = unknown_fields - - -class PayloadListArgs(Type): - _toSchema = {"patterns": "patterns"} - _toPy = {"patterns": "patterns"} - - def __init__(self, patterns=None, **unknown_fields): - """Patterns : typing.Sequence[str]""" - patterns_ = patterns - - # Validate arguments against known Juju API types. - if patterns_ is not None and not isinstance(patterns_, (bytes, str, list)): - raise Exception( - f"Expected patterns_ to be a Sequence, received: {type(patterns_)}" - ) - - self.patterns = patterns_ - self.unknown_fields = unknown_fields - - -class PayloadListResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~Payload]""" - results_ = [Payload.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class PendingResourceUpload(Type): - _toSchema = {"filename": "Filename", "name": "Name", "type_": "Type"} - _toPy = {"Filename": "filename", "Name": "name", "Type": "type_"} - - def __init__(self, filename=None, name=None, type_=None, **unknown_fields): - """Filename : str - name : str - type_ : str - """ - filename_ = filename - name_ = name - type__ = type_ - - # Validate arguments against known Juju API types. - if filename_ is not None and not isinstance(filename_, (bytes, str)): - raise Exception( - f"Expected filename_ to be a str, received: {type(filename_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.filename = filename_ - self.name = name_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class Placement(Type): - _toSchema = {"directive": "directive", "scope": "scope"} - _toPy = {"directive": "directive", "scope": "scope"} - - def __init__(self, directive=None, scope=None, **unknown_fields): - """Directive : str - scope : str - """ - directive_ = directive - scope_ = scope - - # Validate arguments against known Juju API types. - if directive_ is not None and not isinstance(directive_, (bytes, str)): - raise Exception( - f"Expected directive_ to be a str, received: {type(directive_)}" - ) - - if scope_ is not None and not isinstance(scope_, (bytes, str)): - raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") - - self.directive = directive_ - self.scope = scope_ - self.unknown_fields = unknown_fields - - -class ProvisioningScriptParams(Type): - _toSchema = { - "data_dir": "data-dir", - "disable_package_commands": "disable-package-commands", - "machine_id": "machine-id", - "nonce": "nonce", - } - _toPy = { - "data-dir": "data_dir", - "disable-package-commands": "disable_package_commands", - "machine-id": "machine_id", - "nonce": "nonce", - } - - def __init__( - self, - data_dir=None, - disable_package_commands=None, - machine_id=None, - nonce=None, - **unknown_fields, - ): - """data_dir : str - disable_package_commands : bool - machine_id : str - nonce : str - """ - data_dir_ = data_dir - disable_package_commands_ = disable_package_commands - machine_id_ = machine_id - nonce_ = nonce - - # Validate arguments against known Juju API types. - if data_dir_ is not None and not isinstance(data_dir_, (bytes, str)): - raise Exception( - f"Expected data_dir_ to be a str, received: {type(data_dir_)}" - ) - - if disable_package_commands_ is not None and not isinstance( - disable_package_commands_, bool - ): - raise Exception( - f"Expected disable_package_commands_ to be a bool, received: {type(disable_package_commands_)}" - ) - - if machine_id_ is not None and not isinstance(machine_id_, (bytes, str)): - raise Exception( - f"Expected machine_id_ to be a str, received: {type(machine_id_)}" - ) - - if nonce_ is not None and not isinstance(nonce_, (bytes, str)): - raise Exception(f"Expected nonce_ to be a str, received: {type(nonce_)}") - - self.data_dir = data_dir_ - self.disable_package_commands = disable_package_commands_ - self.machine_id = machine_id_ - self.nonce = nonce_ - self.unknown_fields = unknown_fields - - -class ProvisioningScriptResult(Type): - _toSchema = {"script": "script"} - _toPy = {"script": "script"} - - def __init__(self, script=None, **unknown_fields): - """Script : str""" - script_ = script - - # Validate arguments against known Juju API types. - if script_ is not None and not isinstance(script_, (bytes, str)): - raise Exception(f"Expected script_ to be a str, received: {type(script_)}") - - self.script = script_ - self.unknown_fields = unknown_fields - - -class Proxy(Type): - _toSchema = {"config": "config", "type_": "type"} - _toPy = {"config": "config", "type": "type_"} - - def __init__(self, config=None, type_=None, **unknown_fields): - """Config : typing.Mapping[str, typing.Any] - type_ : str - """ - config_ = config - type__ = type_ - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - self.config = config_ - self.type_ = type__ - self.unknown_fields = unknown_fields - - -class QueryApplicationOffersResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ApplicationOfferAdminDetails]""" - results_ = [ApplicationOfferAdminDetails.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class QueryApplicationOffersResultsV5(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ApplicationOfferAdminDetailsV5]""" - results_ = [ApplicationOfferAdminDetailsV5.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class RedirectInfoResult(Type): - _toSchema = {"ca_cert": "ca-cert", "servers": "servers"} - _toPy = {"ca-cert": "ca_cert", "servers": "servers"} - - def __init__(self, ca_cert=None, servers=None, **unknown_fields): - """ca_cert : str - servers : typing.Sequence[~HostPort] - """ - ca_cert_ = ca_cert - servers_ = [HostPort.from_json(o) for o in servers or []] - - # Validate arguments against known Juju API types. - if ca_cert_ is not None and not isinstance(ca_cert_, (bytes, str)): - raise Exception( - f"Expected ca_cert_ to be a str, received: {type(ca_cert_)}" - ) - - if servers_ is not None and not isinstance(servers_, (bytes, str, list)): - raise Exception( - f"Expected servers_ to be a Sequence, received: {type(servers_)}" - ) - - self.ca_cert = ca_cert_ - self.servers = servers_ - self.unknown_fields = unknown_fields - - -class RegionDefaults(Type): - _toSchema = {"region_name": "region-name", "value": "value"} - _toPy = {"region-name": "region_name", "value": "value"} - - def __init__(self, region_name=None, value=None, **unknown_fields): - """region_name : str - value : Any - """ - region_name_ = region_name - value_ = value - - # Validate arguments against known Juju API types. - if region_name_ is not None and not isinstance(region_name_, (bytes, str)): - raise Exception( - f"Expected region_name_ to be a str, received: {type(region_name_)}" - ) - - self.region_name = region_name_ - self.value = value_ - self.unknown_fields = unknown_fields - - -class RelationData(Type): - _toSchema = {"inscope": "InScope", "unitdata": "UnitData"} - _toPy = {"InScope": "inscope", "UnitData": "unitdata"} - - def __init__(self, inscope=None, unitdata=None, **unknown_fields): - """Inscope : bool - unitdata : typing.Mapping[str, typing.Any] - """ - inscope_ = inscope - unitdata_ = unitdata - - # Validate arguments against known Juju API types. - if inscope_ is not None and not isinstance(inscope_, bool): - raise Exception( - f"Expected inscope_ to be a bool, received: {type(inscope_)}" - ) - - if unitdata_ is not None and not isinstance(unitdata_, dict): - raise Exception( - f"Expected unitdata_ to be a Mapping, received: {type(unitdata_)}" - ) - - self.inscope = inscope_ - self.unitdata = unitdata_ - self.unknown_fields = unknown_fields - - -class RelationStatus(Type): - _toSchema = { - "endpoints": "endpoints", - "id_": "id", - "interface": "interface", - "key": "key", - "scope": "scope", - "status": "status", - } - _toPy = { - "endpoints": "endpoints", - "id": "id_", - "interface": "interface", - "key": "key", - "scope": "scope", - "status": "status", - } - - def __init__( - self, - endpoints=None, - id_=None, - interface=None, - key=None, - scope=None, - status=None, - **unknown_fields, - ): - """Endpoints : typing.Sequence[~EndpointStatus] - id_ : int - interface : str - key : str - scope : str - status : DetailedStatus - """ - endpoints_ = [EndpointStatus.from_json(o) for o in endpoints or []] - id__ = id_ - interface_ = interface - key_ = key - scope_ = scope - status_ = DetailedStatus.from_json(status) if status else None - - # Validate arguments against known Juju API types. - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if id__ is not None and not isinstance(id__, int): - raise Exception(f"Expected id__ to be a int, received: {type(id__)}") - - if interface_ is not None and not isinstance(interface_, (bytes, str)): - raise Exception( - f"Expected interface_ to be a str, received: {type(interface_)}" - ) - - if key_ is not None and not isinstance(key_, (bytes, str)): - raise Exception(f"Expected key_ to be a str, received: {type(key_)}") - - if scope_ is not None and not isinstance(scope_, (bytes, str)): - raise Exception(f"Expected scope_ to be a str, received: {type(scope_)}") - - if status_ is not None and not isinstance(status_, (dict, DetailedStatus)): - raise Exception( - f"Expected status_ to be a DetailedStatus, received: {type(status_)}" - ) - - self.endpoints = endpoints_ - self.id_ = id__ - self.interface = interface_ - self.key = key_ - self.scope = scope_ - self.status = status_ - self.unknown_fields = unknown_fields - - -class RelationSuspendedArg(Type): - _toSchema = { - "message": "message", - "relation_id": "relation-id", - "suspended": "suspended", - } - _toPy = { - "message": "message", - "relation-id": "relation_id", - "suspended": "suspended", - } - - def __init__( - self, message=None, relation_id=None, suspended=None, **unknown_fields - ): - """Message : str - relation_id : int - suspended : bool - """ - message_ = message - relation_id_ = relation_id - suspended_ = suspended - - # Validate arguments against known Juju API types. - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - if relation_id_ is not None and not isinstance(relation_id_, int): - raise Exception( - f"Expected relation_id_ to be a int, received: {type(relation_id_)}" - ) - - if suspended_ is not None and not isinstance(suspended_, bool): - raise Exception( - f"Expected suspended_ to be a bool, received: {type(suspended_)}" - ) - - self.message = message_ - self.relation_id = relation_id_ - self.suspended = suspended_ - self.unknown_fields = unknown_fields - - -class RelationSuspendedArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~RelationSuspendedArg]""" - args_ = [RelationSuspendedArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class RemoteApplicationInfo(Type): - _toSchema = { - "description": "description", - "endpoints": "endpoints", - "icon_url_path": "icon-url-path", - "model_tag": "model-tag", - "name": "name", - "offer_url": "offer-url", - "source_model_label": "source-model-label", - } - _toPy = { - "description": "description", - "endpoints": "endpoints", - "icon-url-path": "icon_url_path", - "model-tag": "model_tag", - "name": "name", - "offer-url": "offer_url", - "source-model-label": "source_model_label", - } - - def __init__( - self, - description=None, - endpoints=None, - icon_url_path=None, - model_tag=None, - name=None, - offer_url=None, - source_model_label=None, - **unknown_fields, - ): - """Description : str - endpoints : typing.Sequence[~RemoteEndpoint] - icon_url_path : str - model_tag : str - name : str - offer_url : str - source_model_label : str - """ - description_ = description - endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] - icon_url_path_ = icon_url_path - model_tag_ = model_tag - name_ = name - offer_url_ = offer_url - source_model_label_ = source_model_label - - # Validate arguments against known Juju API types. - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if icon_url_path_ is not None and not isinstance(icon_url_path_, (bytes, str)): - raise Exception( - f"Expected icon_url_path_ to be a str, received: {type(icon_url_path_)}" - ) - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): - raise Exception( - f"Expected offer_url_ to be a str, received: {type(offer_url_)}" - ) - - if source_model_label_ is not None and not isinstance( - source_model_label_, (bytes, str) - ): - raise Exception( - f"Expected source_model_label_ to be a str, received: {type(source_model_label_)}" - ) - - self.description = description_ - self.endpoints = endpoints_ - self.icon_url_path = icon_url_path_ - self.model_tag = model_tag_ - self.name = name_ - self.offer_url = offer_url_ - self.source_model_label = source_model_label_ - self.unknown_fields = unknown_fields - - -class RemoteApplicationInfoResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : RemoteApplicationInfo - """ - error_ = Error.from_json(error) if error else None - result_ = RemoteApplicationInfo.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance( - result_, (dict, RemoteApplicationInfo) - ): - raise Exception( - f"Expected result_ to be a RemoteApplicationInfo, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class RemoteApplicationInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~RemoteApplicationInfoResult]""" - results_ = [RemoteApplicationInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class RemoteApplicationStatus(Type): - _toSchema = { - "endpoints": "endpoints", - "err": "err", - "life": "life", - "offer_name": "offer-name", - "offer_url": "offer-url", - "relations": "relations", - "status": "status", - } - _toPy = { - "endpoints": "endpoints", - "err": "err", - "life": "life", - "offer-name": "offer_name", - "offer-url": "offer_url", - "relations": "relations", - "status": "status", - } - - def __init__( - self, - endpoints=None, - err=None, - life=None, - offer_name=None, - offer_url=None, - relations=None, - status=None, - **unknown_fields, - ): - """Endpoints : typing.Sequence[~RemoteEndpoint] - err : Error - life : str - offer_name : str - offer_url : str - relations : typing.Mapping[str, typing.Sequence[str]] - status : DetailedStatus - """ - endpoints_ = [RemoteEndpoint.from_json(o) for o in endpoints or []] - err_ = Error.from_json(err) if err else None - life_ = life - offer_name_ = offer_name - offer_url_ = offer_url - relations_ = relations - status_ = DetailedStatus.from_json(status) if status else None - - # Validate arguments against known Juju API types. - if endpoints_ is not None and not isinstance(endpoints_, (bytes, str, list)): - raise Exception( - f"Expected endpoints_ to be a Sequence, received: {type(endpoints_)}" - ) - - if err_ is not None and not isinstance(err_, (dict, Error)): - raise Exception(f"Expected err_ to be a Error, received: {type(err_)}") - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if offer_name_ is not None and not isinstance(offer_name_, (bytes, str)): - raise Exception( - f"Expected offer_name_ to be a str, received: {type(offer_name_)}" - ) - - if offer_url_ is not None and not isinstance(offer_url_, (bytes, str)): - raise Exception( - f"Expected offer_url_ to be a str, received: {type(offer_url_)}" - ) - - if relations_ is not None and not isinstance(relations_, dict): - raise Exception( - f"Expected relations_ to be a Mapping, received: {type(relations_)}" - ) - - if status_ is not None and not isinstance(status_, (dict, DetailedStatus)): - raise Exception( - f"Expected status_ to be a DetailedStatus, received: {type(status_)}" - ) - - self.endpoints = endpoints_ - self.err = err_ - self.life = life_ - self.offer_name = offer_name_ - self.offer_url = offer_url_ - self.relations = relations_ - self.status = status_ - self.unknown_fields = unknown_fields - - -class RemoteEndpoint(Type): - _toSchema = { - "interface": "interface", - "limit": "limit", - "name": "name", - "role": "role", - } - _toPy = {"interface": "interface", "limit": "limit", "name": "name", "role": "role"} - - def __init__( - self, interface=None, limit=None, name=None, role=None, **unknown_fields - ): - """Interface : str - limit : int - name : str - role : str - """ - interface_ = interface - limit_ = limit - name_ = name - role_ = role - - # Validate arguments against known Juju API types. - if interface_ is not None and not isinstance(interface_, (bytes, str)): - raise Exception( - f"Expected interface_ to be a str, received: {type(interface_)}" - ) - - if limit_ is not None and not isinstance(limit_, int): - raise Exception(f"Expected limit_ to be a int, received: {type(limit_)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if role_ is not None and not isinstance(role_, (bytes, str)): - raise Exception(f"Expected role_ to be a str, received: {type(role_)}") - - self.interface = interface_ - self.limit = limit_ - self.name = name_ - self.role = role_ - self.unknown_fields = unknown_fields - - -class RemoteSpace(Type): - _toSchema = { - "cloud_type": "cloud-type", - "name": "name", - "provider_attributes": "provider-attributes", - "provider_id": "provider-id", - "subnets": "subnets", - } - _toPy = { - "cloud-type": "cloud_type", - "name": "name", - "provider-attributes": "provider_attributes", - "provider-id": "provider_id", - "subnets": "subnets", - } - - def __init__( - self, - cloud_type=None, - name=None, - provider_attributes=None, - provider_id=None, - subnets=None, - **unknown_fields, - ): - """cloud_type : str - name : str - provider_attributes : typing.Mapping[str, typing.Any] - provider_id : str - subnets : typing.Sequence[~Subnet] - """ - cloud_type_ = cloud_type - name_ = name - provider_attributes_ = provider_attributes - provider_id_ = provider_id - subnets_ = [Subnet.from_json(o) for o in subnets or []] - - # Validate arguments against known Juju API types. - if cloud_type_ is not None and not isinstance(cloud_type_, (bytes, str)): - raise Exception( - f"Expected cloud_type_ to be a str, received: {type(cloud_type_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if provider_attributes_ is not None and not isinstance( - provider_attributes_, dict - ): - raise Exception( - f"Expected provider_attributes_ to be a Mapping, received: {type(provider_attributes_)}" - ) - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if subnets_ is not None and not isinstance(subnets_, (bytes, str, list)): - raise Exception( - f"Expected subnets_ to be a Sequence, received: {type(subnets_)}" - ) - - self.cloud_type = cloud_type_ - self.name = name_ - self.provider_attributes = provider_attributes_ - self.provider_id = provider_id_ - self.subnets = subnets_ - self.unknown_fields = unknown_fields - - -class RemoveBlocksArgs(Type): - _toSchema = {"all_": "all"} - _toPy = {"all": "all_"} - - def __init__(self, all_=None, **unknown_fields): - """all_ : bool""" - all__ = all_ - - # Validate arguments against known Juju API types. - if all__ is not None and not isinstance(all__, bool): - raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") - - self.all_ = all__ - self.unknown_fields = unknown_fields - - -class RemoveSecretBackendArg(Type): - _toSchema = {"force": "force", "name": "name"} - _toPy = {"force": "force", "name": "name"} - - def __init__(self, force=None, name=None, **unknown_fields): - """Force : bool - name : str - """ - force_ = force - name_ = name - - # Validate arguments against known Juju API types. - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - self.force = force_ - self.name = name_ - self.unknown_fields = unknown_fields - - -class RemoveSecretBackendArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~RemoveSecretBackendArg]""" - args_ = [RemoveSecretBackendArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class RemoveSpaceParam(Type): - _toSchema = {"dry_run": "dry-run", "force": "force", "space": "space"} - _toPy = {"dry-run": "dry_run", "force": "force", "space": "space"} - - def __init__(self, dry_run=None, force=None, space=None, **unknown_fields): - """dry_run : bool - force : bool - space : Entity - """ - dry_run_ = dry_run - force_ = force - space_ = Entity.from_json(space) if space else None - - # Validate arguments against known Juju API types. - if dry_run_ is not None and not isinstance(dry_run_, bool): - raise Exception( - f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if space_ is not None and not isinstance(space_, (dict, Entity)): - raise Exception(f"Expected space_ to be a Entity, received: {type(space_)}") - - self.dry_run = dry_run_ - self.force = force_ - self.space = space_ - self.unknown_fields = unknown_fields - - -class RemoveSpaceParams(Type): - _toSchema = {"space_param": "space-param"} - _toPy = {"space-param": "space_param"} - - def __init__(self, space_param=None, **unknown_fields): - """space_param : typing.Sequence[~RemoveSpaceParam]""" - space_param_ = [RemoveSpaceParam.from_json(o) for o in space_param or []] - - # Validate arguments against known Juju API types. - if space_param_ is not None and not isinstance( - space_param_, (bytes, str, list) - ): - raise Exception( - f"Expected space_param_ to be a Sequence, received: {type(space_param_)}" - ) - - self.space_param = space_param_ - self.unknown_fields = unknown_fields - - -class RemoveSpaceResult(Type): - _toSchema = { - "bindings": "bindings", - "constraints": "constraints", - "controller_settings": "controller-settings", - "error": "error", - } - _toPy = { - "bindings": "bindings", - "constraints": "constraints", - "controller-settings": "controller_settings", - "error": "error", - } - - def __init__( - self, - bindings=None, - constraints=None, - controller_settings=None, - error=None, - **unknown_fields, - ): - """Bindings : typing.Sequence[~Entity] - constraints : typing.Sequence[~Entity] - controller_settings : typing.Sequence[str] - error : Error - """ - bindings_ = [Entity.from_json(o) for o in bindings or []] - constraints_ = [Entity.from_json(o) for o in constraints or []] - controller_settings_ = controller_settings - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if bindings_ is not None and not isinstance(bindings_, (bytes, str, list)): - raise Exception( - f"Expected bindings_ to be a Sequence, received: {type(bindings_)}" - ) - - if constraints_ is not None and not isinstance( - constraints_, (bytes, str, list) - ): - raise Exception( - f"Expected constraints_ to be a Sequence, received: {type(constraints_)}" - ) - - if controller_settings_ is not None and not isinstance( - controller_settings_, (bytes, str, list) - ): - raise Exception( - f"Expected controller_settings_ to be a Sequence, received: {type(controller_settings_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.bindings = bindings_ - self.constraints = constraints_ - self.controller_settings = controller_settings_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class RemoveSpaceResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~RemoveSpaceResult]""" - results_ = [RemoveSpaceResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class RemoveStorage(Type): - _toSchema = {"storage": "storage"} - _toPy = {"storage": "storage"} - - def __init__(self, storage=None, **unknown_fields): - """Storage : typing.Sequence[~RemoveStorageInstance]""" - storage_ = [RemoveStorageInstance.from_json(o) for o in storage or []] - - # Validate arguments against known Juju API types. - if storage_ is not None and not isinstance(storage_, (bytes, str, list)): - raise Exception( - f"Expected storage_ to be a Sequence, received: {type(storage_)}" - ) - - self.storage = storage_ - self.unknown_fields = unknown_fields - - -class RemoveStorageInstance(Type): - _toSchema = { - "destroy_attachments": "destroy-attachments", - "destroy_storage": "destroy-storage", - "force": "force", - "max_wait": "max-wait", - "tag": "tag", - } - _toPy = { - "destroy-attachments": "destroy_attachments", - "destroy-storage": "destroy_storage", - "force": "force", - "max-wait": "max_wait", - "tag": "tag", - } - - def __init__( - self, - destroy_attachments=None, - destroy_storage=None, - force=None, - max_wait=None, - tag=None, - **unknown_fields, - ): - """destroy_attachments : bool - destroy_storage : bool - force : bool - max_wait : int - tag : str - """ - destroy_attachments_ = destroy_attachments - destroy_storage_ = destroy_storage - force_ = force - max_wait_ = max_wait - tag_ = tag - - # Validate arguments against known Juju API types. - if destroy_attachments_ is not None and not isinstance( - destroy_attachments_, bool - ): - raise Exception( - f"Expected destroy_attachments_ to be a bool, received: {type(destroy_attachments_)}" - ) - - if destroy_storage_ is not None and not isinstance(destroy_storage_, bool): - raise Exception( - f"Expected destroy_storage_ to be a bool, received: {type(destroy_storage_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if max_wait_ is not None and not isinstance(max_wait_, int): - raise Exception( - f"Expected max_wait_ to be a int, received: {type(max_wait_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.destroy_attachments = destroy_attachments_ - self.destroy_storage = destroy_storage_ - self.force = force_ - self.max_wait = max_wait_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class RenameSpaceParams(Type): - _toSchema = {"from_space_tag": "from-space-tag", "to_space_tag": "to-space-tag"} - _toPy = {"from-space-tag": "from_space_tag", "to-space-tag": "to_space_tag"} - - def __init__(self, from_space_tag=None, to_space_tag=None, **unknown_fields): - """from_space_tag : str - to_space_tag : str - """ - from_space_tag_ = from_space_tag - to_space_tag_ = to_space_tag - - # Validate arguments against known Juju API types. - if from_space_tag_ is not None and not isinstance( - from_space_tag_, (bytes, str) - ): - raise Exception( - f"Expected from_space_tag_ to be a str, received: {type(from_space_tag_)}" - ) - - if to_space_tag_ is not None and not isinstance(to_space_tag_, (bytes, str)): - raise Exception( - f"Expected to_space_tag_ to be a str, received: {type(to_space_tag_)}" - ) - - self.from_space_tag = from_space_tag_ - self.to_space_tag = to_space_tag_ - self.unknown_fields = unknown_fields - - -class RenameSpacesParams(Type): - _toSchema = {"changes": "changes"} - _toPy = {"changes": "changes"} - - def __init__(self, changes=None, **unknown_fields): - """Changes : typing.Sequence[~RenameSpaceParams]""" - changes_ = [RenameSpaceParams.from_json(o) for o in changes or []] - - # Validate arguments against known Juju API types. - if changes_ is not None and not isinstance(changes_, (bytes, str, list)): - raise Exception( - f"Expected changes_ to be a Sequence, received: {type(changes_)}" - ) - - self.changes = changes_ - self.unknown_fields = unknown_fields - - -class ResolveCharmWithChannel(Type): - _toSchema = { - "charm_origin": "charm-origin", - "reference": "reference", - "switch_charm": "switch-charm", - } - _toPy = { - "charm-origin": "charm_origin", - "reference": "reference", - "switch-charm": "switch_charm", - } - - def __init__( - self, charm_origin=None, reference=None, switch_charm=None, **unknown_fields - ): - """charm_origin : CharmOrigin - reference : str - switch_charm : bool - """ - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - reference_ = reference - switch_charm_ = switch_charm - - # Validate arguments against known Juju API types. - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if reference_ is not None and not isinstance(reference_, (bytes, str)): - raise Exception( - f"Expected reference_ to be a str, received: {type(reference_)}" - ) - - if switch_charm_ is not None and not isinstance(switch_charm_, bool): - raise Exception( - f"Expected switch_charm_ to be a bool, received: {type(switch_charm_)}" - ) - - self.charm_origin = charm_origin_ - self.reference = reference_ - self.switch_charm = switch_charm_ - self.unknown_fields = unknown_fields - - -class ResolveCharmWithChannelResult(Type): - _toSchema = { - "charm_origin": "charm-origin", - "error": "error", - "supported_bases": "supported-bases", - "url": "url", - } - _toPy = { - "charm-origin": "charm_origin", - "error": "error", - "supported-bases": "supported_bases", - "url": "url", - } - - def __init__( - self, - charm_origin=None, - error=None, - supported_bases=None, - url=None, - **unknown_fields, - ): - """charm_origin : CharmOrigin - error : Error - supported_bases : typing.Sequence[~Base] - url : str - """ - charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None - error_ = Error.from_json(error) if error else None - supported_bases_ = [Base.from_json(o) for o in supported_bases or []] - url_ = url - - # Validate arguments against known Juju API types. - if charm_origin_ is not None and not isinstance( - charm_origin_, (dict, CharmOrigin) - ): - raise Exception( - f"Expected charm_origin_ to be a CharmOrigin, received: {type(charm_origin_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if supported_bases_ is not None and not isinstance( - supported_bases_, (bytes, str, list) - ): - raise Exception( - f"Expected supported_bases_ to be a Sequence, received: {type(supported_bases_)}" - ) - - if url_ is not None and not isinstance(url_, (bytes, str)): - raise Exception(f"Expected url_ to be a str, received: {type(url_)}") - - self.charm_origin = charm_origin_ - self.error = error_ - self.supported_bases = supported_bases_ - self.url = url_ - self.unknown_fields = unknown_fields - - -class ResolveCharmWithChannelResults(Type): - _toSchema = {"results": "Results"} - _toPy = {"Results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ResolveCharmWithChannelResult]""" - results_ = [ResolveCharmWithChannelResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ResolveCharmsWithChannel(Type): - _toSchema = {"macaroon": "macaroon", "resolve": "resolve"} - _toPy = {"macaroon": "macaroon", "resolve": "resolve"} - - def __init__(self, macaroon=None, resolve=None, **unknown_fields): - """Macaroon : Macaroon - resolve : typing.Sequence[~ResolveCharmWithChannel] - """ - macaroon_ = Macaroon.from_json(macaroon) if macaroon else None - resolve_ = [ResolveCharmWithChannel.from_json(o) for o in resolve or []] - - # Validate arguments against known Juju API types. - if macaroon_ is not None and not isinstance(macaroon_, (dict, Macaroon)): - raise Exception( - f"Expected macaroon_ to be a Macaroon, received: {type(macaroon_)}" - ) - - if resolve_ is not None and not isinstance(resolve_, (bytes, str, list)): - raise Exception( - f"Expected resolve_ to be a Sequence, received: {type(resolve_)}" - ) - - self.macaroon = macaroon_ - self.resolve = resolve_ - self.unknown_fields = unknown_fields - - -class Resource(Type): - _toSchema = { - "application": "application", - "charmresource": "CharmResource", - "description": "description", - "fingerprint": "fingerprint", - "id_": "id", - "name": "name", - "origin": "origin", - "path": "path", - "pending_id": "pending-id", - "revision": "revision", - "size": "size", - "timestamp": "timestamp", - "type_": "type", - "username": "username", - } - _toPy = { - "CharmResource": "charmresource", - "application": "application", - "description": "description", - "fingerprint": "fingerprint", - "id": "id_", - "name": "name", - "origin": "origin", - "path": "path", - "pending-id": "pending_id", - "revision": "revision", - "size": "size", - "timestamp": "timestamp", - "type": "type_", - "username": "username", - } - - def __init__( - self, - charmresource=None, - application=None, - description=None, - fingerprint=None, - id_=None, - name=None, - origin=None, - path=None, - pending_id=None, - revision=None, - size=None, - timestamp=None, - type_=None, - username=None, - **unknown_fields, - ): - """Charmresource : CharmResource - application : str - description : str - fingerprint : typing.Sequence[int] - id_ : str - name : str - origin : str - path : str - pending_id : str - revision : int - size : int - timestamp : str - type_ : str - username : str - """ - charmresource_ = ( - CharmResource.from_json(charmresource) if charmresource else None - ) - application_ = application - description_ = description - fingerprint_ = fingerprint - id__ = id_ - name_ = name - origin_ = origin - path_ = path - pending_id_ = pending_id - revision_ = revision - size_ = size - timestamp_ = timestamp - type__ = type_ - username_ = username - - # Validate arguments against known Juju API types. - if charmresource_ is not None and not isinstance( - charmresource_, (dict, CharmResource) - ): - raise Exception( - f"Expected charmresource_ to be a CharmResource, received: {type(charmresource_)}" - ) - - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if fingerprint_ is not None and not isinstance( - fingerprint_, (bytes, str, list) - ): - raise Exception( - f"Expected fingerprint_ to be a Sequence, received: {type(fingerprint_)}" - ) - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if origin_ is not None and not isinstance(origin_, (bytes, str)): - raise Exception(f"Expected origin_ to be a str, received: {type(origin_)}") - - if path_ is not None and not isinstance(path_, (bytes, str)): - raise Exception(f"Expected path_ to be a str, received: {type(path_)}") - - if pending_id_ is not None and not isinstance(pending_id_, (bytes, str)): - raise Exception( - f"Expected pending_id_ to be a str, received: {type(pending_id_)}" - ) - - if revision_ is not None and not isinstance(revision_, int): - raise Exception( - f"Expected revision_ to be a int, received: {type(revision_)}" - ) - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - if timestamp_ is not None and not isinstance(timestamp_, (bytes, str)): - raise Exception( - f"Expected timestamp_ to be a str, received: {type(timestamp_)}" - ) - - if type__ is not None and not isinstance(type__, (bytes, str)): - raise Exception(f"Expected type__ to be a str, received: {type(type__)}") - - if username_ is not None and not isinstance(username_, (bytes, str)): - raise Exception( - f"Expected username_ to be a str, received: {type(username_)}" - ) - - self.charmresource = charmresource_ - self.application = application_ - self.description = description_ - self.fingerprint = fingerprint_ - self.id_ = id__ - self.name = name_ - self.origin = origin_ - self.path = path_ - self.pending_id = pending_id_ - self.revision = revision_ - self.size = size_ - self.timestamp = timestamp_ - self.type_ = type__ - self.username = username_ - self.unknown_fields = unknown_fields - - -class ResourcesResult(Type): - _toSchema = { - "charm_store_resources": "charm-store-resources", - "error": "error", - "errorresult": "ErrorResult", - "resources": "resources", - "unit_resources": "unit-resources", - } - _toPy = { - "ErrorResult": "errorresult", - "charm-store-resources": "charm_store_resources", - "error": "error", - "resources": "resources", - "unit-resources": "unit_resources", - } - - def __init__( - self, - errorresult=None, - charm_store_resources=None, - error=None, - resources=None, - unit_resources=None, - **unknown_fields, - ): - """Errorresult : ErrorResult - charm_store_resources : typing.Sequence[~CharmResource] - error : Error - resources : typing.Sequence[~Resource] - unit_resources : typing.Sequence[~UnitResources] - """ - errorresult_ = ErrorResult.from_json(errorresult) if errorresult else None - charm_store_resources_ = [ - CharmResource.from_json(o) for o in charm_store_resources or [] - ] - error_ = Error.from_json(error) if error else None - resources_ = [Resource.from_json(o) for o in resources or []] - unit_resources_ = [UnitResources.from_json(o) for o in unit_resources or []] - - # Validate arguments against known Juju API types. - if errorresult_ is not None and not isinstance( - errorresult_, (dict, ErrorResult) - ): - raise Exception( - f"Expected errorresult_ to be a ErrorResult, received: {type(errorresult_)}" - ) - - if charm_store_resources_ is not None and not isinstance( - charm_store_resources_, (bytes, str, list) - ): - raise Exception( - f"Expected charm_store_resources_ to be a Sequence, received: {type(charm_store_resources_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if resources_ is not None and not isinstance(resources_, (bytes, str, list)): - raise Exception( - f"Expected resources_ to be a Sequence, received: {type(resources_)}" - ) - - if unit_resources_ is not None and not isinstance( - unit_resources_, (bytes, str, list) - ): - raise Exception( - f"Expected unit_resources_ to be a Sequence, received: {type(unit_resources_)}" - ) - - self.errorresult = errorresult_ - self.charm_store_resources = charm_store_resources_ - self.error = error_ - self.resources = resources_ - self.unit_resources = unit_resources_ - self.unknown_fields = unknown_fields - - -class ResourcesResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ResourcesResult]""" - results_ = [ResourcesResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class RetryProvisioningArgs(Type): - _toSchema = {"all_": "all", "machines": "machines"} - _toPy = {"all": "all_", "machines": "machines"} - - def __init__(self, all_=None, machines=None, **unknown_fields): - """all_ : bool - machines : typing.Sequence[str] - """ - all__ = all_ - machines_ = machines - - # Validate arguments against known Juju API types. - if all__ is not None and not isinstance(all__, bool): - raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") - - if machines_ is not None and not isinstance(machines_, (bytes, str, list)): - raise Exception( - f"Expected machines_ to be a Sequence, received: {type(machines_)}" - ) - - self.all_ = all__ - self.machines = machines_ - self.unknown_fields = unknown_fields - - -class RevokeCredentialArg(Type): - _toSchema = {"force": "force", "tag": "tag"} - _toPy = {"force": "force", "tag": "tag"} - - def __init__(self, force=None, tag=None, **unknown_fields): - """Force : bool - tag : str - """ - force_ = force - tag_ = tag - - # Validate arguments against known Juju API types. - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.force = force_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class RevokeCredentialArgs(Type): - _toSchema = {"credentials": "credentials"} - _toPy = {"credentials": "credentials"} - - def __init__(self, credentials=None, **unknown_fields): - """Credentials : typing.Sequence[~RevokeCredentialArg]""" - credentials_ = [RevokeCredentialArg.from_json(o) for o in credentials or []] - - # Validate arguments against known Juju API types. - if credentials_ is not None and not isinstance( - credentials_, (bytes, str, list) - ): - raise Exception( - f"Expected credentials_ to be a Sequence, received: {type(credentials_)}" - ) - - self.credentials = credentials_ - self.unknown_fields = unknown_fields - - -class RunParams(Type): - _toSchema = { - "applications": "applications", - "commands": "commands", - "execution_group": "execution-group", - "machines": "machines", - "parallel": "parallel", - "timeout": "timeout", - "units": "units", - "workload_context": "workload-context", - } - _toPy = { - "applications": "applications", - "commands": "commands", - "execution-group": "execution_group", - "machines": "machines", - "parallel": "parallel", - "timeout": "timeout", - "units": "units", - "workload-context": "workload_context", - } - - def __init__( - self, - applications=None, - commands=None, - execution_group=None, - machines=None, - parallel=None, - timeout=None, - units=None, - workload_context=None, - **unknown_fields, - ): - """Applications : typing.Sequence[str] - commands : str - execution_group : str - machines : typing.Sequence[str] - parallel : bool - timeout : int - units : typing.Sequence[str] - workload_context : bool - """ - applications_ = applications - commands_ = commands - execution_group_ = execution_group - machines_ = machines - parallel_ = parallel - timeout_ = timeout - units_ = units - workload_context_ = workload_context - - # Validate arguments against known Juju API types. - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - if commands_ is not None and not isinstance(commands_, (bytes, str)): - raise Exception( - f"Expected commands_ to be a str, received: {type(commands_)}" - ) - - if execution_group_ is not None and not isinstance( - execution_group_, (bytes, str) - ): - raise Exception( - f"Expected execution_group_ to be a str, received: {type(execution_group_)}" - ) - - if machines_ is not None and not isinstance(machines_, (bytes, str, list)): - raise Exception( - f"Expected machines_ to be a Sequence, received: {type(machines_)}" - ) - - if parallel_ is not None and not isinstance(parallel_, bool): - raise Exception( - f"Expected parallel_ to be a bool, received: {type(parallel_)}" - ) - - if timeout_ is not None and not isinstance(timeout_, int): - raise Exception( - f"Expected timeout_ to be a int, received: {type(timeout_)}" - ) - - if units_ is not None and not isinstance(units_, (bytes, str, list)): - raise Exception( - f"Expected units_ to be a Sequence, received: {type(units_)}" - ) - - if workload_context_ is not None and not isinstance(workload_context_, bool): - raise Exception( - f"Expected workload_context_ to be a bool, received: {type(workload_context_)}" - ) - - self.applications = applications_ - self.commands = commands_ - self.execution_group = execution_group_ - self.machines = machines_ - self.parallel = parallel_ - self.timeout = timeout_ - self.units = units_ - self.workload_context = workload_context_ - self.unknown_fields = unknown_fields - - -class SSHAddressResult(Type): - _toSchema = {"address": "address", "error": "error"} - _toPy = {"address": "address", "error": "error"} - - def __init__(self, address=None, error=None, **unknown_fields): - """Address : str - error : Error - """ - address_ = address - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if address_ is not None and not isinstance(address_, (bytes, str)): - raise Exception( - f"Expected address_ to be a str, received: {type(address_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.address = address_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class SSHAddressResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~SSHAddressResult]""" - results_ = [SSHAddressResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class SSHAddressesResult(Type): - _toSchema = {"addresses": "addresses", "error": "error"} - _toPy = {"addresses": "addresses", "error": "error"} - - def __init__(self, addresses=None, error=None, **unknown_fields): - """Addresses : typing.Sequence[str] - error : Error - """ - addresses_ = addresses - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if addresses_ is not None and not isinstance(addresses_, (bytes, str, list)): - raise Exception( - f"Expected addresses_ to be a Sequence, received: {type(addresses_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.addresses = addresses_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class SSHAddressesResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~SSHAddressesResult]""" - results_ = [SSHAddressesResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class SSHProxyResult(Type): - _toSchema = {"use_proxy": "use-proxy"} - _toPy = {"use-proxy": "use_proxy"} - - def __init__(self, use_proxy=None, **unknown_fields): - """use_proxy : bool""" - use_proxy_ = use_proxy - - # Validate arguments against known Juju API types. - if use_proxy_ is not None and not isinstance(use_proxy_, bool): - raise Exception( - f"Expected use_proxy_ to be a bool, received: {type(use_proxy_)}" - ) - - self.use_proxy = use_proxy_ - self.unknown_fields = unknown_fields - - -class SSHPublicKeysResult(Type): - _toSchema = {"error": "error", "public_keys": "public-keys"} - _toPy = {"error": "error", "public-keys": "public_keys"} - - def __init__(self, error=None, public_keys=None, **unknown_fields): - """Error : Error - public_keys : typing.Sequence[str] - """ - error_ = Error.from_json(error) if error else None - public_keys_ = public_keys - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if public_keys_ is not None and not isinstance( - public_keys_, (bytes, str, list) - ): - raise Exception( - f"Expected public_keys_ to be a Sequence, received: {type(public_keys_)}" - ) - - self.error = error_ - self.public_keys = public_keys_ - self.unknown_fields = unknown_fields - - -class SSHPublicKeysResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~SSHPublicKeysResult]""" - results_ = [SSHPublicKeysResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ScaleApplicationInfo(Type): - _toSchema = {"num_units": "num-units"} - _toPy = {"num-units": "num_units"} - - def __init__(self, num_units=None, **unknown_fields): - """num_units : int""" - num_units_ = num_units - - # Validate arguments against known Juju API types. - if num_units_ is not None and not isinstance(num_units_, int): - raise Exception( - f"Expected num_units_ to be a int, received: {type(num_units_)}" - ) - - self.num_units = num_units_ - self.unknown_fields = unknown_fields - - -class ScaleApplicationParams(Type): - _toSchema = { - "application_tag": "application-tag", - "force": "force", - "scale": "scale", - "scale_change": "scale-change", - } - _toPy = { - "application-tag": "application_tag", - "force": "force", - "scale": "scale", - "scale-change": "scale_change", - } - - def __init__( - self, - application_tag=None, - force=None, - scale=None, - scale_change=None, - **unknown_fields, - ): - """application_tag : str - force : bool - scale : int - scale_change : int - """ - application_tag_ = application_tag - force_ = force - scale_ = scale - scale_change_ = scale_change - - # Validate arguments against known Juju API types. - if application_tag_ is not None and not isinstance( - application_tag_, (bytes, str) - ): - raise Exception( - f"Expected application_tag_ to be a str, received: {type(application_tag_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if scale_ is not None and not isinstance(scale_, int): - raise Exception(f"Expected scale_ to be a int, received: {type(scale_)}") - - if scale_change_ is not None and not isinstance(scale_change_, int): - raise Exception( - f"Expected scale_change_ to be a int, received: {type(scale_change_)}" - ) - - self.application_tag = application_tag_ - self.force = force_ - self.scale = scale_ - self.scale_change = scale_change_ - self.unknown_fields = unknown_fields - - -class ScaleApplicationResult(Type): - _toSchema = {"error": "error", "info": "info"} - _toPy = {"error": "error", "info": "info"} - - def __init__(self, error=None, info=None, **unknown_fields): - """Error : Error - info : ScaleApplicationInfo - """ - error_ = Error.from_json(error) if error else None - info_ = ScaleApplicationInfo.from_json(info) if info else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if info_ is not None and not isinstance(info_, (dict, ScaleApplicationInfo)): - raise Exception( - f"Expected info_ to be a ScaleApplicationInfo, received: {type(info_)}" - ) - - self.error = error_ - self.info = info_ - self.unknown_fields = unknown_fields - - -class ScaleApplicationResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ScaleApplicationResult]""" - results_ = [ScaleApplicationResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class ScaleApplicationsParams(Type): - _toSchema = {"applications": "applications"} - _toPy = {"applications": "applications"} - - def __init__(self, applications=None, **unknown_fields): - """Applications : typing.Sequence[~ScaleApplicationParams]""" - applications_ = [ - ScaleApplicationParams.from_json(o) for o in applications or [] - ] - - # Validate arguments against known Juju API types. - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - self.applications = applications_ - self.unknown_fields = unknown_fields - - -class SecretBackend(Type): - _toSchema = { - "backend_type": "backend-type", - "config": "config", - "name": "name", - "token_rotate_interval": "token-rotate-interval", - } - _toPy = { - "backend-type": "backend_type", - "config": "config", - "name": "name", - "token-rotate-interval": "token_rotate_interval", - } - - def __init__( - self, - backend_type=None, - config=None, - name=None, - token_rotate_interval=None, - **unknown_fields, - ): - """backend_type : str - config : typing.Mapping[str, typing.Any] - name : str - token_rotate_interval : int - """ - backend_type_ = backend_type - config_ = config - name_ = name - token_rotate_interval_ = token_rotate_interval - - # Validate arguments against known Juju API types. - if backend_type_ is not None and not isinstance(backend_type_, (bytes, str)): - raise Exception( - f"Expected backend_type_ to be a str, received: {type(backend_type_)}" - ) - - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if token_rotate_interval_ is not None and not isinstance( - token_rotate_interval_, int - ): - raise Exception( - f"Expected token_rotate_interval_ to be a int, received: {type(token_rotate_interval_)}" - ) - - self.backend_type = backend_type_ - self.config = config_ - self.name = name_ - self.token_rotate_interval = token_rotate_interval_ - self.unknown_fields = unknown_fields - - -class SecretBackendResult(Type): - _toSchema = { - "error": "error", - "id_": "id", - "message": "message", - "num_secrets": "num-secrets", - "result": "result", - "status": "status", - } - _toPy = { - "error": "error", - "id": "id_", - "message": "message", - "num-secrets": "num_secrets", - "result": "result", - "status": "status", - } - - def __init__( - self, - error=None, - id_=None, - message=None, - num_secrets=None, - result=None, - status=None, - **unknown_fields, - ): - """Error : Error - id_ : str - message : str - num_secrets : int - result : SecretBackend - status : str - """ - error_ = Error.from_json(error) if error else None - id__ = id_ - message_ = message - num_secrets_ = num_secrets - result_ = SecretBackend.from_json(result) if result else None - status_ = status - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if message_ is not None and not isinstance(message_, (bytes, str)): - raise Exception( - f"Expected message_ to be a str, received: {type(message_)}" - ) - - if num_secrets_ is not None and not isinstance(num_secrets_, int): - raise Exception( - f"Expected num_secrets_ to be a int, received: {type(num_secrets_)}" - ) - - if result_ is not None and not isinstance(result_, (dict, SecretBackend)): - raise Exception( - f"Expected result_ to be a SecretBackend, received: {type(result_)}" - ) - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - self.error = error_ - self.id_ = id__ - self.message = message_ - self.num_secrets = num_secrets_ - self.result = result_ - self.status = status_ - self.unknown_fields = unknown_fields - - -class SecretContentParams(Type): - _toSchema = {"checksum": "checksum", "data": "data", "value_ref": "value-ref"} - _toPy = {"checksum": "checksum", "data": "data", "value-ref": "value_ref"} - - def __init__(self, checksum=None, data=None, value_ref=None, **unknown_fields): - """Checksum : str - data : typing.Mapping[str, str] - value_ref : SecretValueRef - """ - checksum_ = checksum - data_ = data - value_ref_ = SecretValueRef.from_json(value_ref) if value_ref else None - - # Validate arguments against known Juju API types. - if checksum_ is not None and not isinstance(checksum_, (bytes, str)): - raise Exception( - f"Expected checksum_ to be a str, received: {type(checksum_)}" - ) - - if data_ is not None and not isinstance(data_, dict): - raise Exception(f"Expected data_ to be a Mapping, received: {type(data_)}") - - if value_ref_ is not None and not isinstance( - value_ref_, (dict, SecretValueRef) - ): - raise Exception( - f"Expected value_ref_ to be a SecretValueRef, received: {type(value_ref_)}" - ) - - self.checksum = checksum_ - self.data = data_ - self.value_ref = value_ref_ - self.unknown_fields = unknown_fields - - -class SecretRevision(Type): - _toSchema = { - "backend_name": "backend-name", - "create_time": "create-time", - "expire_time": "expire-time", - "revision": "revision", - "update_time": "update-time", - "value_ref": "value-ref", - } - _toPy = { - "backend-name": "backend_name", - "create-time": "create_time", - "expire-time": "expire_time", - "revision": "revision", - "update-time": "update_time", - "value-ref": "value_ref", - } - - def __init__( - self, - backend_name=None, - create_time=None, - expire_time=None, - revision=None, - update_time=None, - value_ref=None, - **unknown_fields, - ): - """backend_name : str - create_time : str - expire_time : str - revision : int - update_time : str - value_ref : SecretValueRef - """ - backend_name_ = backend_name - create_time_ = create_time - expire_time_ = expire_time - revision_ = revision - update_time_ = update_time - value_ref_ = SecretValueRef.from_json(value_ref) if value_ref else None - - # Validate arguments against known Juju API types. - if backend_name_ is not None and not isinstance(backend_name_, (bytes, str)): - raise Exception( - f"Expected backend_name_ to be a str, received: {type(backend_name_)}" - ) - - if create_time_ is not None and not isinstance(create_time_, (bytes, str)): - raise Exception( - f"Expected create_time_ to be a str, received: {type(create_time_)}" - ) - - if expire_time_ is not None and not isinstance(expire_time_, (bytes, str)): - raise Exception( - f"Expected expire_time_ to be a str, received: {type(expire_time_)}" - ) - - if revision_ is not None and not isinstance(revision_, int): - raise Exception( - f"Expected revision_ to be a int, received: {type(revision_)}" - ) - - if update_time_ is not None and not isinstance(update_time_, (bytes, str)): - raise Exception( - f"Expected update_time_ to be a str, received: {type(update_time_)}" - ) - - if value_ref_ is not None and not isinstance( - value_ref_, (dict, SecretValueRef) - ): - raise Exception( - f"Expected value_ref_ to be a SecretValueRef, received: {type(value_ref_)}" - ) - - self.backend_name = backend_name_ - self.create_time = create_time_ - self.expire_time = expire_time_ - self.revision = revision_ - self.update_time = update_time_ - self.value_ref = value_ref_ - self.unknown_fields = unknown_fields - - -class SecretValueRef(Type): - _toSchema = {"backend_id": "backend-id", "revision_id": "revision-id"} - _toPy = {"backend-id": "backend_id", "revision-id": "revision_id"} - - def __init__(self, backend_id=None, revision_id=None, **unknown_fields): - """backend_id : str - revision_id : str - """ - backend_id_ = backend_id - revision_id_ = revision_id - - # Validate arguments against known Juju API types. - if backend_id_ is not None and not isinstance(backend_id_, (bytes, str)): - raise Exception( - f"Expected backend_id_ to be a str, received: {type(backend_id_)}" - ) - - if revision_id_ is not None and not isinstance(revision_id_, (bytes, str)): - raise Exception( - f"Expected revision_id_ to be a str, received: {type(revision_id_)}" - ) - - self.backend_id = backend_id_ - self.revision_id = revision_id_ - self.unknown_fields = unknown_fields - - -class SecretValueResult(Type): - _toSchema = {"data": "data", "error": "error"} - _toPy = {"data": "data", "error": "error"} - - def __init__(self, data=None, error=None, **unknown_fields): - """Data : typing.Mapping[str, str] - error : Error - """ - data_ = data - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if data_ is not None and not isinstance(data_, dict): - raise Exception(f"Expected data_ to be a Mapping, received: {type(data_)}") - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.data = data_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class SecretsFilter(Type): - _toSchema = { - "label": "label", - "owner_tag": "owner-tag", - "revision": "revision", - "uri": "uri", - } - _toPy = { - "label": "label", - "owner-tag": "owner_tag", - "revision": "revision", - "uri": "uri", - } - - def __init__( - self, label=None, owner_tag=None, revision=None, uri=None, **unknown_fields - ): - """Label : str - owner_tag : str - revision : int - uri : str - """ - label_ = label - owner_tag_ = owner_tag - revision_ = revision - uri_ = uri - - # Validate arguments against known Juju API types. - if label_ is not None and not isinstance(label_, (bytes, str)): - raise Exception(f"Expected label_ to be a str, received: {type(label_)}") - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - if revision_ is not None and not isinstance(revision_, int): - raise Exception( - f"Expected revision_ to be a int, received: {type(revision_)}" - ) - - if uri_ is not None and not isinstance(uri_, (bytes, str)): - raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") - - self.label = label_ - self.owner_tag = owner_tag_ - self.revision = revision_ - self.uri = uri_ - self.unknown_fields = unknown_fields - - -class SetConstraints(Type): - _toSchema = {"application": "application", "constraints": "constraints"} - _toPy = {"application": "application", "constraints": "constraints"} - - def __init__(self, application=None, constraints=None, **unknown_fields): - """Application : str - constraints : Value - """ - application_ = application - constraints_ = Value.from_json(constraints) if constraints else None - - # Validate arguments against known Juju API types. - if application_ is not None and not isinstance(application_, (bytes, str)): - raise Exception( - f"Expected application_ to be a str, received: {type(application_)}" - ) - - if constraints_ is not None and not isinstance(constraints_, (dict, Value)): - raise Exception( - f"Expected constraints_ to be a Value, received: {type(constraints_)}" - ) - - self.application = application_ - self.constraints = constraints_ - self.unknown_fields = unknown_fields - - -class SetModelDefaults(Type): - _toSchema = {"config": "config"} - _toPy = {"config": "config"} - - def __init__(self, config=None, **unknown_fields): - """Config : typing.Sequence[~ModelDefaultValues]""" - config_ = [ModelDefaultValues.from_json(o) for o in config or []] - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, (bytes, str, list)): - raise Exception( - f"Expected config_ to be a Sequence, received: {type(config_)}" - ) - - self.config = config_ - self.unknown_fields = unknown_fields - - -class ShowSpaceResult(Type): - _toSchema = { - "applications": "applications", - "error": "error", - "machine_count": "machine-count", - "space": "space", - } - _toPy = { - "applications": "applications", - "error": "error", - "machine-count": "machine_count", - "space": "space", - } - - def __init__( - self, - applications=None, - error=None, - machine_count=None, - space=None, - **unknown_fields, - ): - """Applications : typing.Sequence[str] - error : Error - machine_count : int - space : Space - """ - applications_ = applications - error_ = Error.from_json(error) if error else None - machine_count_ = machine_count - space_ = Space.from_json(space) if space else None - - # Validate arguments against known Juju API types. - if applications_ is not None and not isinstance( - applications_, (bytes, str, list) - ): - raise Exception( - f"Expected applications_ to be a Sequence, received: {type(applications_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if machine_count_ is not None and not isinstance(machine_count_, int): - raise Exception( - f"Expected machine_count_ to be a int, received: {type(machine_count_)}" - ) - - if space_ is not None and not isinstance(space_, (dict, Space)): - raise Exception(f"Expected space_ to be a Space, received: {type(space_)}") - - self.applications = applications_ - self.error = error_ - self.machine_count = machine_count_ - self.space = space_ - self.unknown_fields = unknown_fields - - -class ShowSpaceResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ShowSpaceResult]""" - results_ = [ShowSpaceResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class Space(Type): - _toSchema = {"error": "error", "id_": "id", "name": "name", "subnets": "subnets"} - _toPy = {"error": "error", "id": "id_", "name": "name", "subnets": "subnets"} - - def __init__(self, error=None, id_=None, name=None, subnets=None, **unknown_fields): - """Error : Error - id_ : str - name : str - subnets : typing.Sequence[~Subnet] - """ - error_ = Error.from_json(error) if error else None - id__ = id_ - name_ = name - subnets_ = [Subnet.from_json(o) for o in subnets or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if subnets_ is not None and not isinstance(subnets_, (bytes, str, list)): - raise Exception( - f"Expected subnets_ to be a Sequence, received: {type(subnets_)}" - ) - - self.error = error_ - self.id_ = id__ - self.name = name_ - self.subnets = subnets_ - self.unknown_fields = unknown_fields - - -class StatusHistoryFilter(Type): - _toSchema = {"date": "date", "delta": "delta", "exclude": "exclude", "size": "size"} - _toPy = {"date": "date", "delta": "delta", "exclude": "exclude", "size": "size"} - - def __init__( - self, date=None, delta=None, exclude=None, size=None, **unknown_fields - ): - """Date : str - delta : int - exclude : typing.Sequence[str] - size : int - """ - date_ = date - delta_ = delta - exclude_ = exclude - size_ = size - - # Validate arguments against known Juju API types. - if date_ is not None and not isinstance(date_, (bytes, str)): - raise Exception(f"Expected date_ to be a str, received: {type(date_)}") - - if delta_ is not None and not isinstance(delta_, int): - raise Exception(f"Expected delta_ to be a int, received: {type(delta_)}") - - if exclude_ is not None and not isinstance(exclude_, (bytes, str, list)): - raise Exception( - f"Expected exclude_ to be a Sequence, received: {type(exclude_)}" - ) - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - self.date = date_ - self.delta = delta_ - self.exclude = exclude_ - self.size = size_ - self.unknown_fields = unknown_fields - - -class StatusHistoryRequest(Type): - _toSchema = { - "filter_": "filter", - "historykind": "historyKind", - "size": "size", - "tag": "tag", - } - _toPy = { - "filter": "filter_", - "historyKind": "historykind", - "size": "size", - "tag": "tag", - } - - def __init__( - self, filter_=None, historykind=None, size=None, tag=None, **unknown_fields - ): - """filter_ : StatusHistoryFilter - historykind : str - size : int - tag : str - """ - filter__ = StatusHistoryFilter.from_json(filter_) if filter_ else None - historykind_ = historykind - size_ = size - tag_ = tag - - # Validate arguments against known Juju API types. - if filter__ is not None and not isinstance( - filter__, (dict, StatusHistoryFilter) - ): - raise Exception( - f"Expected filter__ to be a StatusHistoryFilter, received: {type(filter__)}" - ) - - if historykind_ is not None and not isinstance(historykind_, (bytes, str)): - raise Exception( - f"Expected historykind_ to be a str, received: {type(historykind_)}" - ) - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.filter_ = filter__ - self.historykind = historykind_ - self.size = size_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class StatusHistoryRequests(Type): - _toSchema = {"requests": "requests"} - _toPy = {"requests": "requests"} - - def __init__(self, requests=None, **unknown_fields): - """Requests : typing.Sequence[~StatusHistoryRequest]""" - requests_ = [StatusHistoryRequest.from_json(o) for o in requests or []] - - # Validate arguments against known Juju API types. - if requests_ is not None and not isinstance(requests_, (bytes, str, list)): - raise Exception( - f"Expected requests_ to be a Sequence, received: {type(requests_)}" - ) - - self.requests = requests_ - self.unknown_fields = unknown_fields - - -class StatusHistoryResult(Type): - _toSchema = {"error": "error", "history": "history"} - _toPy = {"error": "error", "history": "history"} - - def __init__(self, error=None, history=None, **unknown_fields): - """Error : Error - history : History - """ - error_ = Error.from_json(error) if error else None - history_ = History.from_json(history) if history else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if history_ is not None and not isinstance(history_, (dict, History)): - raise Exception( - f"Expected history_ to be a History, received: {type(history_)}" - ) - - self.error = error_ - self.history = history_ - self.unknown_fields = unknown_fields - - -class StatusHistoryResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~StatusHistoryResult]""" - results_ = [StatusHistoryResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class StatusParams(Type): - _toSchema = {"include_storage": "include-storage", "patterns": "patterns"} - _toPy = {"include-storage": "include_storage", "patterns": "patterns"} - - def __init__(self, include_storage=None, patterns=None, **unknown_fields): - """include_storage : bool - patterns : typing.Sequence[str] - """ - include_storage_ = include_storage - patterns_ = patterns - - # Validate arguments against known Juju API types. - if include_storage_ is not None and not isinstance(include_storage_, bool): - raise Exception( - f"Expected include_storage_ to be a bool, received: {type(include_storage_)}" - ) - - if patterns_ is not None and not isinstance(patterns_, (bytes, str, list)): - raise Exception( - f"Expected patterns_ to be a Sequence, received: {type(patterns_)}" - ) - - self.include_storage = include_storage_ - self.patterns = patterns_ - self.unknown_fields = unknown_fields - - -class StorageAddParams(Type): - _toSchema = {"name": "name", "storage": "storage", "unit": "unit"} - _toPy = {"name": "name", "storage": "storage", "unit": "unit"} - - def __init__(self, name=None, storage=None, unit=None, **unknown_fields): - """Name : str - storage : StorageConstraints - unit : str - """ - name_ = name - storage_ = StorageConstraints.from_json(storage) if storage else None - unit_ = unit - - # Validate arguments against known Juju API types. - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if storage_ is not None and not isinstance( - storage_, (dict, StorageConstraints) - ): - raise Exception( - f"Expected storage_ to be a StorageConstraints, received: {type(storage_)}" - ) - - if unit_ is not None and not isinstance(unit_, (bytes, str)): - raise Exception(f"Expected unit_ to be a str, received: {type(unit_)}") - - self.name = name_ - self.storage = storage_ - self.unit = unit_ - self.unknown_fields = unknown_fields - - -class StorageAttachmentDetails(Type): - _toSchema = { - "life": "life", - "location": "location", - "machine_tag": "machine-tag", - "storage_tag": "storage-tag", - "unit_tag": "unit-tag", - } - _toPy = { - "life": "life", - "location": "location", - "machine-tag": "machine_tag", - "storage-tag": "storage_tag", - "unit-tag": "unit_tag", - } - - def __init__( - self, - life=None, - location=None, - machine_tag=None, - storage_tag=None, - unit_tag=None, - **unknown_fields, - ): - """Life : str - location : str - machine_tag : str - storage_tag : str - unit_tag : str - """ - life_ = life - location_ = location - machine_tag_ = machine_tag - storage_tag_ = storage_tag - unit_tag_ = unit_tag - - # Validate arguments against known Juju API types. - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if location_ is not None and not isinstance(location_, (bytes, str)): - raise Exception( - f"Expected location_ to be a str, received: {type(location_)}" - ) - - if machine_tag_ is not None and not isinstance(machine_tag_, (bytes, str)): - raise Exception( - f"Expected machine_tag_ to be a str, received: {type(machine_tag_)}" - ) - - if storage_tag_ is not None and not isinstance(storage_tag_, (bytes, str)): - raise Exception( - f"Expected storage_tag_ to be a str, received: {type(storage_tag_)}" - ) - - if unit_tag_ is not None and not isinstance(unit_tag_, (bytes, str)): - raise Exception( - f"Expected unit_tag_ to be a str, received: {type(unit_tag_)}" - ) - - self.life = life_ - self.location = location_ - self.machine_tag = machine_tag_ - self.storage_tag = storage_tag_ - self.unit_tag = unit_tag_ - self.unknown_fields = unknown_fields - - -class StorageAttachmentId(Type): - _toSchema = {"storage_tag": "storage-tag", "unit_tag": "unit-tag"} - _toPy = {"storage-tag": "storage_tag", "unit-tag": "unit_tag"} - - def __init__(self, storage_tag=None, unit_tag=None, **unknown_fields): - """storage_tag : str - unit_tag : str - """ - storage_tag_ = storage_tag - unit_tag_ = unit_tag - - # Validate arguments against known Juju API types. - if storage_tag_ is not None and not isinstance(storage_tag_, (bytes, str)): - raise Exception( - f"Expected storage_tag_ to be a str, received: {type(storage_tag_)}" - ) - - if unit_tag_ is not None and not isinstance(unit_tag_, (bytes, str)): - raise Exception( - f"Expected unit_tag_ to be a str, received: {type(unit_tag_)}" - ) - - self.storage_tag = storage_tag_ - self.unit_tag = unit_tag_ - self.unknown_fields = unknown_fields - - -class StorageAttachmentIds(Type): - _toSchema = {"ids": "ids"} - _toPy = {"ids": "ids"} - - def __init__(self, ids=None, **unknown_fields): - """Ids : typing.Sequence[~StorageAttachmentId]""" - ids_ = [StorageAttachmentId.from_json(o) for o in ids or []] - - # Validate arguments against known Juju API types. - if ids_ is not None and not isinstance(ids_, (bytes, str, list)): - raise Exception(f"Expected ids_ to be a Sequence, received: {type(ids_)}") - - self.ids = ids_ - self.unknown_fields = unknown_fields - - -class StorageConstraints(Type): - _toSchema = {"count": "count", "pool": "pool", "size": "size"} - _toPy = {"count": "count", "pool": "pool", "size": "size"} - - def __init__(self, count=None, pool=None, size=None, **unknown_fields): - """Count : int - pool : str - size : int - """ - count_ = count - pool_ = pool - size_ = size - - # Validate arguments against known Juju API types. - if count_ is not None and not isinstance(count_, int): - raise Exception(f"Expected count_ to be a int, received: {type(count_)}") - - if pool_ is not None and not isinstance(pool_, (bytes, str)): - raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - self.count = count_ - self.pool = pool_ - self.size = size_ - self.unknown_fields = unknown_fields - - -class StorageDetachmentParams(Type): - _toSchema = {"force": "force", "ids": "ids", "max_wait": "max-wait"} - _toPy = {"force": "force", "ids": "ids", "max-wait": "max_wait"} - - def __init__(self, force=None, ids=None, max_wait=None, **unknown_fields): - """Force : bool - ids : StorageAttachmentIds - max_wait : int - """ - force_ = force - ids_ = StorageAttachmentIds.from_json(ids) if ids else None - max_wait_ = max_wait - - # Validate arguments against known Juju API types. - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if ids_ is not None and not isinstance(ids_, (dict, StorageAttachmentIds)): - raise Exception( - f"Expected ids_ to be a StorageAttachmentIds, received: {type(ids_)}" - ) - - if max_wait_ is not None and not isinstance(max_wait_, int): - raise Exception( - f"Expected max_wait_ to be a int, received: {type(max_wait_)}" - ) - - self.force = force_ - self.ids = ids_ - self.max_wait = max_wait_ - self.unknown_fields = unknown_fields - - -class StorageDetails(Type): - _toSchema = { - "attachments": "attachments", - "kind": "kind", - "life": "life", - "owner_tag": "owner-tag", - "persistent": "persistent", - "status": "status", - "storage_tag": "storage-tag", - } - _toPy = { - "attachments": "attachments", - "kind": "kind", - "life": "life", - "owner-tag": "owner_tag", - "persistent": "persistent", - "status": "status", - "storage-tag": "storage_tag", - } - - def __init__( - self, - attachments=None, - kind=None, - life=None, - owner_tag=None, - persistent=None, - status=None, - storage_tag=None, - **unknown_fields, - ): - """Attachments : typing.Mapping[str, ~StorageAttachmentDetails] - kind : int - life : str - owner_tag : str - persistent : bool - status : EntityStatus - storage_tag : str - """ - attachments_ = { - k: StorageAttachmentDetails.from_json(v) - for k, v in (attachments or dict()).items() - } - kind_ = kind - life_ = life - owner_tag_ = owner_tag - persistent_ = persistent - status_ = EntityStatus.from_json(status) if status else None - storage_tag_ = storage_tag - - # Validate arguments against known Juju API types. - if attachments_ is not None and not isinstance(attachments_, dict): - raise Exception( - f"Expected attachments_ to be a Mapping, received: {type(attachments_)}" - ) - - if kind_ is not None and not isinstance(kind_, int): - raise Exception(f"Expected kind_ to be a int, received: {type(kind_)}") - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if owner_tag_ is not None and not isinstance(owner_tag_, (bytes, str)): - raise Exception( - f"Expected owner_tag_ to be a str, received: {type(owner_tag_)}" - ) - - if persistent_ is not None and not isinstance(persistent_, bool): - raise Exception( - f"Expected persistent_ to be a bool, received: {type(persistent_)}" - ) - - if status_ is not None and not isinstance(status_, (dict, EntityStatus)): - raise Exception( - f"Expected status_ to be a EntityStatus, received: {type(status_)}" - ) - - if storage_tag_ is not None and not isinstance(storage_tag_, (bytes, str)): - raise Exception( - f"Expected storage_tag_ to be a str, received: {type(storage_tag_)}" - ) - - self.attachments = attachments_ - self.kind = kind_ - self.life = life_ - self.owner_tag = owner_tag_ - self.persistent = persistent_ - self.status = status_ - self.storage_tag = storage_tag_ - self.unknown_fields = unknown_fields - - -class StorageDetailsListResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : typing.Sequence[~StorageDetails] - """ - error_ = Error.from_json(error) if error else None - result_ = [StorageDetails.from_json(o) for o in result or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (bytes, str, list)): - raise Exception( - f"Expected result_ to be a Sequence, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class StorageDetailsListResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~StorageDetailsListResult]""" - results_ = [StorageDetailsListResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class StorageDetailsResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : StorageDetails - """ - error_ = Error.from_json(error) if error else None - result_ = StorageDetails.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, StorageDetails)): - raise Exception( - f"Expected result_ to be a StorageDetails, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class StorageDetailsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~StorageDetailsResult]""" - results_ = [StorageDetailsResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class StorageFilter(Type): - _toSchema = {} - _toPy = {} - - def __init__(self, **unknown_fields): - """ """ - self.unknown_fields = unknown_fields - - -class StorageFilters(Type): - _toSchema = {"filters": "filters"} - _toPy = {"filters": "filters"} - - def __init__(self, filters=None, **unknown_fields): - """Filters : typing.Sequence[~StorageFilter]""" - filters_ = [StorageFilter.from_json(o) for o in filters or []] - - # Validate arguments against known Juju API types. - if filters_ is not None and not isinstance(filters_, (bytes, str, list)): - raise Exception( - f"Expected filters_ to be a Sequence, received: {type(filters_)}" - ) - - self.filters = filters_ - self.unknown_fields = unknown_fields - - -class StoragePool(Type): - _toSchema = {"attrs": "attrs", "name": "name", "provider": "provider"} - _toPy = {"attrs": "attrs", "name": "name", "provider": "provider"} - - def __init__(self, attrs=None, name=None, provider=None, **unknown_fields): - """Attrs : typing.Mapping[str, typing.Any] - name : str - provider : str - """ - attrs_ = attrs - name_ = name - provider_ = provider - - # Validate arguments against known Juju API types. - if attrs_ is not None and not isinstance(attrs_, dict): - raise Exception( - f"Expected attrs_ to be a Mapping, received: {type(attrs_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if provider_ is not None and not isinstance(provider_, (bytes, str)): - raise Exception( - f"Expected provider_ to be a str, received: {type(provider_)}" - ) - - self.attrs = attrs_ - self.name = name_ - self.provider = provider_ - self.unknown_fields = unknown_fields - - -class StoragePoolArgs(Type): - _toSchema = {"pools": "pools"} - _toPy = {"pools": "pools"} - - def __init__(self, pools=None, **unknown_fields): - """Pools : typing.Sequence[~StoragePool]""" - pools_ = [StoragePool.from_json(o) for o in pools or []] - - # Validate arguments against known Juju API types. - if pools_ is not None and not isinstance(pools_, (bytes, str, list)): - raise Exception( - f"Expected pools_ to be a Sequence, received: {type(pools_)}" - ) - - self.pools = pools_ - self.unknown_fields = unknown_fields - - -class StoragePoolDeleteArg(Type): - _toSchema = {"name": "name"} - _toPy = {"name": "name"} - - def __init__(self, name=None, **unknown_fields): - """Name : str""" - name_ = name - - # Validate arguments against known Juju API types. - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - self.name = name_ - self.unknown_fields = unknown_fields - - -class StoragePoolDeleteArgs(Type): - _toSchema = {"pools": "pools"} - _toPy = {"pools": "pools"} - - def __init__(self, pools=None, **unknown_fields): - """Pools : typing.Sequence[~StoragePoolDeleteArg]""" - pools_ = [StoragePoolDeleteArg.from_json(o) for o in pools or []] - - # Validate arguments against known Juju API types. - if pools_ is not None and not isinstance(pools_, (bytes, str, list)): - raise Exception( - f"Expected pools_ to be a Sequence, received: {type(pools_)}" - ) - - self.pools = pools_ - self.unknown_fields = unknown_fields - - -class StoragePoolFilter(Type): - _toSchema = {"names": "names", "providers": "providers"} - _toPy = {"names": "names", "providers": "providers"} - - def __init__(self, names=None, providers=None, **unknown_fields): - """Names : typing.Sequence[str] - providers : typing.Sequence[str] - """ - names_ = names - providers_ = providers - - # Validate arguments against known Juju API types. - if names_ is not None and not isinstance(names_, (bytes, str, list)): - raise Exception( - f"Expected names_ to be a Sequence, received: {type(names_)}" - ) - - if providers_ is not None and not isinstance(providers_, (bytes, str, list)): - raise Exception( - f"Expected providers_ to be a Sequence, received: {type(providers_)}" - ) - - self.names = names_ - self.providers = providers_ - self.unknown_fields = unknown_fields - - -class StoragePoolFilters(Type): - _toSchema = {"filters": "filters"} - _toPy = {"filters": "filters"} - - def __init__(self, filters=None, **unknown_fields): - """Filters : typing.Sequence[~StoragePoolFilter]""" - filters_ = [StoragePoolFilter.from_json(o) for o in filters or []] - - # Validate arguments against known Juju API types. - if filters_ is not None and not isinstance(filters_, (bytes, str, list)): - raise Exception( - f"Expected filters_ to be a Sequence, received: {type(filters_)}" - ) - - self.filters = filters_ - self.unknown_fields = unknown_fields - - -class StoragePoolsResult(Type): - _toSchema = {"error": "error", "storage_pools": "storage-pools"} - _toPy = {"error": "error", "storage-pools": "storage_pools"} - - def __init__(self, error=None, storage_pools=None, **unknown_fields): - """Error : Error - storage_pools : typing.Sequence[~StoragePool] - """ - error_ = Error.from_json(error) if error else None - storage_pools_ = [StoragePool.from_json(o) for o in storage_pools or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if storage_pools_ is not None and not isinstance( - storage_pools_, (bytes, str, list) - ): - raise Exception( - f"Expected storage_pools_ to be a Sequence, received: {type(storage_pools_)}" - ) - - self.error = error_ - self.storage_pools = storage_pools_ - self.unknown_fields = unknown_fields - - -class StoragePoolsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~StoragePoolsResult]""" - results_ = [StoragePoolsResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class StoragesAddParams(Type): - _toSchema = {"storages": "storages"} - _toPy = {"storages": "storages"} - - def __init__(self, storages=None, **unknown_fields): - """Storages : typing.Sequence[~StorageAddParams]""" - storages_ = [StorageAddParams.from_json(o) for o in storages or []] - - # Validate arguments against known Juju API types. - if storages_ is not None and not isinstance(storages_, (bytes, str, list)): - raise Exception( - f"Expected storages_ to be a Sequence, received: {type(storages_)}" - ) - - self.storages = storages_ - self.unknown_fields = unknown_fields - - -class StringResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : str - """ - error_ = Error.from_json(error) if error else None - result_ = result - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (bytes, str)): - raise Exception(f"Expected result_ to be a str, received: {type(result_)}") - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class StringResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~StringResult]""" - results_ = [StringResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class StringsResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : typing.Sequence[str] - """ - error_ = Error.from_json(error) if error else None - result_ = result - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (bytes, str, list)): - raise Exception( - f"Expected result_ to be a Sequence, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class StringsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~StringsResult]""" - results_ = [StringsResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class StringsWatchResult(Type): - _toSchema = {"changes": "changes", "error": "error", "watcher_id": "watcher-id"} - _toPy = {"changes": "changes", "error": "error", "watcher-id": "watcher_id"} - - def __init__(self, changes=None, error=None, watcher_id=None, **unknown_fields): - """Changes : typing.Sequence[str] - error : Error - watcher_id : str - """ - changes_ = changes - error_ = Error.from_json(error) if error else None - watcher_id_ = watcher_id - - # Validate arguments against known Juju API types. - if changes_ is not None and not isinstance(changes_, (bytes, str, list)): - raise Exception( - f"Expected changes_ to be a Sequence, received: {type(changes_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if watcher_id_ is not None and not isinstance(watcher_id_, (bytes, str)): - raise Exception( - f"Expected watcher_id_ to be a str, received: {type(watcher_id_)}" - ) - - self.changes = changes_ - self.error = error_ - self.watcher_id = watcher_id_ - self.unknown_fields = unknown_fields - - -class StringsWatchResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~StringsWatchResult]""" - results_ = [StringsWatchResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class Subnet(Type): - _toSchema = { - "cidr": "cidr", - "life": "life", - "provider_id": "provider-id", - "provider_network_id": "provider-network-id", - "provider_space_id": "provider-space-id", - "space_tag": "space-tag", - "status": "status", - "vlan_tag": "vlan-tag", - "zones": "zones", - } - _toPy = { - "cidr": "cidr", - "life": "life", - "provider-id": "provider_id", - "provider-network-id": "provider_network_id", - "provider-space-id": "provider_space_id", - "space-tag": "space_tag", - "status": "status", - "vlan-tag": "vlan_tag", - "zones": "zones", - } - - def __init__( - self, - cidr=None, - life=None, - provider_id=None, - provider_network_id=None, - provider_space_id=None, - space_tag=None, - status=None, - vlan_tag=None, - zones=None, - **unknown_fields, - ): - """Cidr : str - life : str - provider_id : str - provider_network_id : str - provider_space_id : str - space_tag : str - status : str - vlan_tag : int - zones : typing.Sequence[str] - """ - cidr_ = cidr - life_ = life - provider_id_ = provider_id - provider_network_id_ = provider_network_id - provider_space_id_ = provider_space_id - space_tag_ = space_tag - status_ = status - vlan_tag_ = vlan_tag - zones_ = zones - - # Validate arguments against known Juju API types. - if cidr_ is not None and not isinstance(cidr_, (bytes, str)): - raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if provider_network_id_ is not None and not isinstance( - provider_network_id_, (bytes, str) - ): - raise Exception( - f"Expected provider_network_id_ to be a str, received: {type(provider_network_id_)}" - ) - - if provider_space_id_ is not None and not isinstance( - provider_space_id_, (bytes, str) - ): - raise Exception( - f"Expected provider_space_id_ to be a str, received: {type(provider_space_id_)}" - ) - - if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): - raise Exception( - f"Expected space_tag_ to be a str, received: {type(space_tag_)}" - ) - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - if vlan_tag_ is not None and not isinstance(vlan_tag_, int): - raise Exception( - f"Expected vlan_tag_ to be a int, received: {type(vlan_tag_)}" - ) - - if zones_ is not None and not isinstance(zones_, (bytes, str, list)): - raise Exception( - f"Expected zones_ to be a Sequence, received: {type(zones_)}" - ) - - self.cidr = cidr_ - self.life = life_ - self.provider_id = provider_id_ - self.provider_network_id = provider_network_id_ - self.provider_space_id = provider_space_id_ - self.space_tag = space_tag_ - self.status = status_ - self.vlan_tag = vlan_tag_ - self.zones = zones_ - self.unknown_fields = unknown_fields - - -class SubnetV2(Type): - _toSchema = { - "cidr": "cidr", - "id_": "id", - "life": "life", - "provider_id": "provider-id", - "provider_network_id": "provider-network-id", - "provider_space_id": "provider-space-id", - "space_tag": "space-tag", - "status": "status", - "subnet": "Subnet", - "vlan_tag": "vlan-tag", - "zones": "zones", - } - _toPy = { - "Subnet": "subnet", - "cidr": "cidr", - "id": "id_", - "life": "life", - "provider-id": "provider_id", - "provider-network-id": "provider_network_id", - "provider-space-id": "provider_space_id", - "space-tag": "space_tag", - "status": "status", - "vlan-tag": "vlan_tag", - "zones": "zones", - } - - def __init__( - self, - subnet=None, - cidr=None, - id_=None, - life=None, - provider_id=None, - provider_network_id=None, - provider_space_id=None, - space_tag=None, - status=None, - vlan_tag=None, - zones=None, - **unknown_fields, - ): - """Subnet : Subnet - cidr : str - id_ : str - life : str - provider_id : str - provider_network_id : str - provider_space_id : str - space_tag : str - status : str - vlan_tag : int - zones : typing.Sequence[str] - """ - subnet_ = Subnet.from_json(subnet) if subnet else None - cidr_ = cidr - id__ = id_ - life_ = life - provider_id_ = provider_id - provider_network_id_ = provider_network_id - provider_space_id_ = provider_space_id - space_tag_ = space_tag - status_ = status - vlan_tag_ = vlan_tag - zones_ = zones - - # Validate arguments against known Juju API types. - if subnet_ is not None and not isinstance(subnet_, (dict, Subnet)): - raise Exception( - f"Expected subnet_ to be a Subnet, received: {type(subnet_)}" - ) - - if cidr_ is not None and not isinstance(cidr_, (bytes, str)): - raise Exception(f"Expected cidr_ to be a str, received: {type(cidr_)}") - - if id__ is not None and not isinstance(id__, (bytes, str)): - raise Exception(f"Expected id__ to be a str, received: {type(id__)}") - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if provider_network_id_ is not None and not isinstance( - provider_network_id_, (bytes, str) - ): - raise Exception( - f"Expected provider_network_id_ to be a str, received: {type(provider_network_id_)}" - ) - - if provider_space_id_ is not None and not isinstance( - provider_space_id_, (bytes, str) - ): - raise Exception( - f"Expected provider_space_id_ to be a str, received: {type(provider_space_id_)}" - ) - - if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): - raise Exception( - f"Expected space_tag_ to be a str, received: {type(space_tag_)}" - ) - - if status_ is not None and not isinstance(status_, (bytes, str)): - raise Exception(f"Expected status_ to be a str, received: {type(status_)}") - - if vlan_tag_ is not None and not isinstance(vlan_tag_, int): - raise Exception( - f"Expected vlan_tag_ to be a int, received: {type(vlan_tag_)}" - ) - - if zones_ is not None and not isinstance(zones_, (bytes, str, list)): - raise Exception( - f"Expected zones_ to be a Sequence, received: {type(zones_)}" - ) - - self.subnet = subnet_ - self.cidr = cidr_ - self.id_ = id__ - self.life = life_ - self.provider_id = provider_id_ - self.provider_network_id = provider_network_id_ - self.provider_space_id = provider_space_id_ - self.space_tag = space_tag_ - self.status = status_ - self.vlan_tag = vlan_tag_ - self.zones = zones_ - self.unknown_fields = unknown_fields - - -class SubnetsFilters(Type): - _toSchema = {"space_tag": "space-tag", "zone": "zone"} - _toPy = {"space-tag": "space_tag", "zone": "zone"} - - def __init__(self, space_tag=None, zone=None, **unknown_fields): - """space_tag : str - zone : str - """ - space_tag_ = space_tag - zone_ = zone - - # Validate arguments against known Juju API types. - if space_tag_ is not None and not isinstance(space_tag_, (bytes, str)): - raise Exception( - f"Expected space_tag_ to be a str, received: {type(space_tag_)}" - ) - - if zone_ is not None and not isinstance(zone_, (bytes, str)): - raise Exception(f"Expected zone_ to be a str, received: {type(zone_)}") - - self.space_tag = space_tag_ - self.zone = zone_ - self.unknown_fields = unknown_fields - - -class SubnetsResult(Type): - _toSchema = {"error": "error", "subnets": "subnets"} - _toPy = {"error": "error", "subnets": "subnets"} - - def __init__(self, error=None, subnets=None, **unknown_fields): - """Error : Error - subnets : typing.Sequence[~SubnetV2] - """ - error_ = Error.from_json(error) if error else None - subnets_ = [SubnetV2.from_json(o) for o in subnets or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if subnets_ is not None and not isinstance(subnets_, (bytes, str, list)): - raise Exception( - f"Expected subnets_ to be a Sequence, received: {type(subnets_)}" - ) - - self.error = error_ - self.subnets = subnets_ - self.unknown_fields = unknown_fields - - -class SubnetsResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~SubnetsResult]""" - results_ = [SubnetsResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class SummaryWatcherID(Type): - _toSchema = {"watcher_id": "watcher-id"} - _toPy = {"watcher-id": "watcher_id"} - - def __init__(self, watcher_id=None, **unknown_fields): - """watcher_id : str""" - watcher_id_ = watcher_id - - # Validate arguments against known Juju API types. - if watcher_id_ is not None and not isinstance(watcher_id_, (bytes, str)): - raise Exception( - f"Expected watcher_id_ to be a str, received: {type(watcher_id_)}" - ) - - self.watcher_id = watcher_id_ - self.unknown_fields = unknown_fields - - -class SupportedFeature(Type): - _toSchema = {"description": "description", "name": "name", "version": "version"} - _toPy = {"description": "description", "name": "name", "version": "version"} - - def __init__(self, description=None, name=None, version=None, **unknown_fields): - """Description : str - name : str - version : str - """ - description_ = description - name_ = name - version_ = version - - # Validate arguments against known Juju API types. - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if version_ is not None and not isinstance(version_, (bytes, str)): - raise Exception( - f"Expected version_ to be a str, received: {type(version_)}" - ) - - self.description = description_ - self.name = name_ - self.version = version_ - self.unknown_fields = unknown_fields - - -class TaggedCredential(Type): - _toSchema = {"credential": "credential", "tag": "tag"} - _toPy = {"credential": "credential", "tag": "tag"} - - def __init__(self, credential=None, tag=None, **unknown_fields): - """Credential : CloudCredential - tag : str - """ - credential_ = CloudCredential.from_json(credential) if credential else None - tag_ = tag - - # Validate arguments against known Juju API types. - if credential_ is not None and not isinstance( - credential_, (dict, CloudCredential) - ): - raise Exception( - f"Expected credential_ to be a CloudCredential, received: {type(credential_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.credential = credential_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class TaggedCredentials(Type): - _toSchema = {"credentials": "credentials"} - _toPy = {"credentials": "credentials"} - - def __init__(self, credentials=None, **unknown_fields): - """Credentials : typing.Sequence[~TaggedCredential]""" - credentials_ = [TaggedCredential.from_json(o) for o in credentials or []] - - # Validate arguments against known Juju API types. - if credentials_ is not None and not isinstance( - credentials_, (bytes, str, list) - ): - raise Exception( - f"Expected credentials_ to be a Sequence, received: {type(credentials_)}" - ) - - self.credentials = credentials_ - self.unknown_fields = unknown_fields - - -class Tools(Type): - _toSchema = {"sha256": "sha256", "size": "size", "url": "url", "version": "version"} - _toPy = {"sha256": "sha256", "size": "size", "url": "url", "version": "version"} - - def __init__( - self, sha256=None, size=None, url=None, version=None, **unknown_fields - ): - """sha256 : str - size : int - url : str - version : Binary - """ - sha256_ = sha256 - size_ = size - url_ = url - version_ = Binary.from_json(version) if version else None - - # Validate arguments against known Juju API types. - if sha256_ is not None and not isinstance(sha256_, (bytes, str)): - raise Exception(f"Expected sha256_ to be a str, received: {type(sha256_)}") - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - if url_ is not None and not isinstance(url_, (bytes, str)): - raise Exception(f"Expected url_ to be a str, received: {type(url_)}") - - if version_ is not None and not isinstance(version_, (dict, Binary)): - raise Exception( - f"Expected version_ to be a Binary, received: {type(version_)}" - ) - - self.sha256 = sha256_ - self.size = size_ - self.url = url_ - self.version = version_ - self.unknown_fields = unknown_fields - - -class UnitInfoResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : UnitResult - """ - error_ = Error.from_json(error) if error else None - result_ = UnitResult.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, UnitResult)): - raise Exception( - f"Expected result_ to be a UnitResult, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class UnitInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~UnitInfoResult]""" - results_ = [UnitInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class UnitResources(Type): - _toSchema = { - "download_progress": "download-progress", - "entity": "Entity", - "resources": "resources", - "tag": "tag", - } - _toPy = { - "Entity": "entity", - "download-progress": "download_progress", - "resources": "resources", - "tag": "tag", - } - - def __init__( - self, - entity=None, - download_progress=None, - resources=None, - tag=None, - **unknown_fields, - ): - """Entity : Entity - download_progress : typing.Mapping[str, int] - resources : typing.Sequence[~Resource] - tag : str - """ - entity_ = Entity.from_json(entity) if entity else None - download_progress_ = download_progress - resources_ = [Resource.from_json(o) for o in resources or []] - tag_ = tag - - # Validate arguments against known Juju API types. - if entity_ is not None and not isinstance(entity_, (dict, Entity)): - raise Exception( - f"Expected entity_ to be a Entity, received: {type(entity_)}" - ) - - if download_progress_ is not None and not isinstance(download_progress_, dict): - raise Exception( - f"Expected download_progress_ to be a Mapping, received: {type(download_progress_)}" - ) - - if resources_ is not None and not isinstance(resources_, (bytes, str, list)): - raise Exception( - f"Expected resources_ to be a Sequence, received: {type(resources_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.entity = entity_ - self.download_progress = download_progress_ - self.resources = resources_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class UnitResult(Type): - _toSchema = { - "address": "address", - "charm": "charm", - "leader": "leader", - "life": "life", - "machine": "machine", - "opened_ports": "opened-ports", - "provider_id": "provider-id", - "public_address": "public-address", - "relation_data": "relation-data", - "tag": "tag", - "workload_version": "workload-version", - } - _toPy = { - "address": "address", - "charm": "charm", - "leader": "leader", - "life": "life", - "machine": "machine", - "opened-ports": "opened_ports", - "provider-id": "provider_id", - "public-address": "public_address", - "relation-data": "relation_data", - "tag": "tag", - "workload-version": "workload_version", - } - - def __init__( - self, - address=None, - charm=None, - leader=None, - life=None, - machine=None, - opened_ports=None, - provider_id=None, - public_address=None, - relation_data=None, - tag=None, - workload_version=None, - **unknown_fields, - ): - """Address : str - charm : str - leader : bool - life : str - machine : str - opened_ports : typing.Sequence[str] - provider_id : str - public_address : str - relation_data : typing.Sequence[~EndpointRelationData] - tag : str - workload_version : str - """ - address_ = address - charm_ = charm - leader_ = leader - life_ = life - machine_ = machine - opened_ports_ = opened_ports - provider_id_ = provider_id - public_address_ = public_address - relation_data_ = [ - EndpointRelationData.from_json(o) for o in relation_data or [] - ] - tag_ = tag - workload_version_ = workload_version - - # Validate arguments against known Juju API types. - if address_ is not None and not isinstance(address_, (bytes, str)): - raise Exception( - f"Expected address_ to be a str, received: {type(address_)}" - ) - - if charm_ is not None and not isinstance(charm_, (bytes, str)): - raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") - - if leader_ is not None and not isinstance(leader_, bool): - raise Exception(f"Expected leader_ to be a bool, received: {type(leader_)}") - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if machine_ is not None and not isinstance(machine_, (bytes, str)): - raise Exception( - f"Expected machine_ to be a str, received: {type(machine_)}" - ) - - if opened_ports_ is not None and not isinstance( - opened_ports_, (bytes, str, list) - ): - raise Exception( - f"Expected opened_ports_ to be a Sequence, received: {type(opened_ports_)}" - ) - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if public_address_ is not None and not isinstance( - public_address_, (bytes, str) - ): - raise Exception( - f"Expected public_address_ to be a str, received: {type(public_address_)}" - ) - - if relation_data_ is not None and not isinstance( - relation_data_, (bytes, str, list) - ): - raise Exception( - f"Expected relation_data_ to be a Sequence, received: {type(relation_data_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - if workload_version_ is not None and not isinstance( - workload_version_, (bytes, str) - ): - raise Exception( - f"Expected workload_version_ to be a str, received: {type(workload_version_)}" - ) - - self.address = address_ - self.charm = charm_ - self.leader = leader_ - self.life = life_ - self.machine = machine_ - self.opened_ports = opened_ports_ - self.provider_id = provider_id_ - self.public_address = public_address_ - self.relation_data = relation_data_ - self.tag = tag_ - self.workload_version = workload_version_ - self.unknown_fields = unknown_fields - - -class UnitStatus(Type): - _toSchema = { - "address": "address", - "agent_status": "agent-status", - "charm": "charm", - "leader": "leader", - "machine": "machine", - "opened_ports": "opened-ports", - "provider_id": "provider-id", - "public_address": "public-address", - "subordinates": "subordinates", - "workload_status": "workload-status", - "workload_version": "workload-version", - } - _toPy = { - "address": "address", - "agent-status": "agent_status", - "charm": "charm", - "leader": "leader", - "machine": "machine", - "opened-ports": "opened_ports", - "provider-id": "provider_id", - "public-address": "public_address", - "subordinates": "subordinates", - "workload-status": "workload_status", - "workload-version": "workload_version", - } - - def __init__( - self, - address=None, - agent_status=None, - charm=None, - leader=None, - machine=None, - opened_ports=None, - provider_id=None, - public_address=None, - subordinates=None, - workload_status=None, - workload_version=None, - **unknown_fields, - ): - """Address : str - agent_status : DetailedStatus - charm : str - leader : bool - machine : str - opened_ports : typing.Sequence[str] - provider_id : str - public_address : str - subordinates : typing.Mapping[str, ~UnitStatus] - workload_status : DetailedStatus - workload_version : str - """ - address_ = address - agent_status_ = DetailedStatus.from_json(agent_status) if agent_status else None - charm_ = charm - leader_ = leader - machine_ = machine - opened_ports_ = opened_ports - provider_id_ = provider_id - public_address_ = public_address - subordinates_ = { - k: UnitStatus.from_json(v) for k, v in (subordinates or dict()).items() - } - workload_status_ = ( - DetailedStatus.from_json(workload_status) if workload_status else None - ) - workload_version_ = workload_version - - # Validate arguments against known Juju API types. - if address_ is not None and not isinstance(address_, (bytes, str)): - raise Exception( - f"Expected address_ to be a str, received: {type(address_)}" - ) - - if agent_status_ is not None and not isinstance( - agent_status_, (dict, DetailedStatus) - ): - raise Exception( - f"Expected agent_status_ to be a DetailedStatus, received: {type(agent_status_)}" - ) - - if charm_ is not None and not isinstance(charm_, (bytes, str)): - raise Exception(f"Expected charm_ to be a str, received: {type(charm_)}") - - if leader_ is not None and not isinstance(leader_, bool): - raise Exception(f"Expected leader_ to be a bool, received: {type(leader_)}") - - if machine_ is not None and not isinstance(machine_, (bytes, str)): - raise Exception( - f"Expected machine_ to be a str, received: {type(machine_)}" - ) - - if opened_ports_ is not None and not isinstance( - opened_ports_, (bytes, str, list) - ): - raise Exception( - f"Expected opened_ports_ to be a Sequence, received: {type(opened_ports_)}" - ) - - if provider_id_ is not None and not isinstance(provider_id_, (bytes, str)): - raise Exception( - f"Expected provider_id_ to be a str, received: {type(provider_id_)}" - ) - - if public_address_ is not None and not isinstance( - public_address_, (bytes, str) - ): - raise Exception( - f"Expected public_address_ to be a str, received: {type(public_address_)}" - ) - - if subordinates_ is not None and not isinstance(subordinates_, dict): - raise Exception( - f"Expected subordinates_ to be a Mapping, received: {type(subordinates_)}" - ) - - if workload_status_ is not None and not isinstance( - workload_status_, (dict, DetailedStatus) - ): - raise Exception( - f"Expected workload_status_ to be a DetailedStatus, received: {type(workload_status_)}" - ) - - if workload_version_ is not None and not isinstance( - workload_version_, (bytes, str) - ): - raise Exception( - f"Expected workload_version_ to be a str, received: {type(workload_version_)}" - ) - - self.address = address_ - self.agent_status = agent_status_ - self.charm = charm_ - self.leader = leader_ - self.machine = machine_ - self.opened_ports = opened_ports_ - self.provider_id = provider_id_ - self.public_address = public_address_ - self.subordinates = subordinates_ - self.workload_status = workload_status_ - self.workload_version = workload_version_ - self.unknown_fields = unknown_fields - - -class UnitsResolved(Type): - _toSchema = {"all_": "all", "retry": "retry", "tags": "tags"} - _toPy = {"all": "all_", "retry": "retry", "tags": "tags"} - - def __init__(self, all_=None, retry=None, tags=None, **unknown_fields): - """all_ : bool - retry : bool - tags : Entities - """ - all__ = all_ - retry_ = retry - tags_ = Entities.from_json(tags) if tags else None - - # Validate arguments against known Juju API types. - if all__ is not None and not isinstance(all__, bool): - raise Exception(f"Expected all__ to be a bool, received: {type(all__)}") - - if retry_ is not None and not isinstance(retry_, bool): - raise Exception(f"Expected retry_ to be a bool, received: {type(retry_)}") - - if tags_ is not None and not isinstance(tags_, (dict, Entities)): - raise Exception(f"Expected tags_ to be a Entities, received: {type(tags_)}") - - self.all_ = all__ - self.retry = retry_ - self.tags = tags_ - self.unknown_fields = unknown_fields - - -class UnsetModelDefaults(Type): - _toSchema = {"keys": "keys"} - _toPy = {"keys": "keys"} - - def __init__(self, keys=None, **unknown_fields): - """Keys : typing.Sequence[~ModelUnsetKeys]""" - keys_ = [ModelUnsetKeys.from_json(o) for o in keys or []] - - # Validate arguments against known Juju API types. - if keys_ is not None and not isinstance(keys_, (bytes, str, list)): - raise Exception(f"Expected keys_ to be a Sequence, received: {type(keys_)}") - - self.keys = keys_ - self.unknown_fields = unknown_fields - - -class UpdateChannelArg(Type): - _toSchema = {"channel": "channel", "force": "force", "tag": "tag"} - _toPy = {"channel": "channel", "force": "force", "tag": "tag"} - - def __init__(self, channel=None, force=None, tag=None, **unknown_fields): - """Channel : str - force : bool - tag : Entity - """ - channel_ = channel - force_ = force - tag_ = Entity.from_json(tag) if tag else None - - # Validate arguments against known Juju API types. - if channel_ is not None and not isinstance(channel_, (bytes, str)): - raise Exception( - f"Expected channel_ to be a str, received: {type(channel_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if tag_ is not None and not isinstance(tag_, (dict, Entity)): - raise Exception(f"Expected tag_ to be a Entity, received: {type(tag_)}") - - self.channel = channel_ - self.force = force_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class UpdateChannelArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~UpdateChannelArg]""" - args_ = [UpdateChannelArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class UpdateCloudArgs(Type): - _toSchema = {"clouds": "clouds"} - _toPy = {"clouds": "clouds"} - - def __init__(self, clouds=None, **unknown_fields): - """Clouds : typing.Sequence[~AddCloudArgs]""" - clouds_ = [AddCloudArgs.from_json(o) for o in clouds or []] - - # Validate arguments against known Juju API types. - if clouds_ is not None and not isinstance(clouds_, (bytes, str, list)): - raise Exception( - f"Expected clouds_ to be a Sequence, received: {type(clouds_)}" - ) - - self.clouds = clouds_ - self.unknown_fields = unknown_fields - - -class UpdateCredentialArgs(Type): - _toSchema = {"credentials": "credentials", "force": "force"} - _toPy = {"credentials": "credentials", "force": "force"} - - def __init__(self, credentials=None, force=None, **unknown_fields): - """Credentials : typing.Sequence[~TaggedCredential] - force : bool - """ - credentials_ = [TaggedCredential.from_json(o) for o in credentials or []] - force_ = force - - # Validate arguments against known Juju API types. - if credentials_ is not None and not isinstance( - credentials_, (bytes, str, list) - ): - raise Exception( - f"Expected credentials_ to be a Sequence, received: {type(credentials_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - self.credentials = credentials_ - self.force = force_ - self.unknown_fields = unknown_fields - - -class UpdateCredentialModelResult(Type): - _toSchema = {"errors": "errors", "name": "name", "uuid": "uuid"} - _toPy = {"errors": "errors", "name": "name", "uuid": "uuid"} - - def __init__(self, errors=None, name=None, uuid=None, **unknown_fields): - """Errors : typing.Sequence[~ErrorResult] - name : str - uuid : str - """ - errors_ = [ErrorResult.from_json(o) for o in errors or []] - name_ = name - uuid_ = uuid - - # Validate arguments against known Juju API types. - if errors_ is not None and not isinstance(errors_, (bytes, str, list)): - raise Exception( - f"Expected errors_ to be a Sequence, received: {type(errors_)}" - ) - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if uuid_ is not None and not isinstance(uuid_, (bytes, str)): - raise Exception(f"Expected uuid_ to be a str, received: {type(uuid_)}") - - self.errors = errors_ - self.name = name_ - self.uuid = uuid_ - self.unknown_fields = unknown_fields - - -class UpdateCredentialResult(Type): - _toSchema = {"error": "error", "models": "models", "tag": "tag"} - _toPy = {"error": "error", "models": "models", "tag": "tag"} - - def __init__(self, error=None, models=None, tag=None, **unknown_fields): - """Error : Error - models : typing.Sequence[~UpdateCredentialModelResult] - tag : str - """ - error_ = Error.from_json(error) if error else None - models_ = [UpdateCredentialModelResult.from_json(o) for o in models or []] - tag_ = tag - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if models_ is not None and not isinstance(models_, (bytes, str, list)): - raise Exception( - f"Expected models_ to be a Sequence, received: {type(models_)}" - ) - - if tag_ is not None and not isinstance(tag_, (bytes, str)): - raise Exception(f"Expected tag_ to be a str, received: {type(tag_)}") - - self.error = error_ - self.models = models_ - self.tag = tag_ - self.unknown_fields = unknown_fields - - -class UpdateCredentialResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~UpdateCredentialResult]""" - results_ = [UpdateCredentialResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class UpdateSecretBackendArg(Type): - _toSchema = { - "config": "config", - "force": "force", - "name": "name", - "name_change": "name-change", - "reset": "reset", - "token_rotate_interval": "token-rotate-interval", - } - _toPy = { - "config": "config", - "force": "force", - "name": "name", - "name-change": "name_change", - "reset": "reset", - "token-rotate-interval": "token_rotate_interval", - } - - def __init__( - self, - config=None, - force=None, - name=None, - name_change=None, - reset=None, - token_rotate_interval=None, - **unknown_fields, - ): - """Config : typing.Mapping[str, typing.Any] - force : bool - name : str - name_change : str - reset : typing.Sequence[str] - token_rotate_interval : int - """ - config_ = config - force_ = force - name_ = name - name_change_ = name_change - reset_ = reset - token_rotate_interval_ = token_rotate_interval - - # Validate arguments against known Juju API types. - if config_ is not None and not isinstance(config_, dict): - raise Exception( - f"Expected config_ to be a Mapping, received: {type(config_)}" - ) - - if force_ is not None and not isinstance(force_, bool): - raise Exception(f"Expected force_ to be a bool, received: {type(force_)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - if name_change_ is not None and not isinstance(name_change_, (bytes, str)): - raise Exception( - f"Expected name_change_ to be a str, received: {type(name_change_)}" - ) - - if reset_ is not None and not isinstance(reset_, (bytes, str, list)): - raise Exception( - f"Expected reset_ to be a Sequence, received: {type(reset_)}" - ) - - if token_rotate_interval_ is not None and not isinstance( - token_rotate_interval_, int - ): - raise Exception( - f"Expected token_rotate_interval_ to be a int, received: {type(token_rotate_interval_)}" - ) - - self.config = config_ - self.force = force_ - self.name = name_ - self.name_change = name_change_ - self.reset = reset_ - self.token_rotate_interval = token_rotate_interval_ - self.unknown_fields = unknown_fields - - -class UpdateSecretBackendArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~UpdateSecretBackendArg]""" - args_ = [UpdateSecretBackendArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class UpdateUserSecretArg(Type): - _toSchema = { - "auto_prune": "auto-prune", - "content": "content", - "description": "description", - "existing_label": "existing-label", - "expire_time": "expire-time", - "label": "label", - "params": "params", - "rotate_policy": "rotate-policy", - "upsertsecretarg": "UpsertSecretArg", - "uri": "uri", - } - _toPy = { - "UpsertSecretArg": "upsertsecretarg", - "auto-prune": "auto_prune", - "content": "content", - "description": "description", - "existing-label": "existing_label", - "expire-time": "expire_time", - "label": "label", - "params": "params", - "rotate-policy": "rotate_policy", - "uri": "uri", - } - - def __init__( - self, - upsertsecretarg=None, - auto_prune=None, - content=None, - description=None, - existing_label=None, - expire_time=None, - label=None, - params=None, - rotate_policy=None, - uri=None, - **unknown_fields, - ): - """Upsertsecretarg : UpsertSecretArg - auto_prune : bool - content : SecretContentParams - description : str - existing_label : str - expire_time : str - label : str - params : typing.Mapping[str, typing.Any] - rotate_policy : str - uri : str - """ - upsertsecretarg_ = ( - UpsertSecretArg.from_json(upsertsecretarg) if upsertsecretarg else None - ) - auto_prune_ = auto_prune - content_ = SecretContentParams.from_json(content) if content else None - description_ = description - existing_label_ = existing_label - expire_time_ = expire_time - label_ = label - params_ = params - rotate_policy_ = rotate_policy - uri_ = uri - - # Validate arguments against known Juju API types. - if upsertsecretarg_ is not None and not isinstance( - upsertsecretarg_, (dict, UpsertSecretArg) - ): - raise Exception( - f"Expected upsertsecretarg_ to be a UpsertSecretArg, received: {type(upsertsecretarg_)}" - ) - - if auto_prune_ is not None and not isinstance(auto_prune_, bool): - raise Exception( - f"Expected auto_prune_ to be a bool, received: {type(auto_prune_)}" - ) - - if content_ is not None and not isinstance( - content_, (dict, SecretContentParams) - ): - raise Exception( - f"Expected content_ to be a SecretContentParams, received: {type(content_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if existing_label_ is not None and not isinstance( - existing_label_, (bytes, str) - ): - raise Exception( - f"Expected existing_label_ to be a str, received: {type(existing_label_)}" - ) - - if expire_time_ is not None and not isinstance(expire_time_, (bytes, str)): - raise Exception( - f"Expected expire_time_ to be a str, received: {type(expire_time_)}" - ) - - if label_ is not None and not isinstance(label_, (bytes, str)): - raise Exception(f"Expected label_ to be a str, received: {type(label_)}") - - if params_ is not None and not isinstance(params_, dict): - raise Exception( - f"Expected params_ to be a Mapping, received: {type(params_)}" - ) - - if rotate_policy_ is not None and not isinstance(rotate_policy_, (bytes, str)): - raise Exception( - f"Expected rotate_policy_ to be a str, received: {type(rotate_policy_)}" - ) - - if uri_ is not None and not isinstance(uri_, (bytes, str)): - raise Exception(f"Expected uri_ to be a str, received: {type(uri_)}") - - self.upsertsecretarg = upsertsecretarg_ - self.auto_prune = auto_prune_ - self.content = content_ - self.description = description_ - self.existing_label = existing_label_ - self.expire_time = expire_time_ - self.label = label_ - self.params = params_ - self.rotate_policy = rotate_policy_ - self.uri = uri_ - self.unknown_fields = unknown_fields - - -class UpdateUserSecretArgs(Type): - _toSchema = {"args": "args"} - _toPy = {"args": "args"} - - def __init__(self, args=None, **unknown_fields): - """Args : typing.Sequence[~UpdateUserSecretArg]""" - args_ = [UpdateUserSecretArg.from_json(o) for o in args or []] - - # Validate arguments against known Juju API types. - if args_ is not None and not isinstance(args_, (bytes, str, list)): - raise Exception(f"Expected args_ to be a Sequence, received: {type(args_)}") - - self.args = args_ - self.unknown_fields = unknown_fields - - -class UpgradeModelParams(Type): - _toSchema = { - "agent_stream": "agent-stream", - "dry_run": "dry-run", - "ignore_agent_versions": "ignore-agent-versions", - "model_tag": "model-tag", - "target_version": "target-version", - } - _toPy = { - "agent-stream": "agent_stream", - "dry-run": "dry_run", - "ignore-agent-versions": "ignore_agent_versions", - "model-tag": "model_tag", - "target-version": "target_version", - } - - def __init__( - self, - agent_stream=None, - dry_run=None, - ignore_agent_versions=None, - model_tag=None, - target_version=None, - **unknown_fields, - ): - """agent_stream : str - dry_run : bool - ignore_agent_versions : bool - model_tag : str - target_version : Number - """ - agent_stream_ = agent_stream - dry_run_ = dry_run - ignore_agent_versions_ = ignore_agent_versions - model_tag_ = model_tag - target_version_ = Number.from_json(target_version) if target_version else None - - # Validate arguments against known Juju API types. - if agent_stream_ is not None and not isinstance(agent_stream_, (bytes, str)): - raise Exception( - f"Expected agent_stream_ to be a str, received: {type(agent_stream_)}" - ) - - if dry_run_ is not None and not isinstance(dry_run_, bool): - raise Exception( - f"Expected dry_run_ to be a bool, received: {type(dry_run_)}" - ) - - if ignore_agent_versions_ is not None and not isinstance( - ignore_agent_versions_, bool - ): - raise Exception( - f"Expected ignore_agent_versions_ to be a bool, received: {type(ignore_agent_versions_)}" - ) - - if model_tag_ is not None and not isinstance(model_tag_, (bytes, str)): - raise Exception( - f"Expected model_tag_ to be a str, received: {type(model_tag_)}" - ) - - if target_version_ is not None and not isinstance( - target_version_, (dict, Number) - ): - raise Exception( - f"Expected target_version_ to be a Number, received: {type(target_version_)}" - ) - - self.agent_stream = agent_stream_ - self.dry_run = dry_run_ - self.ignore_agent_versions = ignore_agent_versions_ - self.model_tag = model_tag_ - self.target_version = target_version_ - self.unknown_fields = unknown_fields - - -class UpgradeModelResult(Type): - _toSchema = {"chosen_version": "chosen-version", "error": "error"} - _toPy = {"chosen-version": "chosen_version", "error": "error"} - - def __init__(self, chosen_version=None, error=None, **unknown_fields): - """chosen_version : Number - error : Error - """ - chosen_version_ = Number.from_json(chosen_version) if chosen_version else None - error_ = Error.from_json(error) if error else None - - # Validate arguments against known Juju API types. - if chosen_version_ is not None and not isinstance( - chosen_version_, (dict, Number) - ): - raise Exception( - f"Expected chosen_version_ to be a Number, received: {type(chosen_version_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - self.chosen_version = chosen_version_ - self.error = error_ - self.unknown_fields = unknown_fields - - -class UpgradeSeriesNotificationParam(Type): - _toSchema = {"entity": "entity", "watcher_id": "watcher-id"} - _toPy = {"entity": "entity", "watcher-id": "watcher_id"} - - def __init__(self, entity=None, watcher_id=None, **unknown_fields): - """Entity : Entity - watcher_id : str - """ - entity_ = Entity.from_json(entity) if entity else None - watcher_id_ = watcher_id - - # Validate arguments against known Juju API types. - if entity_ is not None and not isinstance(entity_, (dict, Entity)): - raise Exception( - f"Expected entity_ to be a Entity, received: {type(entity_)}" - ) - - if watcher_id_ is not None and not isinstance(watcher_id_, (bytes, str)): - raise Exception( - f"Expected watcher_id_ to be a str, received: {type(watcher_id_)}" - ) - - self.entity = entity_ - self.watcher_id = watcher_id_ - self.unknown_fields = unknown_fields - - -class UpgradeSeriesNotificationParams(Type): - _toSchema = {"params": "params"} - _toPy = {"params": "params"} - - def __init__(self, params=None, **unknown_fields): - """Params : typing.Sequence[~UpgradeSeriesNotificationParam]""" - params_ = [UpgradeSeriesNotificationParam.from_json(o) for o in params or []] - - # Validate arguments against known Juju API types. - if params_ is not None and not isinstance(params_, (bytes, str, list)): - raise Exception( - f"Expected params_ to be a Sequence, received: {type(params_)}" - ) - - self.params = params_ - self.unknown_fields = unknown_fields - - -class UpgradeSeriesUnitsResult(Type): - _toSchema = {"error": "error", "unit_names": "unit-names"} - _toPy = {"error": "error", "unit-names": "unit_names"} - - def __init__(self, error=None, unit_names=None, **unknown_fields): - """Error : Error - unit_names : typing.Sequence[str] - """ - error_ = Error.from_json(error) if error else None - unit_names_ = unit_names - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if unit_names_ is not None and not isinstance(unit_names_, (bytes, str, list)): - raise Exception( - f"Expected unit_names_ to be a Sequence, received: {type(unit_names_)}" - ) - - self.error = error_ - self.unit_names = unit_names_ - self.unknown_fields = unknown_fields - - -class UpgradeSeriesUnitsResults(Type): - _toSchema = {"results": "Results"} - _toPy = {"Results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~UpgradeSeriesUnitsResult]""" - results_ = [UpgradeSeriesUnitsResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class UpsertSecretArg(Type): - _toSchema = { - "content": "content", - "description": "description", - "expire_time": "expire-time", - "label": "label", - "params": "params", - "rotate_policy": "rotate-policy", - } - _toPy = { - "content": "content", - "description": "description", - "expire-time": "expire_time", - "label": "label", - "params": "params", - "rotate-policy": "rotate_policy", - } - - def __init__( - self, - content=None, - description=None, - expire_time=None, - label=None, - params=None, - rotate_policy=None, - **unknown_fields, - ): - """Content : SecretContentParams - description : str - expire_time : str - label : str - params : typing.Mapping[str, typing.Any] - rotate_policy : str - """ - content_ = SecretContentParams.from_json(content) if content else None - description_ = description - expire_time_ = expire_time - label_ = label - params_ = params - rotate_policy_ = rotate_policy - - # Validate arguments against known Juju API types. - if content_ is not None and not isinstance( - content_, (dict, SecretContentParams) - ): - raise Exception( - f"Expected content_ to be a SecretContentParams, received: {type(content_)}" - ) - - if description_ is not None and not isinstance(description_, (bytes, str)): - raise Exception( - f"Expected description_ to be a str, received: {type(description_)}" - ) - - if expire_time_ is not None and not isinstance(expire_time_, (bytes, str)): - raise Exception( - f"Expected expire_time_ to be a str, received: {type(expire_time_)}" - ) - - if label_ is not None and not isinstance(label_, (bytes, str)): - raise Exception(f"Expected label_ to be a str, received: {type(label_)}") - - if params_ is not None and not isinstance(params_, dict): - raise Exception( - f"Expected params_ to be a Mapping, received: {type(params_)}" - ) - - if rotate_policy_ is not None and not isinstance(rotate_policy_, (bytes, str)): - raise Exception( - f"Expected rotate_policy_ to be a str, received: {type(rotate_policy_)}" - ) - - self.content = content_ - self.description = description_ - self.expire_time = expire_time_ - self.label = label_ - self.params = params_ - self.rotate_policy = rotate_policy_ - self.unknown_fields = unknown_fields - - -class UserAccess(Type): - _toSchema = {"access": "access", "user_tag": "user-tag"} - _toPy = {"access": "access", "user-tag": "user_tag"} - - def __init__(self, access=None, user_tag=None, **unknown_fields): - """Access : str - user_tag : str - """ - access_ = access - user_tag_ = user_tag - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): - raise Exception( - f"Expected user_tag_ to be a str, received: {type(user_tag_)}" - ) - - self.access = access_ - self.user_tag = user_tag_ - self.unknown_fields = unknown_fields - - -class UserAccessResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : UserAccess - """ - error_ = Error.from_json(error) if error else None - result_ = UserAccess.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, UserAccess)): - raise Exception( - f"Expected result_ to be a UserAccess, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class UserAccessResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~UserAccessResult]""" - results_ = [UserAccessResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class UserCloud(Type): - _toSchema = {"cloud_tag": "cloud-tag", "user_tag": "user-tag"} - _toPy = {"cloud-tag": "cloud_tag", "user-tag": "user_tag"} - - def __init__(self, cloud_tag=None, user_tag=None, **unknown_fields): - """cloud_tag : str - user_tag : str - """ - cloud_tag_ = cloud_tag - user_tag_ = user_tag - - # Validate arguments against known Juju API types. - if cloud_tag_ is not None and not isinstance(cloud_tag_, (bytes, str)): - raise Exception( - f"Expected cloud_tag_ to be a str, received: {type(cloud_tag_)}" - ) - - if user_tag_ is not None and not isinstance(user_tag_, (bytes, str)): - raise Exception( - f"Expected user_tag_ to be a str, received: {type(user_tag_)}" - ) - - self.cloud_tag = cloud_tag_ - self.user_tag = user_tag_ - self.unknown_fields = unknown_fields - - -class UserClouds(Type): - _toSchema = {"user_clouds": "user-clouds"} - _toPy = {"user-clouds": "user_clouds"} - - def __init__(self, user_clouds=None, **unknown_fields): - """user_clouds : typing.Sequence[~UserCloud]""" - user_clouds_ = [UserCloud.from_json(o) for o in user_clouds or []] - - # Validate arguments against known Juju API types. - if user_clouds_ is not None and not isinstance( - user_clouds_, (bytes, str, list) - ): - raise Exception( - f"Expected user_clouds_ to be a Sequence, received: {type(user_clouds_)}" - ) - - self.user_clouds = user_clouds_ - self.unknown_fields = unknown_fields - - -class UserInfo(Type): - _toSchema = { - "access": "access", - "created_by": "created-by", - "date_created": "date-created", - "disabled": "disabled", - "display_name": "display-name", - "last_connection": "last-connection", - "username": "username", - } - _toPy = { - "access": "access", - "created-by": "created_by", - "date-created": "date_created", - "disabled": "disabled", - "display-name": "display_name", - "last-connection": "last_connection", - "username": "username", - } - - def __init__( - self, - access=None, - created_by=None, - date_created=None, - disabled=None, - display_name=None, - last_connection=None, - username=None, - **unknown_fields, - ): - """Access : str - created_by : str - date_created : str - disabled : bool - display_name : str - last_connection : str - username : str - """ - access_ = access - created_by_ = created_by - date_created_ = date_created - disabled_ = disabled - display_name_ = display_name - last_connection_ = last_connection - username_ = username - - # Validate arguments against known Juju API types. - if access_ is not None and not isinstance(access_, (bytes, str)): - raise Exception(f"Expected access_ to be a str, received: {type(access_)}") - - if created_by_ is not None and not isinstance(created_by_, (bytes, str)): - raise Exception( - f"Expected created_by_ to be a str, received: {type(created_by_)}" - ) - - if date_created_ is not None and not isinstance(date_created_, (bytes, str)): - raise Exception( - f"Expected date_created_ to be a str, received: {type(date_created_)}" - ) - - if disabled_ is not None and not isinstance(disabled_, bool): - raise Exception( - f"Expected disabled_ to be a bool, received: {type(disabled_)}" - ) - - if display_name_ is not None and not isinstance(display_name_, (bytes, str)): - raise Exception( - f"Expected display_name_ to be a str, received: {type(display_name_)}" - ) - - if last_connection_ is not None and not isinstance( - last_connection_, (bytes, str) - ): - raise Exception( - f"Expected last_connection_ to be a str, received: {type(last_connection_)}" - ) - - if username_ is not None and not isinstance(username_, (bytes, str)): - raise Exception( - f"Expected username_ to be a str, received: {type(username_)}" - ) - - self.access = access_ - self.created_by = created_by_ - self.date_created = date_created_ - self.disabled = disabled_ - self.display_name = display_name_ - self.last_connection = last_connection_ - self.username = username_ - self.unknown_fields = unknown_fields - - -class UserInfoRequest(Type): - _toSchema = {"entities": "entities", "include_disabled": "include-disabled"} - _toPy = {"entities": "entities", "include-disabled": "include_disabled"} - - def __init__(self, entities=None, include_disabled=None, **unknown_fields): - """Entities : typing.Sequence[~Entity] - include_disabled : bool - """ - entities_ = [Entity.from_json(o) for o in entities or []] - include_disabled_ = include_disabled - - # Validate arguments against known Juju API types. - if entities_ is not None and not isinstance(entities_, (bytes, str, list)): - raise Exception( - f"Expected entities_ to be a Sequence, received: {type(entities_)}" - ) - - if include_disabled_ is not None and not isinstance(include_disabled_, bool): - raise Exception( - f"Expected include_disabled_ to be a bool, received: {type(include_disabled_)}" - ) - - self.entities = entities_ - self.include_disabled = include_disabled_ - self.unknown_fields = unknown_fields - - -class UserInfoResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : UserInfo - """ - error_ = Error.from_json(error) if error else None - result_ = UserInfo.from_json(result) if result else None - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (dict, UserInfo)): - raise Exception( - f"Expected result_ to be a UserInfo, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class UserInfoResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~UserInfoResult]""" - results_ = [UserInfoResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class UserModel(Type): - _toSchema = {"last_connection": "last-connection", "model": "model"} - _toPy = {"last-connection": "last_connection", "model": "model"} - - def __init__(self, last_connection=None, model=None, **unknown_fields): - """last_connection : str - model : Model - """ - last_connection_ = last_connection - model_ = Model.from_json(model) if model else None - - # Validate arguments against known Juju API types. - if last_connection_ is not None and not isinstance( - last_connection_, (bytes, str) - ): - raise Exception( - f"Expected last_connection_ to be a str, received: {type(last_connection_)}" - ) - - if model_ is not None and not isinstance(model_, (dict, Model)): - raise Exception(f"Expected model_ to be a Model, received: {type(model_)}") - - self.last_connection = last_connection_ - self.model = model_ - self.unknown_fields = unknown_fields - - -class UserModelList(Type): - _toSchema = {"user_models": "user-models"} - _toPy = {"user-models": "user_models"} - - def __init__(self, user_models=None, **unknown_fields): - """user_models : typing.Sequence[~UserModel]""" - user_models_ = [UserModel.from_json(o) for o in user_models or []] - - # Validate arguments against known Juju API types. - if user_models_ is not None and not isinstance( - user_models_, (bytes, str, list) - ): - raise Exception( - f"Expected user_models_ to be a Sequence, received: {type(user_models_)}" - ) - - self.user_models = user_models_ - self.unknown_fields = unknown_fields - - -class Value(Type): - _toSchema = { - "allocate_public_ip": "allocate-public-ip", - "arch": "arch", - "container": "container", - "cores": "cores", - "cpu_power": "cpu-power", - "image_id": "image-id", - "instance_role": "instance-role", - "instance_type": "instance-type", - "mem": "mem", - "root_disk": "root-disk", - "root_disk_source": "root-disk-source", - "spaces": "spaces", - "tags": "tags", - "virt_type": "virt-type", - "zones": "zones", - } - _toPy = { - "allocate-public-ip": "allocate_public_ip", - "arch": "arch", - "container": "container", - "cores": "cores", - "cpu-power": "cpu_power", - "image-id": "image_id", - "instance-role": "instance_role", - "instance-type": "instance_type", - "mem": "mem", - "root-disk": "root_disk", - "root-disk-source": "root_disk_source", - "spaces": "spaces", - "tags": "tags", - "virt-type": "virt_type", - "zones": "zones", - } - - def __init__( - self, - allocate_public_ip=None, - arch=None, - container=None, - cores=None, - cpu_power=None, - image_id=None, - instance_role=None, - instance_type=None, - mem=None, - root_disk=None, - root_disk_source=None, - spaces=None, - tags=None, - virt_type=None, - zones=None, - **unknown_fields, - ): - """allocate_public_ip : bool - arch : str - container : str - cores : int - cpu_power : int - image_id : str - instance_role : str - instance_type : str - mem : int - root_disk : int - root_disk_source : str - spaces : typing.Sequence[str] - tags : typing.Sequence[str] - virt_type : str - zones : typing.Sequence[str] - """ - allocate_public_ip_ = allocate_public_ip - arch_ = arch - container_ = container - cores_ = cores - cpu_power_ = cpu_power - image_id_ = image_id - instance_role_ = instance_role - instance_type_ = instance_type - mem_ = mem - root_disk_ = root_disk - root_disk_source_ = root_disk_source - spaces_ = spaces - tags_ = tags - virt_type_ = virt_type - zones_ = zones - - # Validate arguments against known Juju API types. - if allocate_public_ip_ is not None and not isinstance( - allocate_public_ip_, bool - ): - raise Exception( - f"Expected allocate_public_ip_ to be a bool, received: {type(allocate_public_ip_)}" - ) - - if arch_ is not None and not isinstance(arch_, (bytes, str)): - raise Exception(f"Expected arch_ to be a str, received: {type(arch_)}") - - if container_ is not None and not isinstance(container_, (bytes, str)): - raise Exception( - f"Expected container_ to be a str, received: {type(container_)}" - ) - - if cores_ is not None and not isinstance(cores_, int): - raise Exception(f"Expected cores_ to be a int, received: {type(cores_)}") - - if cpu_power_ is not None and not isinstance(cpu_power_, int): - raise Exception( - f"Expected cpu_power_ to be a int, received: {type(cpu_power_)}" - ) - - if image_id_ is not None and not isinstance(image_id_, (bytes, str)): - raise Exception( - f"Expected image_id_ to be a str, received: {type(image_id_)}" - ) - - if instance_role_ is not None and not isinstance(instance_role_, (bytes, str)): - raise Exception( - f"Expected instance_role_ to be a str, received: {type(instance_role_)}" - ) - - if instance_type_ is not None and not isinstance(instance_type_, (bytes, str)): - raise Exception( - f"Expected instance_type_ to be a str, received: {type(instance_type_)}" - ) - - if mem_ is not None and not isinstance(mem_, int): - raise Exception(f"Expected mem_ to be a int, received: {type(mem_)}") - - if root_disk_ is not None and not isinstance(root_disk_, int): - raise Exception( - f"Expected root_disk_ to be a int, received: {type(root_disk_)}" - ) - - if root_disk_source_ is not None and not isinstance( - root_disk_source_, (bytes, str) - ): - raise Exception( - f"Expected root_disk_source_ to be a str, received: {type(root_disk_source_)}" - ) - - if spaces_ is not None and not isinstance(spaces_, (bytes, str, list)): - raise Exception( - f"Expected spaces_ to be a Sequence, received: {type(spaces_)}" - ) - - if tags_ is not None and not isinstance(tags_, (bytes, str, list)): - raise Exception(f"Expected tags_ to be a Sequence, received: {type(tags_)}") - - if virt_type_ is not None and not isinstance(virt_type_, (bytes, str)): - raise Exception( - f"Expected virt_type_ to be a str, received: {type(virt_type_)}" - ) - - if zones_ is not None and not isinstance(zones_, (bytes, str, list)): - raise Exception( - f"Expected zones_ to be a Sequence, received: {type(zones_)}" - ) - - self.allocate_public_ip = allocate_public_ip_ - self.arch = arch_ - self.container = container_ - self.cores = cores_ - self.cpu_power = cpu_power_ - self.image_id = image_id_ - self.instance_role = instance_role_ - self.instance_type = instance_type_ - self.mem = mem_ - self.root_disk = root_disk_ - self.root_disk_source = root_disk_source_ - self.spaces = spaces_ - self.tags = tags_ - self.virt_type = virt_type_ - self.zones = zones_ - self.unknown_fields = unknown_fields - - -class VolumeAttachmentDetails(Type): - _toSchema = { - "bus_address": "bus-address", - "device_link": "device-link", - "device_name": "device-name", - "life": "life", - "plan_info": "plan-info", - "read_only": "read-only", - "volumeattachmentinfo": "VolumeAttachmentInfo", - } - _toPy = { - "VolumeAttachmentInfo": "volumeattachmentinfo", - "bus-address": "bus_address", - "device-link": "device_link", - "device-name": "device_name", - "life": "life", - "plan-info": "plan_info", - "read-only": "read_only", - } - - def __init__( - self, - volumeattachmentinfo=None, - bus_address=None, - device_link=None, - device_name=None, - life=None, - plan_info=None, - read_only=None, - **unknown_fields, - ): - """Volumeattachmentinfo : VolumeAttachmentInfo - bus_address : str - device_link : str - device_name : str - life : str - plan_info : VolumeAttachmentPlanInfo - read_only : bool - """ - volumeattachmentinfo_ = ( - VolumeAttachmentInfo.from_json(volumeattachmentinfo) - if volumeattachmentinfo - else None - ) - bus_address_ = bus_address - device_link_ = device_link - device_name_ = device_name - life_ = life - plan_info_ = ( - VolumeAttachmentPlanInfo.from_json(plan_info) if plan_info else None - ) - read_only_ = read_only - - # Validate arguments against known Juju API types. - if volumeattachmentinfo_ is not None and not isinstance( - volumeattachmentinfo_, (dict, VolumeAttachmentInfo) - ): - raise Exception( - f"Expected volumeattachmentinfo_ to be a VolumeAttachmentInfo, received: {type(volumeattachmentinfo_)}" - ) - - if bus_address_ is not None and not isinstance(bus_address_, (bytes, str)): - raise Exception( - f"Expected bus_address_ to be a str, received: {type(bus_address_)}" - ) - - if device_link_ is not None and not isinstance(device_link_, (bytes, str)): - raise Exception( - f"Expected device_link_ to be a str, received: {type(device_link_)}" - ) - - if device_name_ is not None and not isinstance(device_name_, (bytes, str)): - raise Exception( - f"Expected device_name_ to be a str, received: {type(device_name_)}" - ) - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if plan_info_ is not None and not isinstance( - plan_info_, (dict, VolumeAttachmentPlanInfo) - ): - raise Exception( - f"Expected plan_info_ to be a VolumeAttachmentPlanInfo, received: {type(plan_info_)}" - ) - - if read_only_ is not None and not isinstance(read_only_, bool): - raise Exception( - f"Expected read_only_ to be a bool, received: {type(read_only_)}" - ) - - self.volumeattachmentinfo = volumeattachmentinfo_ - self.bus_address = bus_address_ - self.device_link = device_link_ - self.device_name = device_name_ - self.life = life_ - self.plan_info = plan_info_ - self.read_only = read_only_ - self.unknown_fields = unknown_fields - - -class VolumeAttachmentInfo(Type): - _toSchema = { - "bus_address": "bus-address", - "device_link": "device-link", - "device_name": "device-name", - "plan_info": "plan-info", - "read_only": "read-only", - } - _toPy = { - "bus-address": "bus_address", - "device-link": "device_link", - "device-name": "device_name", - "plan-info": "plan_info", - "read-only": "read_only", - } - - def __init__( - self, - bus_address=None, - device_link=None, - device_name=None, - plan_info=None, - read_only=None, - **unknown_fields, - ): - """bus_address : str - device_link : str - device_name : str - plan_info : VolumeAttachmentPlanInfo - read_only : bool - """ - bus_address_ = bus_address - device_link_ = device_link - device_name_ = device_name - plan_info_ = ( - VolumeAttachmentPlanInfo.from_json(plan_info) if plan_info else None - ) - read_only_ = read_only - - # Validate arguments against known Juju API types. - if bus_address_ is not None and not isinstance(bus_address_, (bytes, str)): - raise Exception( - f"Expected bus_address_ to be a str, received: {type(bus_address_)}" - ) - - if device_link_ is not None and not isinstance(device_link_, (bytes, str)): - raise Exception( - f"Expected device_link_ to be a str, received: {type(device_link_)}" - ) - - if device_name_ is not None and not isinstance(device_name_, (bytes, str)): - raise Exception( - f"Expected device_name_ to be a str, received: {type(device_name_)}" - ) - - if plan_info_ is not None and not isinstance( - plan_info_, (dict, VolumeAttachmentPlanInfo) - ): - raise Exception( - f"Expected plan_info_ to be a VolumeAttachmentPlanInfo, received: {type(plan_info_)}" - ) - - if read_only_ is not None and not isinstance(read_only_, bool): - raise Exception( - f"Expected read_only_ to be a bool, received: {type(read_only_)}" - ) - - self.bus_address = bus_address_ - self.device_link = device_link_ - self.device_name = device_name_ - self.plan_info = plan_info_ - self.read_only = read_only_ - self.unknown_fields = unknown_fields - - -class VolumeAttachmentPlanInfo(Type): - _toSchema = {"device_attributes": "device-attributes", "device_type": "device-type"} - _toPy = {"device-attributes": "device_attributes", "device-type": "device_type"} - - def __init__(self, device_attributes=None, device_type=None, **unknown_fields): - """device_attributes : typing.Mapping[str, str] - device_type : str - """ - device_attributes_ = device_attributes - device_type_ = device_type - - # Validate arguments against known Juju API types. - if device_attributes_ is not None and not isinstance(device_attributes_, dict): - raise Exception( - f"Expected device_attributes_ to be a Mapping, received: {type(device_attributes_)}" - ) - - if device_type_ is not None and not isinstance(device_type_, (bytes, str)): - raise Exception( - f"Expected device_type_ to be a str, received: {type(device_type_)}" - ) - - self.device_attributes = device_attributes_ - self.device_type = device_type_ - self.unknown_fields = unknown_fields - - -class VolumeDetails(Type): - _toSchema = { - "info": "info", - "life": "life", - "machine_attachments": "machine-attachments", - "status": "status", - "storage": "storage", - "unit_attachments": "unit-attachments", - "volume_tag": "volume-tag", - } - _toPy = { - "info": "info", - "life": "life", - "machine-attachments": "machine_attachments", - "status": "status", - "storage": "storage", - "unit-attachments": "unit_attachments", - "volume-tag": "volume_tag", - } - - def __init__( - self, - info=None, - life=None, - machine_attachments=None, - status=None, - storage=None, - unit_attachments=None, - volume_tag=None, - **unknown_fields, - ): - """Info : VolumeInfo - life : str - machine_attachments : typing.Mapping[str, ~VolumeAttachmentDetails] - status : EntityStatus - storage : StorageDetails - unit_attachments : typing.Mapping[str, ~VolumeAttachmentDetails] - volume_tag : str - """ - info_ = VolumeInfo.from_json(info) if info else None - life_ = life - machine_attachments_ = { - k: VolumeAttachmentDetails.from_json(v) - for k, v in (machine_attachments or dict()).items() - } - status_ = EntityStatus.from_json(status) if status else None - storage_ = StorageDetails.from_json(storage) if storage else None - unit_attachments_ = { - k: VolumeAttachmentDetails.from_json(v) - for k, v in (unit_attachments or dict()).items() - } - volume_tag_ = volume_tag - - # Validate arguments against known Juju API types. - if info_ is not None and not isinstance(info_, (dict, VolumeInfo)): - raise Exception( - f"Expected info_ to be a VolumeInfo, received: {type(info_)}" - ) - - if life_ is not None and not isinstance(life_, (bytes, str)): - raise Exception(f"Expected life_ to be a str, received: {type(life_)}") - - if machine_attachments_ is not None and not isinstance( - machine_attachments_, dict - ): - raise Exception( - f"Expected machine_attachments_ to be a Mapping, received: {type(machine_attachments_)}" - ) - - if status_ is not None and not isinstance(status_, (dict, EntityStatus)): - raise Exception( - f"Expected status_ to be a EntityStatus, received: {type(status_)}" - ) - - if storage_ is not None and not isinstance(storage_, (dict, StorageDetails)): - raise Exception( - f"Expected storage_ to be a StorageDetails, received: {type(storage_)}" - ) - - if unit_attachments_ is not None and not isinstance(unit_attachments_, dict): - raise Exception( - f"Expected unit_attachments_ to be a Mapping, received: {type(unit_attachments_)}" - ) - - if volume_tag_ is not None and not isinstance(volume_tag_, (bytes, str)): - raise Exception( - f"Expected volume_tag_ to be a str, received: {type(volume_tag_)}" - ) - - self.info = info_ - self.life = life_ - self.machine_attachments = machine_attachments_ - self.status = status_ - self.storage = storage_ - self.unit_attachments = unit_attachments_ - self.volume_tag = volume_tag_ - self.unknown_fields = unknown_fields - - -class VolumeDetailsListResult(Type): - _toSchema = {"error": "error", "result": "result"} - _toPy = {"error": "error", "result": "result"} - - def __init__(self, error=None, result=None, **unknown_fields): - """Error : Error - result : typing.Sequence[~VolumeDetails] - """ - error_ = Error.from_json(error) if error else None - result_ = [VolumeDetails.from_json(o) for o in result or []] - - # Validate arguments against known Juju API types. - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if result_ is not None and not isinstance(result_, (bytes, str, list)): - raise Exception( - f"Expected result_ to be a Sequence, received: {type(result_)}" - ) - - self.error = error_ - self.result = result_ - self.unknown_fields = unknown_fields - - -class VolumeDetailsListResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~VolumeDetailsListResult]""" - results_ = [VolumeDetailsListResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields - - -class VolumeFilter(Type): - _toSchema = {"machines": "machines"} - _toPy = {"machines": "machines"} - - def __init__(self, machines=None, **unknown_fields): - """Machines : typing.Sequence[str]""" - machines_ = machines - - # Validate arguments against known Juju API types. - if machines_ is not None and not isinstance(machines_, (bytes, str, list)): - raise Exception( - f"Expected machines_ to be a Sequence, received: {type(machines_)}" - ) - - self.machines = machines_ - self.unknown_fields = unknown_fields - - -class VolumeFilters(Type): - _toSchema = {"filters": "filters"} - _toPy = {"filters": "filters"} - - def __init__(self, filters=None, **unknown_fields): - """Filters : typing.Sequence[~VolumeFilter]""" - filters_ = [VolumeFilter.from_json(o) for o in filters or []] - - # Validate arguments against known Juju API types. - if filters_ is not None and not isinstance(filters_, (bytes, str, list)): - raise Exception( - f"Expected filters_ to be a Sequence, received: {type(filters_)}" - ) - - self.filters = filters_ - self.unknown_fields = unknown_fields - - -class VolumeInfo(Type): - _toSchema = { - "hardware_id": "hardware-id", - "persistent": "persistent", - "pool": "pool", - "size": "size", - "volume_id": "volume-id", - "wwn": "wwn", - } - _toPy = { - "hardware-id": "hardware_id", - "persistent": "persistent", - "pool": "pool", - "size": "size", - "volume-id": "volume_id", - "wwn": "wwn", - } - - def __init__( - self, - hardware_id=None, - persistent=None, - pool=None, - size=None, - volume_id=None, - wwn=None, - **unknown_fields, - ): - """hardware_id : str - persistent : bool - pool : str - size : int - volume_id : str - wwn : str - """ - hardware_id_ = hardware_id - persistent_ = persistent - pool_ = pool - size_ = size - volume_id_ = volume_id - wwn_ = wwn - - # Validate arguments against known Juju API types. - if hardware_id_ is not None and not isinstance(hardware_id_, (bytes, str)): - raise Exception( - f"Expected hardware_id_ to be a str, received: {type(hardware_id_)}" - ) - - if persistent_ is not None and not isinstance(persistent_, bool): - raise Exception( - f"Expected persistent_ to be a bool, received: {type(persistent_)}" - ) - - if pool_ is not None and not isinstance(pool_, (bytes, str)): - raise Exception(f"Expected pool_ to be a str, received: {type(pool_)}") - - if size_ is not None and not isinstance(size_, int): - raise Exception(f"Expected size_ to be a int, received: {type(size_)}") - - if volume_id_ is not None and not isinstance(volume_id_, (bytes, str)): - raise Exception( - f"Expected volume_id_ to be a str, received: {type(volume_id_)}" - ) - - if wwn_ is not None and not isinstance(wwn_, (bytes, str)): - raise Exception(f"Expected wwn_ to be a str, received: {type(wwn_)}") - - self.hardware_id = hardware_id_ - self.persistent = persistent_ - self.pool = pool_ - self.size = size_ - self.volume_id = volume_id_ - self.wwn = wwn_ - self.unknown_fields = unknown_fields - - -class ZoneResult(Type): - _toSchema = {"available": "available", "error": "error", "name": "name"} - _toPy = {"available": "available", "error": "error", "name": "name"} - - def __init__(self, available=None, error=None, name=None, **unknown_fields): - """Available : bool - error : Error - name : str - """ - available_ = available - error_ = Error.from_json(error) if error else None - name_ = name - - # Validate arguments against known Juju API types. - if available_ is not None and not isinstance(available_, bool): - raise Exception( - f"Expected available_ to be a bool, received: {type(available_)}" - ) - - if error_ is not None and not isinstance(error_, (dict, Error)): - raise Exception(f"Expected error_ to be a Error, received: {type(error_)}") - - if name_ is not None and not isinstance(name_, (bytes, str)): - raise Exception(f"Expected name_ to be a str, received: {type(name_)}") - - self.available = available_ - self.error = error_ - self.name = name_ - self.unknown_fields = unknown_fields - - -class ZoneResults(Type): - _toSchema = {"results": "results"} - _toPy = {"results": "results"} - - def __init__(self, results=None, **unknown_fields): - """Results : typing.Sequence[~ZoneResult]""" - results_ = [ZoneResult.from_json(o) for o in results or []] - - # Validate arguments against known Juju API types. - if results_ is not None and not isinstance(results_, (bytes, str, list)): - raise Exception( - f"Expected results_ to be a Sequence, received: {type(results_)}" - ) - - self.results = results_ - self.unknown_fields = unknown_fields diff --git a/build/lib/juju/client/client.py b/build/lib/juju/client/client.py deleted file mode 100644 index e7e073b2..00000000 --- a/build/lib/juju/client/client.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -"""Replace auto-generated classes with our own, where necessary.""" - -from . import _client, _definitions, overrides # isort:skip - -for o in overrides.__all__: - if "Facade" not in o: - # Override stuff in _definitions, which is all imported - # into _client. We Monkey patch both the original class and - # the ref in _client (import shenanigans are fun!) - setattr(_definitions, o, getattr(overrides, o)) - setattr(_client, o, getattr(overrides, o)) - # We shouldn't be overriding Facades! - else: - raise ValueError( - "Cannot override a versioned Facade class -- you must patch it instead." - ) - -for o in overrides.__patches__: - # Patch a versioned Facade. - for client_version in _client.CLIENTS.values(): - try: - c_type = getattr(client_version, o) - except AttributeError: - # Not all the _client modules may have the - # facade. That's okay -- we just skip over them. - continue - o_type = getattr(overrides, o) - for a in dir(o_type): - if not a.startswith("_"): - setattr(c_type, a, getattr(o_type, a)) - -from ._client import * # noqa: F403,E402, isort:skip diff --git a/build/lib/juju/client/codegen.py b/build/lib/juju/client/codegen.py deleted file mode 100644 index eda73d02..00000000 --- a/build/lib/juju/client/codegen.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -from collections import defaultdict -from io import StringIO -from textwrap import indent - - -class CodeWriter(StringIO): - """Blob of text that, when used in the context of facade.py, ends up - holding the source code for a Python class and associated methods. - - """ - - INDENT = " " - - CLASS = 0 - METHOD = 1 - - def write(self, msg, depth=0): - if depth: - prefix = self.INDENT * depth - msg = indent(msg, prefix) - - return super().write(msg) - - def __str__(self): - return super().getvalue() - - -class Capture(defaultdict): - """A collection of CodeWriter objects, together representing a Python - module. - - """ - - def __init__(self, default_factory=CodeWriter, *args, **kwargs): - super().__init__(default_factory, *args, **kwargs) - - def clear(self, name): - """Reset one of the keys in this class, if it exists. - - This is necessary, because we don't worry about de-duplicating - the schemas for each version of juju up front, and this gives - us a way to sort of de-duplicate on the fly, by resetting a - specific CodeWriter instance before we start to write a class - into it. - - """ - try: - del self[name] - except KeyError: - pass diff --git a/build/lib/juju/client/connection.py b/build/lib/juju/client/connection.py deleted file mode 100644 index 03eb5096..00000000 --- a/build/lib/juju/client/connection.py +++ /dev/null @@ -1,924 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -import base64 -import json -import logging -import ssl -import urllib.request -import warnings -import weakref -from http.client import HTTPSConnection -from typing import Any, Literal, Sequence - -import macaroonbakery.bakery as bakery -import macaroonbakery.httpbakery as httpbakery -import websockets -from dateutil.parser import parse -from typing_extensions import Self, TypeAlias, overload - -from juju import errors, jasyncio, tag, utils -from juju.client import client -from juju.utils import IdQueue -from juju.version import CLIENT_VERSION - -from .facade import TypeEncoder, _Json, _RichJson -from .facade_versions import client_facade_versions, known_unsupported_facades - -SpecifiedFacades: TypeAlias = "dict[str, dict[Literal['versions'], Sequence[int]]]" -_WebSocket: TypeAlias = websockets.WebSocketClientProtocol - -LEVELS = ["TRACE", "DEBUG", "INFO", "WARNING", "ERROR"] -log = logging.getLogger("juju.client.connection") - - -class Monitor: - """Monitor helper class for our Connection class. - - Contains a reference to an instantiated Connection, along with a - reference to the Connection.receiver Future. Upon inspection of - these objects, this class determines whether the connection is in - an 'error', 'connected' or 'disconnected' state. - - Use this class to stay up to date on the health of a connection, - and take appropriate action if the connection errors out due to - network issues or other unexpected circumstances. - - """ - - ERROR = "error" - CONNECTED = "connected" - DISCONNECTING = "disconnecting" - DISCONNECTED = "disconnected" - - def __init__(self, connection: Connection): - self.connection = weakref.ref(connection) - self.reconnecting = jasyncio.Lock() - self.close_called = jasyncio.Event() - - @property - def status(self): - """Determine the status of the connection and receiver, and return - ERROR, CONNECTED, or DISCONNECTED as appropriate. - - For simplicity, we only consider ourselves to be connected - after the Connection class has setup a receiver task. This - only happens after the websocket is open, and the connection - isn't usable until that receiver has been started. - - """ - connection = self.connection() - - # the connection instance was destroyed but someone kept - # a separate reference to the monitor for some reason - if not connection: - return self.DISCONNECTED - - # connection cleanly disconnected or not yet opened - if not connection._ws: - return self.DISCONNECTED - - # close called but not yet complete - if self.close_called.is_set(): - return self.DISCONNECTING - - # connection closed uncleanly (we didn't call connection.close) - if connection.is_debug_log_connection: - stopped = connection._debug_log_task.cancelled() - else: - stopped = ( - connection._receiver_task is not None - and connection._receiver_task.cancelled() - ) - - if stopped or connection._ws.state is not websockets.protocol.State.OPEN: - return self.ERROR - - # everything is fine! - return self.CONNECTED - - -class Connection: - """Usage:: - - # Connect to an arbitrary api server - client = await Connection.connect( - api_endpoint, model_uuid, username, password, cacert) - - """ - - MAX_FRAME_SIZE = 2**22 - "Maximum size for a single frame. Defaults to 4MB." - facades: dict[str, int] - _specified_facades: dict[str, Sequence[int]] - bakery_client: Any - usertag: str | None - password: str | None - name: str - __request_id__: int - endpoints: list[tuple[str, str]] | None # Set by juju/controller.py - is_debug_log_connection: bool - monitor: Monitor - proxy: Any # Need to find types for this library - max_frame_size: int - _retries: int - _retry_backoff: float - uuid: str | None - messages: IdQueue - _ws: _WebSocket | None - - @classmethod - async def connect( - cls, - endpoint=None, - uuid: str | None = None, - username: str | None = None, - password: str | None = None, - cacert=None, - bakery_client=None, - max_frame_size: int | None = None, - retries=3, - retry_backoff=10, - specified_facades: SpecifiedFacades | None = None, - proxy=None, - debug_log_conn=None, - debug_log_params={}, - ) -> Self: - """Connect to the websocket. - - If uuid is None, the connection will be to the controller. Otherwise it - will be to the model. - - :param str endpoint: The hostname:port of the controller to connect to (or list of strings). - :param str uuid: The model UUID to connect to (None for a - controller-only connection). - :param str username: The username for controller-local users (or None - to use macaroon-based login.) - :param str password: The password for controller-local users. - :param str cacert: The CA certificate of the controller - (PEM formatted). - :param httpbakery.Client bakery_client: The macaroon bakery client to - to use when performing macaroon-based login. Macaroon tokens - acquired when logging will be saved to bakery_client.cookies. - If this is None, a default bakery_client will be used. - :param int max_frame_size: The maximum websocket frame size to allow. - :param int retries: When connecting or reconnecting, and all endpoints - fail, how many times to retry the connection before giving up. - :param int retry_backoff: Number of seconds to increase the wait - between connection retry attempts (a backoff of 10 with 3 retries - would wait 10s, 20s, and 30s). - :param specified_facades: (deprecated) define a series of facade versions you wish to override - to prevent using the conservative client pinning with in the client. - :param TextIOWrapper debug_log_conn: target if this is a debug log connection - :param dict debug_log_params: filtering parameters for the debug-log output - """ - self = cls() - if endpoint is None: - raise ValueError("no endpoint provided") - if not isinstance(endpoint, str) and not isinstance(endpoint, list): - raise TypeError("Endpoint should be either str or list") - self.uuid = uuid - if bakery_client is None: - bakery_client = httpbakery.Client() - self.bakery_client = bakery_client - if username and "@" in username and not username.endswith("@local"): - # We're trying to log in as an external user - we need to use - # macaroon authentication with no username or password. - if password is not None: - raise errors.JujuAuthError( - "cannot log in as external user with a password" - ) - username = None - self.usertag = tag.user(username) - self.password = password - - self.__request_id__ = 0 - - # The following instance variables are initialized by the - # _connect_with_redirect method, but create them here - # as a reminder that they will exist. - self.addr = None - self._ws = None - self.endpoint = None - self.endpoints = None - self.cacert = None - self.info = None - - self.debug_log_target = debug_log_conn - self.is_debug_log_connection = debug_log_conn is not None - self.debug_log_params = debug_log_params - self.debug_log_shown_lines = 0 # number of lines - - # Create that _Task objects but don't start the tasks yet. - self._pinger_task = None - self._receiver_task = None - self._debug_log_task = None - - self._retries = retries - self._retry_backoff = retry_backoff - - self.facades = {} - - if specified_facades: - warnings.warn( - "The `specified_facades` argument is deprecated and will be removed soon", - DeprecationWarning, - stacklevel=3, - ) - self._specified_facades = { - name: d["versions"] for name, d in specified_facades.items() - } - else: - self._specified_facades = {} - - self.messages = IdQueue() - self.monitor = Monitor(connection=self) - if max_frame_size is None: - max_frame_size = self.MAX_FRAME_SIZE - self.max_frame_size = max_frame_size - - self.proxy = proxy - if self.proxy is not None: - self.proxy.connect() - - _endpoints = ( - [(endpoint, cacert)] - if isinstance(endpoint, str) - else [(e, cacert) for e in endpoint] - ) - last_error = None - for _ep in _endpoints: - try: - if self.is_debug_log_connection: - # make a direct connection with basic auth if - # debug-log (i.e. no redirection or login) - await self._connect([_ep]) - else: - await self._connect_with_redirect([_ep]) - return self - except ssl.SSLError as e: - last_error = e - continue - except OSError as e: - logging.debug(f"Cannot access endpoint {_ep}: {e.strerror}") - last_error = e - continue - if last_error is not None: - raise last_error - raise Exception("Unable to connect to websocket") - - @property - def ws(self): - log.warning( - "Direct access to the websocket object may cause disruptions in asyncio event handling." - ) - return self._ws - - @property - def username(self) -> str | None: - if not self.usertag: - return None - return self.usertag[len("user-") :] - - @property - def is_using_old_client(self): - if self.info is None: - raise errors.JujuError("Not connected yet.") - return self.info["server-version"].startswith("2.") - - @property - def is_open(self): - return self.monitor.status == Monitor.CONNECTED - - def _get_ssl(self, cert: str | None = None) -> ssl.SSLContext: - context = ssl.create_default_context( - purpose=ssl.Purpose.SERVER_AUTH, cadata=cert - ) - if cert: - # Disable hostname checking if and only if we have an explicit cert - # to validate against, because the cert doesn't contain the IP addr - # of the controller, which is what self-bootstrapped controllers - # use. And because we pre-share and trust both the cert and - # endpoint address anyway, it's safe to skip that check. - # See: https://github.com/juju/python-libjuju/issues/302 - context.check_hostname = False - return context - - async def _open( - self, endpoint: str, cacert: str - ) -> tuple[_WebSocket, str, str, str]: - if self.is_debug_log_connection: - assert self.uuid - url = f"wss://user-{self.username}:{self.password}@{endpoint}/model/{self.uuid}/log" - elif self.uuid: - url = f"wss://{endpoint}/model/{self.uuid}/api" - else: - url = f"wss://{endpoint}/api" - - # We need to establish a server_hostname here for TLS sni if we are - # connecting through a proxy as the Juju controller certificates will - # not be covering the proxy - sock = None - server_hostname = None - if self.proxy is not None: - sock = self.proxy.socket() - server_hostname = "juju-app" - - return ( - ( - await websockets.connect( - url, - ssl=self._get_ssl(cacert), - max_size=self.max_frame_size, - server_hostname=server_hostname, - sock=sock, - ) - ), - url, - endpoint, - cacert, - ) - - async def close(self, to_reconnect: bool = False): - if not self._ws: - return - self.monitor.close_called.set() - - # Cancel all the tasks (that we started): - tasks_need_to_be_gathered = [] - if self._pinger_task: - tasks_need_to_be_gathered.append(self._pinger_task) - self._pinger_task.cancel() - if self._receiver_task: - tasks_need_to_be_gathered.append(self._receiver_task) - self._receiver_task.cancel() - if self._debug_log_task: - tasks_need_to_be_gathered.append(self._debug_log_task) - self._debug_log_task.cancel() - - await self._ws.close() - - if not to_reconnect: - try: - log.debug("Gathering all tasks for connection close") - await jasyncio.gather(*tasks_need_to_be_gathered) - except jasyncio.CancelledError: - pass - except websockets.exceptions.ConnectionClosed: - pass - - self._pinger_task = None - self._receiver_task = None - self._debug_log_task = None - - if self.proxy is not None: - self.proxy.close() - - async def _recv(self, request_id: int) -> dict[str, Any]: - if not self.is_open: - raise websockets.exceptions.ConnectionClosedOK(None, None) - try: - return await self.messages.get(request_id) - except GeneratorExit: - return {} - - def debug_log_filter_write(self, result): - write_or_not = True - - entity = result["tag"] - msg_lev = result["sev"] - mod = result["mod"] - msg = result["msg"] - - excluded_entities = self.debug_log_params["exclude"] - excluded_modules = self.debug_log_params["exclude_module"] - write_or_not = ( - write_or_not - and (mod not in excluded_modules) - and (entity not in excluded_entities) - ) - - included_entities = self.debug_log_params["include"] - only_these_modules = self.debug_log_params["include_module"] - write_or_not = ( - write_or_not - and (only_these_modules == [] or mod in only_these_modules) - and (included_entities == [] or entity in included_entities) - ) - - log_level = self.debug_log_params["level"] - - if log_level != "" and log_level not in LEVELS: - log.warning( - "Debug Logger: level should be one of %s, given %s" - % (LEVELS, log_level) - ) - else: - write_or_not = write_or_not and ( - log_level == "" or (LEVELS.index(msg_lev) >= LEVELS.index(log_level)) - ) - - # TODO - # lines = self.debug_log_params['lines'] - # no_tail = self.debug_log_params['no_tail'] - - if write_or_not: - ts = parse(result["ts"]) - - self.debug_log_target.write( - "%s %02d:%02d:%02d %s %s %s\n" - % (entity, ts.hour, ts.minute, ts.second, msg_lev, mod, msg) - ) - return 1 - else: - return 0 - - async def _debug_logger(self): - try: - while self.is_open: - result = await utils.run_with_interrupt( - self._ws.recv(), self.monitor.close_called, log=log - ) - if self.monitor.close_called.is_set(): - break - if result is not None and result != "{}\n": - result = json.loads(result) - - number_of_lines_written = self.debug_log_filter_write(result) - - self.debug_log_shown_lines += number_of_lines_written - - if self.debug_log_shown_lines >= self.debug_log_params["limit"]: - jasyncio.create_task(self.close(), name="Task_Close") - return - - except KeyError as e: - log.exception("Unexpected debug line -- %s" % e) - jasyncio.create_task(self.close(), name="Task_Close") - raise - except jasyncio.CancelledError: - jasyncio.create_task(self.close(), name="Task_Close") - raise - except websockets.exceptions.ConnectionClosed: - log.warning("Debug Logger: Connection closed, reconnecting") - # the reconnect has to be done as a task because the receiver will - # be cancelled by the reconnect and we don't want the reconnect - # to be aborted half-way through - jasyncio.ensure_future(self.reconnect()) - return - except Exception as e: - log.exception("Error in debug logger : %s" % e) - jasyncio.create_task(self.close(), name="Task_Close") - raise - - async def _receiver(self): - try: - while self.is_open: - result = await utils.run_with_interrupt( - self._ws.recv(), self.monitor.close_called, log=log - ) - if self.monitor.close_called.is_set(): - break - if result is not None: - result = json.loads(result) - await self.messages.put(result["request-id"], result) - except jasyncio.CancelledError: - log.debug("Receiver: Cancelled") - pass - except websockets.exceptions.ConnectionClosed as e: - log.warning("Receiver: Connection closed, reconnecting") - await self.messages.put_all(e) - # the reconnect has to be done as a task because the receiver will - # be cancelled by the reconnect and we don't want the reconnect - # to be aborted half-way through - jasyncio.ensure_future(self.reconnect()) - return - except Exception as e: - log.exception("Error in receiver") - # make pending listeners aware of the error - await self.messages.put_all(e) - raise - - async def _pinger(self): - """A Controller can time us out if we are silent for too long. This - is especially true in JaaS, which has a fairly strict timeout. - - To prevent timing out, we send a ping every ten seconds. - - """ - - async def _do_ping(): - try: - log.debug(f"Pinger {self._pinger_task}: pinging") - await pinger_facade.Ping() - except jasyncio.CancelledError: - raise - - pinger_facade = client.PingerFacade.from_connection(self) - try: - while True: - await utils.run_with_interrupt( - _do_ping(), self.monitor.close_called, log=log - ) - if self.monitor.close_called.is_set(): - break - await jasyncio.sleep(10) - except jasyncio.CancelledError: - log.debug("Pinger: Cancelled") - pass - except websockets.exceptions.ConnectionClosed: - # The connection has closed - we can't do anything - # more until the connection is restarted. - log.debug("ping failed because of closed connection") - pass - - @overload - async def rpc( - self, msg: dict[str, _Json], encoder: None = None - ) -> dict[str, _Json]: ... - - @overload - async def rpc( - self, msg: dict[str, _RichJson], encoder: TypeEncoder - ) -> dict[str, _Json]: ... - - async def rpc( - self, msg: dict[str, Any], encoder: json.JSONEncoder | None = None - ) -> dict[str, _Json]: - """Make an RPC to the API. The message is encoded as JSON - using the given encoder if any. - :param msg: Parameters for the call (will be encoded as JSON). - :param encoder: Encoder to be used when encoding the message. - :return: The result of the call. - :raises JujuAPIError: When there's an error returned. - :raises JujuError: - """ - self.__request_id__ += 1 - msg["request-id"] = self.__request_id__ - if "params" not in msg: - msg["params"] = {} - if "version" not in msg: - msg["version"] = self.facades[msg["type"]] - outgoing = json.dumps(msg, indent=2, cls=encoder) - log.debug(f"connection id: {id(self)} ---> {outgoing}") - for attempt in range(3): - if self.monitor.status == Monitor.DISCONNECTED: - # closed cleanly; shouldn't try to reconnect - raise websockets.exceptions.ConnectionClosed( - websockets.frames.Close( - websockets.frames.CloseCode.NORMAL_CLOSURE, "websocket closed" - ) - ) - try: - await self._ws.send(outgoing) - break - except websockets.ConnectionClosed: - if attempt == 2: - raise - log.warning("RPC: Connection closed, reconnecting") - # the reconnect has to be done in a separate task because, - # if it is triggered by the pinger, then this RPC call will - # be cancelled when the pinger is cancelled by the reconnect, - # and we don't want the reconnect to be aborted halfway through - await jasyncio.wait([jasyncio.create_task(self.reconnect())]) - if self.monitor.status != Monitor.CONNECTED: - # reconnect failed; abort and shutdown - log.error("RPC: Automatic reconnect failed") - raise - result = await self._recv(msg["request-id"]) - log.debug(f"connection id : {id(self)} <--- {result}") - - if not result: - return result - - if "error" in result: - # API Error Response - raise errors.JujuAPIError(result) - - if "response" not in result: - # This may never happen - return result - - if "results" in result["response"]: - # Check for errors in a result list. - # TODO This loses the results that might have succeeded. - # Perhaps JujuError should return all the results including - # errors, or perhaps a keyword parameter to the rpc method - # could be added to trigger this behaviour. - err_results = [ - res["error"]["message"] - for res in (result["response"]["results"] or []) - if res.get("error", {}).get("message") - ] - if err_results: - raise errors.JujuError(err_results) - - elif result["response"].get("error", {}).get("message"): - raise errors.JujuError(result["response"]["error"]["message"]) - - return result - - def _http_headers(self) -> dict[str, str]: - """Return dictionary of http headers necessary for making an http - connection to the endpoint of this Connection. - - :return: Dictionary of headers - - """ - if not self.usertag: - return {} - - creds = "{}:{}".format(self.usertag, self.password or "") - token = base64.b64encode(creds.encode()) - return {"Authorization": f"Basic {token.decode()}"} - - def https_connection(self) -> tuple[HTTPSConnection, dict[str, str], str]: - """Return an https connection to this Connection's endpoint. - - Returns a 3-tuple containing:: - - 1. The :class:`HTTPSConnection` instance - 2. Dictionary of auth headers to be used with the connection - 3. The root url path (str) to be used for requests. - - """ - endpoint = self.endpoint - # Support IPv6 by right splitting on : and removing [] around IP address for host - host, remainder = endpoint.rsplit(":", 1) - host = host.strip("[]") - port = remainder - if "/" in remainder: - port, _ = remainder.split("/", 1) - - conn = HTTPSConnection( - host, - int(port), - context=self._get_ssl(self.cacert), - ) - - path = f"/model/{self.uuid}" if self.uuid else "" - return conn, self._http_headers(), path - - async def clone(self): - """Return a new Connection, connected to the same websocket endpoint - as this one. - - """ - return await Connection.connect(**self.connect_params()) - - def connect_params(self): - """Return a dict of parameters suitable for passing to - Connection.connect that can be used to make a new connection - to the same controller (and model if specified). - """ - return { - "endpoint": self.endpoint, - "uuid": self.uuid, - "username": self.username, - "password": self.password, - "cacert": self.cacert, - "bakery_client": self.bakery_client, - "max_frame_size": self.max_frame_size, - "proxy": self.proxy, - } - - async def controller(self): - """Return a Connection to the controller at self.endpoint""" - return await Connection.connect( - self.endpoint, - username=self.username, - password=self.password, - cacert=self.cacert, - bakery_client=self.bakery_client, - max_frame_size=self.max_frame_size, - ) - - async def reconnect(self): - """Force a reconnection.""" - monitor = self.monitor - if monitor.reconnecting.locked() or monitor.close_called.is_set(): - return - async with monitor.reconnecting: - await self.close(to_reconnect=True) - connector = ( - self._connect - if self.is_debug_log_connection - else self._connect_with_login - ) - res = await connector( - [(self.endpoint, self.cacert)] if not self.endpoints else self.endpoints - ) - if not self.is_debug_log_connection: - self._build_facades(res.get("facades", {})) - if not self._pinger_task: - log.debug("reconnect: scheduling a pinger task") - self._pinger_task = jasyncio.create_task( - self._pinger(), name="Task_Pinger" - ) - - async def _connect(self, endpoints): - if len(endpoints) == 0: - raise errors.JujuConnectionError("no endpoints to connect to") - - async def _try_endpoint( - endpoint, cacert, delay - ) -> tuple[_WebSocket, str, str, str]: - if delay: - await jasyncio.sleep(delay) - return await self._open(endpoint, cacert) - - # Try all endpoints in parallel, with slight increasing delay (+100ms - # for each subsequent endpoint); the delay allows us to prefer the - # earlier endpoints over the latter. Use first successful connection. - tasks = [ - jasyncio.ensure_future(_try_endpoint(endpoint, cacert, 0.1 * i)) - for i, (endpoint, cacert) in enumerate(endpoints) - ] - result: tuple[_WebSocket, str, str, str] | None = None - - for attempt in range(self._retries + 1): - for task in jasyncio.as_completed(tasks): - try: - result = await task - break - except ConnectionError: - continue # ignore; try another endpoint - else: - _endpoints_str = ", ".join([endpoint for endpoint, cacert in endpoints]) - if attempt < self._retries: - log.debug( - f"Retrying connection to endpoints: {_endpoints_str}; attempt {attempt + 1} of {self._retries + 1}" - ) - await jasyncio.sleep((attempt + 1) * self._retry_backoff) - continue - else: - raise errors.JujuConnectionError( - f"Unable to connect to any endpoint: {_endpoints_str}" - ) - # only executed if inner loop's else did not continue - # (i.e., inner loop did break due to successful connection) - break - - for task in tasks: - task.cancel() - - assert result # loop raises or sets the result - - self._ws = result[0] - self.addr = result[1] - self.endpoint = result[2] - self.cacert = result[3] - - # If this is a debug-log connection, and the _debug_log_task - # is not created yet, then go ahead and schedule it - if self.is_debug_log_connection and not self._debug_log_task: - self._debug_log_task = jasyncio.create_task( - self._debug_logger(), name="Task_Debug_Log" - ) - - # If this is regular connection, and we dont have a - # receiver_task yet, then schedule a _receiver_task - elif not self.is_debug_log_connection and not self._receiver_task: - log.debug("_connect: scheduling a receiver task") - self._receiver_task = jasyncio.create_task( - self._receiver(), name="Task_Receiver" - ) - - log.debug("Driver connected to juju %s", self.addr) - self.monitor.close_called.clear() - - async def _connect_with_login(self, endpoints): - """Connect to the websocket. - - If uuid is None, the connection will be to the controller. Otherwise it - will be to the model. - :return: The response field of login response JSON object. - """ - success = False - try: - await self._connect(endpoints) - # It's possible that we may get several discharge-required errors, - # corresponding to different levels of authentication, so retry - # a few times. - for _ in range(0, 2): - result = (await self.login())["response"] - macaroon_json = result.get("discharge-required") - if macaroon_json is None: - self.info = result - success = True - return result - macaroon = bakery.Macaroon.from_dict(macaroon_json) - self.bakery_client.handle_error( - httpbakery.Error( - code=httpbakery.ERR_DISCHARGE_REQUIRED, - message=result.get("discharge-required-error"), - version=macaroon.version, - info=httpbakery.ErrorInfo( - macaroon=macaroon, - macaroon_path=result.get("macaroon-path"), - ), - ), - # note: remove the port number. - "https://" + self.endpoint + "/", - ) - raise errors.JujuAuthError("failed to authenticate after several attempts") - finally: - if not success: - await self.close() - - async def _connect_with_redirect(self, endpoints): - try: - login_result = await self._connect_with_login(endpoints) - except errors.JujuRedirectException as e: - # Bubble up exception if the client should not follow the redirect - if e.follow_redirect is False: - raise - login_result = await self._connect_with_login(e.endpoints) - self._build_facades(login_result.get("facades", {})) - if not self._pinger_task: - log.debug("_connect_with_redirect: scheduling a pinger task") - self._pinger_task = jasyncio.create_task(self._pinger(), name="Task_Pinger") - - # _build_facades takes the facade list that comes from the connection with the controller, - # validates that the client knows about them (client_facade_versions) and builds the facade list - # (into the self._specified facades) with the max versions that both the client and the controller - # can negotiate on - def _build_facades(self, facades_from_connection): - self.facades.clear() - for facade in facades_from_connection: - name = facade["name"] - if name in self._specified_facades: - client_versions = self._specified_facades[name] - elif name in client_facade_versions: - client_versions = client_facade_versions[name] - elif name in known_unsupported_facades: - continue - else: - log.warning(f"unexpected facade {name} received from the controller") - continue - - controller_versions = facade["versions"] - candidates = set(client_versions) & set(controller_versions) - if not candidates: - log.warning( - f"unknown common facade version for {name},\n" - f"versions known to client : {client_versions}\n" - f"versions known to controller : {controller_versions}" - ) - continue - self.facades[name] = max(candidates) - - async def login(self): - params = {} - # Set the client version - params["client-version"] = CLIENT_VERSION - params["auth-tag"] = self.usertag - if self.password: - params["credentials"] = self.password - else: - macaroons = _macaroons_for_domain(self.bakery_client.cookies, self.endpoint) - params["macaroons"] = [ - [bakery.macaroon_to_dict(m) for m in ms] for ms in macaroons - ] - - try: - return await self.rpc({ - "type": "Admin", - "request": "Login", - "version": 3, - "params": params, - }) - except errors.JujuAPIError as e: - if e.error_code != "redirection required": - raise - log.info("Controller requested redirect") - # Check if the redirect error provides a payload with embedded - # redirection info (juju 2.6+ controller). In this case, return a - # redirect exception which the library should not automatically - # follow but rather bubble up to the user. This matches the - # behaviour of juju cli whereas for JAAS-like redirects we will - # need to make an extra RPC call to get the redirect info. - if e.error_info is not None: - raise errors.JujuRedirectException(e.error_info, False) from e - - # Fetch additional redirection information now so that - # we can safely close the connection after login - # fails. - redirect_info = ( - await self.rpc({ - "type": "Admin", - "request": "RedirectInfo", - "version": 3, - }) - )["response"] - raise errors.JujuRedirectException(redirect_info, True) from e - - -def _macaroons_for_domain(cookies, domain): - """Return any macaroons from the given cookie jar that - apply to the given domain name. - """ - req = urllib.request.Request("https://" + domain + "/") - cookies.add_cookie_header(req) - return httpbakery.extract_macaroons(req) diff --git a/build/lib/juju/client/connector.py b/build/lib/juju/client/connector.py deleted file mode 100644 index cc307902..00000000 --- a/build/lib/juju/client/connector.py +++ /dev/null @@ -1,232 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -import copy -import logging -from typing import Any - -import macaroonbakery.httpbakery as httpbakery -from packaging import version - -from juju.client import client -from juju.client.connection import Connection -from juju.client.gocookies import GoCookieJar, go_to_py_cookie -from juju.client.jujudata import API_ENDPOINTS_KEY, FileJujuData -from juju.client.proxy.factory import proxy_from_config -from juju.errors import JujuConnectionError, JujuError, JujuUnknownVersion -from juju.version import CLIENT_VERSION - -log = logging.getLogger("connector") - - -class NoConnectionException(Exception): - """Raised by Connector when the connection method is called - and there is no current connection. - """ - - pass - - -class Connector: - """Abstracts out a reconnectable client that can connect - to controllers and models found in the Juju data files. - """ - - def __init__( - self, - max_frame_size: int | None = None, - bakery_client: Any | None = None, - jujudata: Any | None = None, - ): - """Initialize a connector that will use the given parameters - by default when making a new connection - """ - self.max_frame_size = max_frame_size - self.bakery_client = bakery_client - self._connection = None - self._log_connection = None - self.controller_uuid = None - self.model_name = None - self.jujudata = jujudata or FileJujuData() - - def is_connected(self) -> bool: - """Report whether there is a currently connected controller or not""" - return self._connection is not None - - def connection(self) -> Connection: - """Return the current connection; raises an exception if there - is no current connection. - """ - if not self.is_connected(): - raise NoConnectionException("not connected") - assert self._connection - return self._connection - - async def connect(self, **kwargs): - """Connect to an arbitrary Juju model. - - kwargs are passed through to Connection.connect() - """ - kwargs.setdefault("max_frame_size", self.max_frame_size) - kwargs.setdefault("bakery_client", self.bakery_client) - if "macaroons" in kwargs: - if not kwargs["bakery_client"]: - kwargs["bakery_client"] = httpbakery.Client() - if not kwargs["bakery_client"].cookies: - kwargs["bakery_client"].cookies = GoCookieJar() - jar = kwargs["bakery_client"].cookies - for macaroon in kwargs.pop("macaroons"): - jar.set_cookie(go_to_py_cookie(macaroon)) - if "debug_log_conn" in kwargs: - assert self._connection - self._log_connection = await Connection.connect(**kwargs) - else: - # TODO (cderici): we need to investigate how to reuse/share - # connections between Model & Controller objects - # At least either avoid overwriting self._connection with - # different types of connections (model, controller), or - # have an indication of which type of entity this is - # connected to. - if self._connection: - await self._connection.close() - - account = kwargs.pop("account", {}) - # Prioritize the username and password that user provided - # If not enough, try to patch it with info from accounts.yaml - if "username" not in kwargs and account.get("user"): - kwargs.update(username=account.get("user")) - if "password" not in kwargs and account.get("password"): - kwargs.update(password=account.get("password")) - - if not ({"username", "password"}.issubset(kwargs)): - required = {"username", "password"}.difference(kwargs) - raise ValueError( - f"Some authentication parameters are required : {','.join(required)}" - ) - self._connection = await Connection.connect(**kwargs) - - # Check if we support the target controller - server_version = self._connection.info["server-version"] - try: - juju_server_version = version.parse(server_version) - except version.InvalidVersion as err: - # We're only interested in the major version, so - # we attempt to clean up versions such as 3.4-rc1.2 as just 3.4 - if "-" not in server_version: - raise JujuUnknownVersion(err) - juju_server_version = version.parse(server_version.split("-")[0]) - - # CLIENT_VERSION statically comes from the VERSION file in the repo - client_version = version.parse(CLIENT_VERSION) - - if juju_server_version.major != client_version.major: - raise JujuConnectionError( - "juju server-version %s not supported" % juju_server_version - ) - - async def disconnect(self, entity): - """Shut down the watcher task and close websockets.""" - if self._connection: - log.debug(f"Connector: closing {entity} connection") - await self._connection.close() - self._connection = None - if self._log_connection: - log.debug("Also closing debug-log connection") - await self._log_connection.close() - self._log_connection = None - - async def connect_controller( - self, controller_name=None, specified_facades=None, **kwargs - ): - """Connect to a controller by name. If the name is empty, it - connect to the current controller. - """ - if not controller_name: - controller_name = self.jujudata.current_controller() - - controller = self.jujudata.controllers()[controller_name] - endpoints = controller[API_ENDPOINTS_KEY] - accounts = self.jujudata.accounts().get(controller_name, {}) - - proxy = proxy_from_config(controller.get("proxy-config", None)) - - kwargs.update( - endpoint=endpoints, - uuid=None, - account=accounts, - cacert=controller.get("ca-cert"), - bakery_client=self.bakery_client_for_controller(controller_name), - specified_facades=specified_facades, - proxy=proxy, - ) - await self.connect(**kwargs) - self.controller_name = controller_name - self.controller_uuid = controller["uuid"] - - async def connect_model(self, _model_name=None, **kwargs): - """Connect to a model by name. If either controller or model - parts of the name are empty, the current controller and/or model - will be used. - - :param str model: : - """ - try: - controller_name, _model_name = self.jujudata.parse_model(_model_name) - controller = self.jujudata.controllers().get(controller_name) - except JujuError as e: - raise JujuConnectionError(e.message) from e - if controller is None: - raise JujuConnectionError(f"Controller {controller_name} not found") - endpoints = controller[API_ENDPOINTS_KEY] - account = self.jujudata.accounts().get(controller_name, {}) - models = self.jujudata.models().get(controller_name, {}).get("models", {}) - model_uuid = None - if _model_name in models: - model_uuid = models[_model_name]["uuid"] - else: - # let's try to find it through the actual controller - await self.connect_controller(controller_name=controller_name) - # get the facade - controller_facade = client.ControllerFacade.from_connection( - self.connection() - ) - # get all the user models from the api - response = await controller_facade.AllModels() - # search the one that contains admin/model_name - for user_model in response.user_models: - if "admin/" + user_model.model.name == _model_name: - model_uuid = user_model.model.uuid - - if model_uuid is None: - raise JujuConnectionError(f"Model not found: {_model_name}") - - proxy = proxy_from_config(controller.get("proxy-config", None)) - - # TODO remove the need for base.CleanModel to subclass - # JujuData. - kwargs.update( - endpoint=endpoints, - uuid=model_uuid, - account=account, - cacert=controller.get("ca-cert"), - bakery_client=self.bakery_client_for_controller(controller_name), - proxy=proxy, - ) - await self.connect(**kwargs) - # TODO this might be a good spot to trigger refreshing the - # local cache (the connection to the model might help) - self.model_name = controller_name + ":" + _model_name - return model_uuid - - def bakery_client_for_controller(self, controller_name): - """Make a copy of the bakery client with a the appropriate controller's - cookiejar in it. - """ - bakery_client = self.bakery_client - if bakery_client: - bakery_client = copy.copy(bakery_client) - else: - bakery_client = httpbakery.Client() - bakery_client.cookies = self.jujudata.cookies_for_controller(controller_name) - return bakery_client diff --git a/build/lib/juju/client/facade.py b/build/lib/juju/client/facade.py deleted file mode 100644 index f0ee7513..00000000 --- a/build/lib/juju/client/facade.py +++ /dev/null @@ -1,989 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -import argparse -import builtins -import functools -import json -import keyword -import pprint -import re -import textwrap -import typing -from collections import defaultdict -from glob import glob -from pathlib import Path -from typing import Any, Mapping, Sequence - -import packaging.version -import typing_inspect -from typing_extensions import TypeAlias - -from . import codegen - -# Plain JSON, what is received from Juju -_JsonLeaf: TypeAlias = "None | bool | int | float | str" -_Json: TypeAlias = "_JsonLeaf|list[_Json]|dict[str, _Json]" - -# Type-enriched JSON, what can be sent to Juju -_RichLeaf: TypeAlias = "_JsonLeaf|Type" -_RichJson: TypeAlias = "_RichLeaf|list[_RichJson]|dict[str, _RichJson]" - -_marker = object() - -JUJU_VERSION = re.compile(r"[0-9]+\.[0-9-]+[\.\-][0-9a-z]+(\.[0-9]+)?") -# Workaround for https://bugs.launchpad.net/juju/+bug/1683906 -NAUGHTY_CLASSES = ["ClientFacade", "Client", "ModelStatusInfo"] - - -# Map basic types to Python's typing with a callable -SCHEMA_TO_PYTHON = { - "string": str, - "integer": int, - "float": float, - "number": float, - "boolean": bool, - "object": Any, -} - - -# Friendly warning message to stick at the top of generated files. -HEADER = """\ -# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py. -# Changes will be overwritten/lost when the file is regenerated. - -""" - - -# Classes and helper functions that we'll write to _client.py -LOOKUP_FACADE = ''' -def lookup_facade(name, version): - """ - Given a facade name and version, attempt to pull that facade out - of the correct client.py file. - - """ - for _version in range(int(version), 0, -1): - try: - facade = getattr(CLIENTS[str(_version)], name) - return facade - except (KeyError, AttributeError): - continue - else: - raise ImportError("No supported version for facade: " - "{}".format(name)) - -''' - -TYPE_FACTORY = ''' -class TypeFactory: - @classmethod - def from_connection(cls, connection): - """ - Given a connected Connection object, return an initialized and - connected instance of an API Interface matching the name of - this class. - - @param connection: initialized Connection object. - - """ - facade_name = cls.__name__ - if not facade_name.endswith('Facade'): - raise TypeError('Unexpected class name: {}'.format(facade_name)) - facade_name = facade_name[:-len('Facade')] - version = connection.facades.get(facade_name) - if version is None: - raise Exception('No facade {} in facades {}'.format(facade_name, - connection.facades)) - - c = lookup_facade(cls.__name__, version) - c = c() - c.connect(connection) - - return c - - @classmethod - def best_facade_version(cls, connection): - """ - Returns the best facade version for a given facade. This will help with - trying to provide different functionality for different facade versions. - - @param connection: initialized Connection object. - """ - facade_name = cls.__name__ - if not facade_name.endswith('Facade'): - raise TypeError('Unexpected class name: {}'.format(facade_name)) - facade_name = facade_name[:-len('Facade')] - return connection.facades.get(facade_name) - - -''' - -CLIENT_TABLE = """ -CLIENTS = {{ - {clients} -}} - -""" - - -class KindRegistry(dict): - def register(self, name, version, obj): - self[name] = { - version: { - "object": obj, - } - } - - def lookup(self, name, version=None): - """If version is omitted, max version is used""" - versions = self.get(name) - if not versions: - return None - if version: - return versions[version] - return versions[max(versions)] - - def get_obj(self, name, version=None): - result = self.lookup(name, version) - if result: - obj = result["object"] - return obj - return None - - -class TypeRegistry(dict): - def __init__(self, schema): - self.schema = schema - - def get(self, name): - # Two way mapping - refname = self.schema.reference_name(name) - if refname not in self: - result = typing.TypeVar(refname) - self[refname] = result - self[result] = refname - - return self[refname] - - def get_ref_type(self, ref): - return self.get(ref) - - def obj_type(self, obj): - kind = obj.get("type") - if not kind: - raise ValueError("%s has no type" % obj) - result = SCHEMA_TO_PYTHON.get(kind) - if not result: - raise ValueError("%s has type %s" % (obj, kind)) - return result - - def ref_type(self, obj): - return self.get_ref_type(obj["$ref"]) - - -CLASSES = {} -factories = codegen.Capture() - - -def booler(v): - if v == "false": - return False - return bool(v) - - -basic_types = [str, bool, int, float] - - -type_mapping = { - "str": "(bytes, str)", - "Sequence": "(bytes, str, list)", - "Union": "dict", - "Mapping": "dict", -} - - -def name_to_py(name): - result = name.replace("-", "_") - result = result.lower() - if keyword.iskeyword(result) or result in dir(builtins): - result += "_" - return result - - -def var_type_to_py(kind): - return "None" - - -def kind_to_py(kind): - if kind is None or kind is typing.Any: - return "None", "", False - - name = "" - if typing_inspect.is_generic_type(kind): - origin = typing_inspect.get_origin(kind) - name = origin.__name__ - else: - name = kind.__name__ - - if kind in basic_types or type(kind) in basic_types: - return name, type_mapping.get(name) or name, True - if name in type_mapping: - return name, type_mapping[name], True - - suffix = name.lstrip("~") - return suffix, f"(dict, {suffix})", True - - -def strcast(kind, keep_builtins=False): - if (kind in basic_types or type(kind) in basic_types) and keep_builtins is False: - return kind.__name__ - if str(kind).startswith("~"): - return str(kind)[1:] - if kind is typing.Any: - return "Any" - try: - if issubclass(kind, typing.GenericMeta): - return str(kind)[1:] - except AttributeError: - pass - return kind - - -class Args(list): - def __init__(self, schema, defs): - self.schema = schema - self.defs = defs - if defs: - rtypes = schema.registry.get_obj(schema.types[defs]) - if len(rtypes) == 1: - if not self.do_explode(rtypes[0][1]): - for name, rtype in rtypes: - self.append((name, rtype)) - else: - for name, rtype in rtypes: - self.append((name, rtype)) - - def do_explode(self, kind): - if kind is Any: - return False - if kind in basic_types or type(kind) is typing.TypeVar: - return False - if typing_inspect.is_generic_type(kind) and issubclass( - typing_inspect.get_origin(kind), Sequence - ): - return False - if typing_inspect.is_generic_type(kind) and issubclass( - typing_inspect.get_origin(kind), Mapping - ): - return False - self.clear() - self.extend(Args(self.schema, kind)) - return True - - def py_to_schema_mapping(self): - m = {} - for n, _ in self: - m[name_to_py(n)] = n - return m - - def schema_to_py_mapping(self): - m = {} - for n, _ in self: - m[n] = name_to_py(n) - return m - - def _format(self, name, rtype, typed=True): - if typed: - return f"{name_to_py(name)} : {strcast(rtype)}" - else: - return name_to_py(name) - - def _get_arg_str(self, typed=False, joined=", "): - if self: - parts = [self._format(item[0], item[1], typed) for item in self] - if joined: - return joined.join(parts) - return parts - return "" - - def as_kwargs(self): - if self: - parts = [] - for item in self: - var_name = name_to_py(item[0]) - var_type = var_type_to_py(item[1]) - parts.append(f"{var_name}={var_type}") - return ", ".join(parts) - return "" - - def as_validation(self): - """as_validation returns a series of validation statements for every item - in the the Args. - """ - parts = [] - for item in self: - var_name = name_to_py(item[0]) - var_type, var_sub_type, ok = kind_to_py(item[1]) - if ok: - parts.append(build_validation(var_name, var_type, var_sub_type)) - return "\n".join(parts) - - def typed(self): - return self._get_arg_str(True) - - def __str__(self): - return self._get_arg_str(False) - - def get_doc(self): - return self._get_arg_str(True, "\n") - - -def build_validation(name, instance_type, instance_sub_type, ident=None): - indent = ident or " " - source = f"""{indent}if {name} is not None and not isinstance({name}, {instance_sub_type}): -{indent} raise Exception("Expected {name} to be a {instance_type}, received: {{}}".format(type({name}))) -""" - return source - - -def build_types(schema, capture): - indent = " " - for kind in sorted( - (k for k in schema.types if not isinstance(k, str)), key=lambda x: str(x) - ): - name = schema.types[kind] - if not name: - # when running on juju 3.1.0 client-only schemas, we get a seemingly empty entry with no name - # this breaks codegen when generating a class with no name so we explicitly skip it here - # note that this is not a problem with the original 3.1.0 full schema (client + others) - # nor is it a problem with client-only schemas for latter juju versions (e.g. 3.3.0) - continue - if name in capture and name not in NAUGHTY_CLASSES: - continue - args = Args(schema, kind) - # Write Factory class for _client.py - make_factory(name) - # Write actual class - source = [ - """ -class {}(Type): - _toSchema = {} - _toPy = {} - def __init__(self{}{}, **unknown_fields): - ''' -{} - '''""".format( - name, - # pprint these to get stable ordering across regens - pprint.pformat(args.py_to_schema_mapping(), width=999), - pprint.pformat(args.schema_to_py_mapping(), width=999), - ", " if args else "", - args.as_kwargs(), - textwrap.indent(args.get_doc(), indent * 2), - ) - ] - - if not args: - source.append(f"{indent * 2}self.unknown_fields = unknown_fields") - else: - # do the validation first, before setting the variables - for arg in args: - arg_name = name_to_py(arg[0]) - arg_type = arg[1] - arg_type_name = strcast(arg_type) - if arg_type in basic_types or arg_type is typing.Any: - source.append(f"{indent * 2}{arg_name}_ = {arg_name}") - elif type(arg_type) is typing.TypeVar: - source.append( - f"{indent * 2}{arg_name}_ = {arg_type_name}.from_json({arg_name}) if {arg_name} else None" - ) - elif typing_inspect.is_generic_type(arg_type) and issubclass( - typing_inspect.get_origin(arg_type), Sequence - ): - parameters = typing_inspect.get_parameters(arg_type) - value_type = parameters[0] if len(parameters) else None - if type(value_type) is typing.TypeVar: - source.append( - f"{indent * 2}{arg_name}_ = [{strcast(value_type)}.from_json(o) for o in {arg_name} or []]" - ) - else: - source.append(f"{indent * 2}{arg_name}_ = {arg_name}") - elif typing_inspect.is_generic_type(arg_type) and issubclass( - typing_inspect.get_origin(arg_type), Mapping - ): - parameters = typing_inspect.get_parameters(arg_type) - value_type = parameters[0] if len(parameters) else None - if type(value_type) is typing.TypeVar: - source.append( - f"{indent * 2}{arg_name}_ = {{k: {strcast(value_type)}.from_json(v) " - f"for k, v in ({arg_name} or dict()).items()}}" - ) - else: - source.append(f"{indent * 2}{arg_name}_ = {arg_name}") - else: - source.append(f"{indent * 2}{arg_name}_ = {arg_name}") - if len(args) > 0: - source.append( - f"\n{indent * 2}# Validate arguments against known Juju API types." - ) - for arg in args: - arg_name = f"{name_to_py(arg[0])}_" - arg_type, arg_sub_type, ok = kind_to_py(arg[1]) - if ok: - source.append( - "{}".format( - build_validation( - arg_name, arg_type, arg_sub_type, ident=indent * 2 - ) - ) - ) - - for arg in args: - arg_name = name_to_py(arg[0]) - source.append(f"{indent * 2}self.{arg_name} = {arg_name}_") - # Ensure that we take the kwargs (unknown_fields) and put it on the - # Results/Params so we can inspect it. - source.append(f"{indent * 2}self.unknown_fields = unknown_fields") - - source = "\n".join(source) - capture.clear(name) - capture[name].write(source) - capture[name].write("\n\n") - if name is None: - print(source) - co = compile(source, __name__, "exec") - ns = _getns(schema) - exec(co, ns) # noqa: S102 - cls = ns[name] - CLASSES[name] = cls - - -def retspec(schema, defs): - # return specs - # only return 1, so if there is more than one type - # we need to include a union - # In truth there is only 1 return - # Error or the expected Type - if not defs: - return None - if defs in basic_types: - return strcast(defs, False) - return strcast(defs, False) - - -def ReturnMapping(cls): # noqa: N802 - # Annotate the method with a return Type - # so the value can be cast - def decorator(f): - @functools.wraps(f) - async def wrapper(*args, **kwargs): - nonlocal cls - reply = await f(*args, **kwargs) - if cls is None: - return reply - if "error" in reply: - cls = CLASSES["Error"] - if typing_inspect.is_generic_type(cls) and issubclass( - typing_inspect.get_origin(cls), Sequence - ): - parameters = typing_inspect.get_parameters(cls) - result = [] - item_cls = parameters[0] - for item in reply: - result.append(item_cls.from_json(item)) - """ - if 'error' in item: - cls = CLASSES['Error'] - else: - cls = item_cls - result.append(cls.from_json(item)) - """ - else: - result = cls.from_json(reply["response"]) - - return result - - return wrapper - - return decorator - - -def make_func(cls, name, description, params, result, _async=True): - indent = " " - args = Args(cls.schema, params) - toschema = args.py_to_schema_mapping() - assignments = [ - f"{indent}_params['{toschema[arg]}'] = {arg}" - for arg in args._get_arg_str(False, False) - ] - assignments = "\n".join(assignments) - res = retspec(cls.schema, result) - source = """ - -@ReturnMapping({rettype}) -{_async}def {name}(self{argsep}{args}): - ''' -{docstring} - Returns -> {res} - ''' -{validation} - # map input types to rpc msg - _params = dict() - msg = dict(type='{cls.name}', - request='{name}', - version={cls.version}, - params=_params) -{assignments} - reply = {_await}self.rpc(msg) - return reply - -""" - - if description != "": - description = f"{description}\n" - if args_doc := args.get_doc(): - args_doc = f"\n{args_doc}" - doc_string = f"{description}{args_doc}" - fsource = source.format( - _async="async " if _async else "", - name=name, - argsep=", " if args else "", - args=args.as_kwargs(), - res=res, - validation=args.as_validation(), - rettype=result.__name__ if result else None, - docstring=textwrap.indent(doc_string, indent), - cls=cls, - assignments=assignments, - _await="await " if _async else "", - ) - ns = _getns(cls.schema) - exec(fsource, ns) # noqa: S102 - func = ns[name] - return func, fsource - - -def make_rpc_func(cls): - source = """ - -async def rpc(self, msg): - ''' - Patch rpc method to add Id. - ''' - if not hasattr(self, 'Id'): - raise RuntimeError('Missing "Id" field') - msg['Id'] = id - - from .facade import TypeEncoder - reply = await self.connection.rpc(msg, encoder=TypeEncoder) - return reply - -""" - ns = _getns(cls.schema) - exec(source, ns) # noqa: S102 - func = ns["rpc"] - return func, source - - -def build_methods(cls, capture): - properties = cls.schema["properties"] - for methodname in sorted(properties): - method, source = _build_method(cls, methodname) - setattr(cls, methodname, method) - capture[f"{cls.__name__}Facade"].write(source, depth=1) - - -def _build_method(cls, name): - params = None - result = None - method = cls.schema["properties"][name] - description = "" - if "description" in method: - description = method["description"] - if "properties" in method: - prop = method["properties"] - spec = prop.get("Params") - if spec: - params = cls.schema.types.get(spec["$ref"]) - spec = prop.get("Result") - if spec: - if "$ref" in spec: - result = cls.schema.types.get(spec["$ref"]) - else: - result = SCHEMA_TO_PYTHON[spec["type"]] - return make_func(cls, name, description, params, result) - - -def build_watcher_methods(cls, capture): - properties = cls.schema["properties"] - if "Next" in properties and "Stop" in properties: - method, source = make_rpc_func(cls) - cls.rpc = method - capture[f"{cls.__name__}Facade"].write(source, depth=1) - - -def build_facade(schema): - cls = type( - schema.name, - (Type,), - dict(name=schema.name, version=schema.version, schema=schema), - ) - source = f""" -class {schema.name}Facade(Type): - name = '{schema.name}' - version = {schema.version} - """ - return cls, source - - -class TypeEncoder(json.JSONEncoder): - def default(self, obj: _RichJson) -> _Json: - if isinstance(obj, Type): - return obj.serialize() - return json.JSONEncoder.default(self, obj) - - -class Type: - def connect(self, connection): - self.connection = connection - - def __repr__(self): - return f"{self.__class__}({self.__dict__})" - - def __eq__(self, other): - if not isinstance(other, Type): - return NotImplemented - - return self.__dict__ == other.__dict__ - - async def rpc(self, msg: dict[str, _RichJson]) -> _Json: - result = await self.connection.rpc(msg, encoder=TypeEncoder) - return result - - @classmethod - def from_json(cls, data): - def _parse_nested_list_entry(expr, result_dict): - if isinstance(expr, str): - if ">" in expr or ">=" in expr: - # something like juju >= 2.9.31 - i = expr.index(">") - _key = expr[:i].strip() - _value = expr[i:].strip() - result_dict[_key] = _value - else: - # this is a simple entry - result_dict[expr] = "" - elif isinstance(expr, dict): - for v in expr.values(): - _parse_nested_list_entry(v, result_dict) - elif isinstance(expr, list): - for v in expr: - _parse_nested_list_entry(v, result_dict) - else: - raise TypeError( - f"Unexpected type of entry in assumes expression: {expr}" - ) - - if isinstance(data, cls): - return data - if isinstance(data, str): - try: - data = json.loads(data) - except json.JSONDecodeError: - raise - if isinstance(data, dict): - d = {} - for k, v in (data or {}).items(): - d[cls._toPy.get(k, k)] = v - try: - return cls(**d) - except TypeError: - raise - if isinstance(data, list): - # check: https://juju.is/docs/sdk/assumes - # assumes are in the form of a list - d = {} - _parse_nested_list_entry(data, d) - return cls(**d) - return None - - def serialize(self) -> dict[str, _Json]: - d = {} - for attr, tgt in self._toSchema.items(): - d[tgt] = getattr(self, attr) - return d - - def to_json(self) -> str: - return json.dumps(self.serialize(), cls=TypeEncoder, sort_keys=True) - - def __contains__(self, key): - return key in self._toPy - - # treat subscript gets as JSON representation - def __getitem__(self, key): - attr = self._toPy[key] - return getattr(self, attr) - - # treat subscript sets as JSON representation - def __setitem__(self, key, value): - attr = self._toPy[key] - setattr(self, attr, value) - - # legacy: generated definitions used to not correctly - # create typed objects and would use dict instead (from JSON) - # so we emulate some dict methods. - def get(self, key, default=None): - try: - attr = self._toPy[key] - except KeyError: - return default - return getattr(self, attr, default) - - -class Schema(dict): - def __init__(self, schema): - self.name = schema["Name"] - self.version = schema["Version"] - self.update(schema["Schema"]) - - self.registry = KindRegistry() - self.types = TypeRegistry(self) - - def reference_name(self, ref): - if ref.startswith("#/definitions/"): - ref = ref.rsplit("/", 1)[-1] - return ref - - def build_definitions(self): - # here we are building the types out - # anything in definitions is a type - # but these may contain references themselves - # so we dfs to the bottom and build upwards - # when a types is already in the registry - defs = self.get("definitions") - if not defs: - return - definitions = {} - for d, data in defs.items(): - if d in self.registry and d not in NAUGHTY_CLASSES: - continue - if data.get("type") != "object": - continue - definitions[d] = data - for d, definition in definitions.items(): - node = self.build_object(definition, d) - self.registry.register(d, self.version, node) - self.types.get_ref_type(d) - - def build_object(self, node, name=None): - # we don't need to build types recursively here - # they are all in definitions already - # we only want to include the type reference - # which we can derive from the name - struct = [] - add = struct.append - props = node.get("properties") - pprops = node.get("patternProperties") - if props: - # Sort these so the __init__ arg list for each Type remains - # consistently ordered across regens of client.py - for p in sorted(props): - prop = props[p] - if "$ref" in prop: - add((p, self.types.ref_type(prop))) - else: - kind = prop["type"] - if kind == "array": - add((p, self.build_array(prop))) - elif kind == "object": - struct.extend(self.build_object(prop, p)) - else: - add((p, self.types.obj_type(prop))) - if pprops: - if ".*" not in pprops: - raise ValueError( - "Cannot handle actual pattern in patternProperties %s" % pprops - ) - pprop = pprops[".*"] - if "$ref" in pprop: - add((name, Mapping[str, self.types.ref_type(pprop)])) - return struct - ppkind = pprop["type"] - if ppkind == "array": - add((name, Mapping[str, self.build_array(pprop)])) - else: - add((name, Mapping[str, SCHEMA_TO_PYTHON[ppkind]])) - - if not struct and node.get("additionalProperties", False): - add((name, SCHEMA_TO_PYTHON.get("object"))) - - return struct - - def build_array(self, obj): - # return a sequence from an array in the schema - if "$ref" in obj: - return Sequence[self.types.ref_type(obj)] - else: - kind = obj.get("type") - if kind and kind == "array": - items = obj["items"] - return self.build_array(items) - else: - return Sequence[self.types.obj_type(obj)] - - -def _getns(schema): - ns = {"Type": Type, "typing": typing, "ReturnMapping": ReturnMapping} - # Copy our types into the globals of the method - for facade in schema.registry: - ns[facade] = schema.registry.get_obj(facade) - return ns - - -def make_factory(name): - if name in factories: - del factories[name] - factories[name].write(f"class {name}(TypeFactory):\n pass\n\n") - - -def write_facades(captures, options): - """Write the Facades to the appropriate _client.py""" - for version in sorted(captures.keys()): - filename = f"{options.output_dir}/_client{version}.py" - with open(filename, "w") as f: - f.write(HEADER) - f.write("from juju.client.facade import Type, ReturnMapping\n") - f.write("from juju.client._definitions import *\n\n") - for key in sorted([k for k in captures[version] if "Facade" in k]): - print(captures[version][key], file=f) - - # Return the last (most recent) version for use in other routines. - return version - - -def write_definitions(captures, options): - """Write auxiliary (non versioned) classes to - _definitions.py The auxiliary classes currently get - written redudantly into each capture object, so we can look in - one of them -- we just use the last one from the loop above. - - """ - with open(f"{options.output_dir}/_definitions.py", "w") as f: - f.write(HEADER) - f.write("from juju.client.facade import Type, ReturnMapping\n\n") - for key in sorted([k for k in captures if "Facade" not in k]): - print(captures[key], file=f) - - -def write_client(captures, options): - """Write the TypeFactory classes to _client.py, along with some - imports and tables so that we can look up versioned Facades. - - """ - with open(f"{options.output_dir}/_client.py", "w") as f: - f.write(HEADER) - f.write("from juju.client._definitions import *\n\n") - clients = ", ".join(f"_client{v}" for v in captures) - - # from juju.client import _client2, _client1, _client3 ... - f.write("\nfrom juju.client import " + clients + "\n\n") - # CLIENTS = { .... - f.write( - CLIENT_TABLE.format( - clients=",\n ".join([f'"{v}": _client{v}' for v in captures]) - ) - ) - - f.write(LOOKUP_FACADE) - f.write(TYPE_FACTORY) - for key in sorted([k for k in factories if "Facade" in k]): - print(factories[key], file=f) - - -def generate_definitions(schemas): - # Build all of the auxiliary (unversioned) classes - # TODO: get rid of some of the excess trips through loops in the - # called functions. - definitions = codegen.Capture() - - for juju_version in sorted(schemas.keys()): - for schema in schemas[juju_version]: - schema.build_definitions() - - # ensure we write the latest ones first, so that earlier revisions - # get dropped. - for juju_version in sorted(schemas.keys(), reverse=True): - for schema in schemas[juju_version]: - build_types(schema, definitions) - - return definitions - - -def generate_facades( - schemas: dict[str, list[Schema]], -) -> dict[str, dict[int, codegen.Capture]]: - captures = defaultdict(codegen.Capture) - - # Build the Facade classes - for juju_version in sorted(schemas.keys(), key=packaging.version.parse): - for schema in schemas[juju_version]: - cls, source = build_facade(schema) - cls_name = f"{schema.name}Facade" - - captures[schema.version].clear(cls_name) - # Make the factory class for _client.py - make_factory(cls_name) - # Make the actual class - captures[schema.version][cls_name].write(source) - # Build the methods for each Facade class. - build_methods(cls, captures[schema.version]) - # Build the override RPC method if the Facade is a watcher. - build_watcher_methods(cls, captures[schema.version]) - # Mark this Facade class as being done for this version -- - # helps mitigate some excessive looping. - CLASSES[schema.name] = cls - - return captures - - -def load_schemas(options): - schemas = {} - for p in sorted(glob(options.schema)): - try: - juju_version = re.search(JUJU_VERSION, p).group() - except AttributeError: - print(f"Cannot extract a juju version from {p}") - print("Schemas must include a juju version in the filename") - raise SystemExit(1) - new_schemas = json.loads(Path(p).read_text("utf-8")) - schemas[juju_version] = [Schema(s) for s in new_schemas] - return schemas - - -def setup(): - parser = argparse.ArgumentParser() - parser.add_argument("-s", "--schema", default="juju/client/schemas-juju-*.json") - parser.add_argument("-o", "--output_dir", default="juju/client") - options = parser.parse_args() - return options - - -def main(): - options = setup() - - schemas = load_schemas(options) - - # Generate some text blobs - definitions = generate_definitions(schemas) - captures = generate_facades(schemas) - - # ... and write them out - write_definitions(definitions, options) - write_facades(captures, options) - write_client(captures, options) - - -if __name__ == "__main__": - main() diff --git a/build/lib/juju/client/facade_versions.py b/build/lib/juju/client/facade_versions.py deleted file mode 100644 index a4f7cf29..00000000 --- a/build/lib/juju/client/facade_versions.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright 2024 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -"""Constants for facade version negotiation.""" - -from typing import Dict, Sequence - -# Please keep in alphabetical order -# in future this will likely be generated automatically (perhaps at runtime) -client_facade_versions = { - "Action": (7,), - "Admin": (3,), - "AllModelWatcher": (4,), - "AllWatcher": (3,), - "Annotations": (2,), - "Application": (17, 19, 20), - "ApplicationOffers": (4, 5), - "Backups": (3,), - "Block": (2,), - "Bundle": (6,), - "Charms": (6,), - "Client": (6, 7, 8), - "Cloud": (7,), - "Controller": (11, 12), - "CredentialManager": (1,), - "FirewallRules": (1,), - "HighAvailability": (2,), - "ImageMetadataManager": (1,), - "KeyManager": (1,), - "MachineManager": (10,), - "MetricsDebug": (2,), - "ModelConfig": (3,), - "ModelGeneration": (4,), - "ModelManager": (9, 10), - "ModelUpgrader": (1,), - "Payloads": (1,), - "Pinger": (1,), - "Resources": (3,), - "SSHClient": (4,), - "SecretBackends": (1,), - "Secrets": (1, 2), - "Spaces": (6,), - "Storage": (6,), - "Subnets": (5,), - "UserManager": (3,), -} - -# Manual list of facades present in schemas + codegen which python-libjuju does not yet support -excluded_facade_versions: Dict[str, Sequence[int]] = {"Charms": (7,)} - - -# We don't generate code for these, as we can never use them. -# The controller happily lists them though, don't warn about these. -known_unsupported_facades = ( - "ActionPruner", - "Agent", - "AgentLifeFlag", - "AgentTools", - "ApplicationScaler", - "CAASAdmission", - "CAASAgent", - "CAASApplication", - "CAASApplicationProvisioner", - "CAASFirewaller", - "CAASFirewallerSidecar", - "CAASModelConfigManager", - "CAASModelOperator", - "CAASOperator", - "CAASOperatorProvisioner", - "CAASOperatorUpgrader", - "CAASUnitProvisioner", - "CharmDownloader", - "CharmRevisionUpdater", - "Cleaner", - "CredentialValidator", - "CrossController", - "CrossModelRelations", - "CrossModelSecrets", - "Deployer", - "DiskManager", - "EntityWatcher", - "EnvironUpgrader", - "ExternalControllerUpdater", - "FanConfigurer", - "FilesystemAttachmentsWatcher", - "Firewaller", - "HostKeyReporter", - "ImageMetadata", - "InstanceMutater", - "InstancePoller", - "KeyUpdater", - "LeadershipService", - "LifeFlag", - "LogForwarding", - "Logger", - "MachineActions", - "MachineUndertaker", - "Machiner", - "MeterStatus", - "MetricsAdder", - "MetricsManager", - "MigrationFlag", - "MigrationMaster", - "MigrationMinion", - "MigrationStatusWatcher", - "MigrationTarget", - "ModelSummaryWatcher", - "NotifyWatcher", - "OfferStatusWatcher", - "PayloadsHookContext", - "Provisioner", - "ProxyUpdater", - "Reboot", - "RelationStatusWatcher", - "RelationUnitsWatcher", - "RemoteRelationWatcher", - "RemoteRelations", - "ResourcesHookContext", - "RetryStrategy", - "SecretBackendsManager", - "SecretBackendsRotateWatcher", - "SecretsDrain", - "SecretsManager", - "SecretsRevisionWatcher", - "SecretsTriggerWatcher", - "Singular", - "StatusHistory", - "StorageProvisioner", - "StringsWatcher", - "Undertaker", - "UnitAssigner", - "Uniter", - "UpgradeSeries", - "UpgradeSteps", - "Upgrader", - "UserSecretsDrain", - "UserSecretsManager", - "VolumeAttachmentPlansWatcher", - "VolumeAttachmentsWatcher", -) diff --git a/build/lib/juju/client/flags.py b/build/lib/juju/client/flags.py deleted file mode 100644 index fc678452..00000000 --- a/build/lib/juju/client/flags.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import os - -PYLIBJUJU_DEV_FEATURE_FLAG = "PYLIBJUJU_DEV_FEATURE_FLAGS" - - -DEFAULT_VALUES_FLAG = "default_values" - - -def feature_enabled(name): - flags = os.environ.get(PYLIBJUJU_DEV_FEATURE_FLAG) - if flags is not None: - parts = [s.strip() for s in flags.split(",")] - return name in parts - return False diff --git a/build/lib/juju/client/gocookies.py b/build/lib/juju/client/gocookies.py deleted file mode 100644 index 0c33f0be..00000000 --- a/build/lib/juju/client/gocookies.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import datetime -import http.cookiejar as cookiejar -import json -import time - -import pyrfc3339 - - -class GoCookieJar(cookiejar.FileCookieJar): - """A CookieJar implementation that reads and writes cookies - to the cookiejar format as understood by the Go package - github.com/juju/persistent-cookiejar. - """ - - def _really_load(self, f, filename, ignore_discard, ignore_expires): - """Implement the _really_load method called by FileCookieJar - to implement the actual cookie loading - """ - data = json.load(f) or [] - now = time.time() - for cookie in map(go_to_py_cookie, data): - if not ignore_expires and cookie.is_expired(now): - continue - self.set_cookie(cookie) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - """Implement the FileCookieJar abstract method.""" - if filename is None: - if self.filename is not None: - filename = self.filename - else: - raise ValueError(cookiejar.MISSING_FILENAME_TEXT) - - # TODO: obtain file lock, read contents of file, and merge with - # current content. - go_cookies = [] - now = time.time() - for cookie in self: - if not ignore_discard and cookie.discard: - continue - if not ignore_expires and cookie.is_expired(now): - continue - go_cookies.append(py_to_go_cookie(cookie)) - with open(filename, "w") as f: - f.write(json.dumps(go_cookies)) - - -def go_to_py_cookie(go_cookie): - """Convert a Go-style JSON-unmarshaled cookie into a Python cookie""" - expires = None - if go_cookie.get("Expires") is not None: - t = pyrfc3339.parse(go_cookie["Expires"]) - expires = t.timestamp() - return cookiejar.Cookie( - version=0, - name=go_cookie["Name"], - value=go_cookie["Value"], - port=None, - port_specified=False, - # Unfortunately Python cookies don't record the original - # host that the cookie came from, so we'll just use Domain - # for that purpose, and record that the domain was specified, - # even though it probably was not. This means that - # we won't correctly record the CanonicalHost entry - # when writing the cookie file after reading it. - domain=go_cookie["Domain"], - domain_specified=not go_cookie["HostOnly"], - domain_initial_dot=False, - path=go_cookie["Path"], - path_specified=True, - secure=go_cookie["Secure"], - expires=expires, - discard=False, - comment=None, - comment_url=None, - rest=None, - rfc2109=False, - ) - - -def py_to_go_cookie(py_cookie): - """Convert a python cookie to the JSON-marshalable Go-style cookie form.""" - # TODO (perhaps): - # HttpOnly - # Creation - # LastAccess - # Updated - # not done properly: CanonicalHost. - go_cookie = { - "Name": py_cookie.name, - "Value": py_cookie.value, - "Domain": py_cookie.domain, - "HostOnly": not py_cookie.domain_specified, - "Persistent": not py_cookie.discard, - "Secure": py_cookie.secure, - "CanonicalHost": py_cookie.domain, - } - if py_cookie.path_specified: - go_cookie["Path"] = py_cookie.path - if py_cookie.expires is not None: - unix_time = datetime.datetime.fromtimestamp(py_cookie.expires) - # Note: fromtimestamp bizarrely produces a time without - # a time zone, so we need to use accept_naive. - go_cookie["Expires"] = pyrfc3339.generate(unix_time, accept_naive=True) - return go_cookie diff --git a/build/lib/juju/client/jujudata.py b/build/lib/juju/client/jujudata.py deleted file mode 100644 index 5742985b..00000000 --- a/build/lib/juju/client/jujudata.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import abc -import os -import pathlib - -import yaml - -from juju import tag -from juju.client import client as jujuclient -from juju.client.gocookies import GoCookieJar -from juju.errors import ( - JujuControllerNotFoundError, - JujuError, - PylibjujuProgrammingError, -) -from juju.utils import juju_config_dir - -API_ENDPOINTS_KEY = "api-endpoints" - - -class NoModelException(Exception): - pass - - -class JujuData: - __metaclass__ = abc.ABCMeta - - def parse_model(self, model): - """Split the given model_name into controller and model parts. - If the controller part is empty, the current controller will be used. - If the model part is empty, the current model will be used for - the controller. - The returned model name will always be qualified with a username. - :param model str: The model name to parse. - :return (str, str): The controller and model names. - """ - # TODO if model is empty, use $JUJU_MODEL environment variable. - if model and ":" in model: - # explicit controller given - controller_name, model_name = model.split(":") - else: - # use the current controller if one isn't explicitly given - controller_name = self.current_controller() - model_name = model - if not controller_name: - controller_name = self.current_controller() - if not model_name: - model_name = self.current_model(controller_name, model_only=True) - if not model_name: - raise NoModelException("no current model") - - if "/" not in model_name: - # model name doesn't include a user prefix, so add one - # by using the current user for the controller. - accounts = self.accounts().get(controller_name) - if accounts is None: - raise JujuError(f"No account found for controller {controller_name} ") - username = accounts.get("user") - if username is None: - raise JujuError(f"No username found for controller {controller_name}") - model_name = username + "/" + model_name - - return controller_name, model_name - - -class FileJujuData(JujuData): - """Provide access to the Juju client configuration files. - Any configuration file is read once and then cached. - """ - - def __init__(self): - self.path = juju_config_dir() - # _loaded keeps track of the loaded YAML from - # the Juju data files so we don't need to load the same - # file many times. - self._loaded = {} - - def refresh(self): - """Forget the cache of configuration file data""" - self._loaded = {} - - def current_controller(self): - """Return the current controller name""" - try: - return self._load_yaml("controllers.yaml", "current-controller") - except FileNotFoundError: - raise JujuControllerNotFoundError( - "No controllers.yaml file found. python-libjuju requires a bootstrapped Juju controller." - ) - - def current_model(self, controller_name=None, model_only=False): - """Return the current model, qualified by its controller name. - If controller_name is specified, the current model for - that controller will be returned. - - If model_only is true, only the model name, not qualified by - its controller name, will be returned. - """ - # TODO respect JUJU_MODEL environment variable. - if not controller_name: - controller_name = self.current_controller() - if not controller_name: - raise JujuError("No current controller") - models = self.models()[controller_name] - if "current-model" not in models: - return None - if model_only: - return models["current-model"] - return controller_name + ":" + models["current-model"] - - def load_credential(self, cloud, name=None): - """Load a local credential. - - :param str cloud: Name of cloud to load credentials from. - :param str name: Name of credential. If None, the default credential - will be used, if available. - :return: A CloudCredential instance, or None. - """ - try: - cloud = tag.untag("cloud-", cloud) - creds_data = self.credentials()[cloud] - if not name: - default_credential = creds_data.pop("default-credential", None) - default_region = creds_data.pop("default-region", None) # noqa - if default_credential: - name = creds_data["default-credential"] - elif len(creds_data) == 1: - name = next(iter(creds_data)) - else: - return None, None - cred_data = creds_data[name] - auth_type = cred_data.pop("auth-type") - return name, jujuclient.CloudCredential( - auth_type=auth_type, - attrs=cred_data, - ) - except (KeyError, FileNotFoundError): - return None, None - - def controller_name_by_endpoint(self, endpoint): - """Finds the controller that has the given endpoints, returns the name. - - :param str endpoint: The endpoint of the controller we're looking for - """ - for controller_name, controller in self.controllers().items(): - if isinstance(endpoint, str): - if endpoint in controller[API_ENDPOINTS_KEY]: - return controller_name - elif isinstance(endpoint, list): - for e in endpoint: - if e in controller[API_ENDPOINTS_KEY]: - return controller_name - else: - raise PylibjujuProgrammingError() - raise JujuError(f"Unable to find controller with endpoint {endpoint}") - - def controllers(self): - return self._load_yaml("controllers.yaml", "controllers") - - def models(self): - return self._load_yaml("models.yaml", "controllers") - - def accounts(self): - return self._load_yaml("accounts.yaml", "controllers") - - def credentials(self): - return self._load_yaml("credentials.yaml", "credentials") - - def _load_yaml(self, filename, key): - if filename in self._loaded: - # Data already exists in the cache. - return self._loaded[filename].get(key) - # TODO use the file lock like Juju does. - filepath = os.path.join(self.path, filename) - with open(filepath) as f: - data = yaml.safe_load(f) - self._loaded[filename] = data - return data.get(key) - - def cookies_for_controller(self, controller_name): - f = pathlib.Path(self.path) / "cookies" / (controller_name + ".json") - if not f.exists(): - f = pathlib.Path("~/.go-cookies").expanduser() - # TODO if neither cookie file exists, where should - # we create the cookies? - jar = GoCookieJar(str(f)) - jar.load() - return jar diff --git a/build/lib/juju/client/overrides.py b/build/lib/juju/client/overrides.py deleted file mode 100644 index 1b569255..00000000 --- a/build/lib/juju/client/overrides.py +++ /dev/null @@ -1,418 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -import re -from typing import Any, NamedTuple - -from . import _client, _definitions -from .facade import ReturnMapping, Type, TypeEncoder - -__all__ = [ - "Binary", - "ConfigValue", - "Delta", - "Number", - "Resource", -] - -__patches__ = [ - "ResourcesFacade", - "AllWatcherFacade", - "ActionFacade", -] - - -class _Change(NamedTuple): - entity: str - type: str - data: dict[str, Any] - - -class Delta(Type): - """A single websocket delta. - - :ivar entity: The entity name, e.g. 'unit', 'application' - :vartype entity: str - - :ivar type: The delta type, e.g. 'add', 'change', 'remove' - :vartype type: str - - :ivar data: The raw delta data - :vartype data: dict - - NOTE: The 'data' variable above is being incorrectly cross-linked by a - Sphinx bug: https://github.com/sphinx-doc/sphinx/issues/2549 - - """ - - _toSchema = {"deltas": "deltas"} - _toPy = {"deltas": "deltas"} - - def __init__(self, deltas: tuple[str, str, dict[str, Any]]): - """:param deltas: [str, str, object]""" - self.deltas = deltas - - change = _Change(*self.deltas) - - self.entity = change.entity - self.type = change.type - self.data = change.data - - @classmethod - def from_json(cls, data): - return cls(deltas=data) - - -class ResourcesFacade(Type): - """Patch parts of ResourcesFacade to make it work.""" - - # FIXME: a facade method from codegen can be used instead - @ReturnMapping(_client.AddPendingResourcesResult) - async def AddPendingResources( # noqa: N802 - self, application_tag="", charm_url="", charm_origin=None, resources=None - ): - """Fix the calling signature of AddPendingResources. - - The ResourcesFacade doesn't conform to the standard facade pattern in - the Juju source, which leads to the schemagened code not matching up - properly with the actual calling convention in the API. There is work - planned to fix this in Juju, but we have to work around it for now. - - application_tag : str - charm_url : str - resources : typing.Sequence<+T_co>[~CharmResource]<~CharmResource> - Returns -> typing.Union[_ForwardRef('ErrorResult'), - typing.Sequence<+T_co>[str]] - """ - version = _client.ResourcesFacade.best_facade_version(self.connection) - # map input types to rpc msg - _params = dict() - msg = dict( - type="Resources", - request="AddPendingResources", - version=version, - params=_params, - ) - _params["tag"] = application_tag - _params["url"] = charm_url - _params["resources"] = resources - _params["charm-origin"] = charm_origin - reply = await self.rpc(msg) - return reply - - -class AllWatcherFacade(Type): - """Patch rpc method of allwatcher to add in 'id' stuff.""" - - async def rpc(self, msg): - if not hasattr(self, "Id"): - client = _client.ClientFacade.from_connection(self.connection) - - result = await client.WatchAll() - self.Id = result.watcher_id - - msg["Id"] = self.Id - result = await self.connection.rpc(msg, encoder=TypeEncoder) - return result - - -class ActionFacade(Type): - class _FindTagsResults(Type): - _toSchema = {"matches": "matches"} - _toPy = {"matches": "matches"} - - def __init__(self, matches=None, **unknown_fields): - """FindTagsResults wraps the mapping between the requested prefix and the - matching tags for each requested prefix. - - Matches map[string][]Entity `json:"matches"` - """ - self.matches = {} - matches = matches or {} - for prefix, tags in matches.items(): - self.matches[prefix] = [_definitions.Entity.from_json(r) for r in tags] - - # FIXME: This seems internal, can be renamed - @ReturnMapping(_FindTagsResults) - async def FindActionTagsByPrefix(self, prefixes): # noqa: N802 - """Prefixes : typing.Sequence[str] - Returns -> typing.Sequence[~Entity] - """ - # map input types to rpc msg - _params = dict() - msg = dict( - type="Action", request="FindActionTagsByPrefix", version=2, params=_params - ) - _params["prefixes"] = prefixes - reply = await self.rpc(msg) - return reply - - -class Number(_definitions.Number): - """Represent a semver string. - - Because it is not standard JSON, the typical from_json parsing fails and - the parsing must be handled specially. - - See https://github.com/juju/version for more info. - """ - - numberPat = re.compile( - r"^(\d{1,9})\.(\d{1,9})(?:\.|-([a-z]+))(\d{1,9})(\.\d{1,9})?$" - ) - - def __init__( - self, major=None, minor=None, patch=None, tag=None, build=None, **unknown_fields - ): - """Major : int - minor : int - patch : int - tag : str - build : int - """ - self.major = int(major or "0") - self.minor = int(minor or "0") - self.patch = int(patch or "0") - self.tag = tag or "" - self.build = int(build or "0") - - def __repr__(self): - return f"" - - def __str__(self): - return self.serialize() - - @property - def _cmp(self): - return (self.major, self.minor, self.tag, self.patch, self.build) - - def __eq__(self, other): - return isinstance(other, type(self)) and self._cmp == other._cmp - - def __lt__(self, other): - return self._cmp < other._cmp - - def __le__(self, other): - return self._cmp <= other._cmp - - def __gt__(self, other): - return self._cmp > other._cmp - - def __ge__(self, other): - return self._cmp >= other._cmp - - @classmethod - def from_json(cls, data): - parsed = None - if isinstance(data, cls): - return data - elif data is None: - return cls() - elif isinstance(data, dict): - parsed = data - elif isinstance(data, str): - match = cls.numberPat.match(data) - if match: - parsed = { - "major": match.group(1), - "minor": match.group(2), - "tag": match.group(3), - "patch": match.group(4), - "build": (match.group(5)[1:] if match.group(5) else 0), - } - if not parsed: - raise TypeError(f"Unable to parse Number version string: {data}") - d = {} - for k, v in parsed.items(): - d[cls._toPy.get(k, k)] = v - - return cls(**d) - - def serialize(self): - s = "" - if not self.tag: - s = f"{self.major}.{self.minor}.{self.patch}" - else: - s = f"{self.major}.{self.minor}-{self.tag}{self.patch}" - if self.build: - s = f"{s}.{self.build}" - return s - - def to_json(self): - return self.serialize() - - -class Binary(_definitions.Binary): - """Represent a semver string with additional series and arch info. - - Because it is not standard JSON, the typical from_json parsing fails and - the parsing must be handled specially. - - See https://github.com/juju/version for more info. - """ - - binaryPat = re.compile( - r"^(\d{1,9})\.(\d{1,9})(?:\.|-([a-z]+))(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$" - ) - - def __init__(self, number=None, series=None, arch=None, **unknown_fields): - """Number : Number - series : str - arch : str - """ - self.number = Number.from_json(number) - self.series = series - self.arch = arch - - def __repr__(self): - return f"" - - def __str__(self): - return self.serialize() - - def __eq__(self, other): - return ( - isinstance(other, type(self)) - and other.number == self.number - and other.series == self.series - and other.arch == self.arch - ) - - @classmethod - def from_json(cls, data): - parsed = None - if isinstance(data, cls): - return data - elif data is None: - return cls() - elif isinstance(data, dict): - parsed = data - elif isinstance(data, str): - match = cls.binaryPat.match(data) - if match: - parsed = { - "number": { - "major": match.group(1), - "minor": match.group(2), - "tag": match.group(3), - "patch": match.group(4), - "build": (match.group(5)[1:] if match.group(5) else 0), - }, - "series": match.group(6), - "arch": match.group(7), - } - if parsed is None: - raise TypeError(f"Unable to parse Binary version string: {data}") - d = {} - for k, v in parsed.items(): - d[cls._toPy.get(k, k)] = v - - return cls(**d) - - def serialize(self): - return f"{self.number.serialize()}-{self.series}-{self.arch}" - - def to_json(self): - return self.serialize() - - -class ConfigValue(_definitions.ConfigValue): - def __repr__(self): - return f"<{type(self).__name__} source={self.source!r} value={self.value!r}>" - - -class Resource(Type): - _toSchema = { - "application": "application", - "charmresource": "CharmResource", - "id_": "id", - "pending_id": "pending-id", - "timestamp": "timestamp", - "username": "username", - "name": "name", - "origin": "origin", - } - _toPy = { - "CharmResource": "charmresource", - "application": "application", - "id": "id_", - "pending-id": "pending_id", - "timestamp": "timestamp", - "username": "username", - "name": "name", - "origin": "origin", - } - - def __init__( - self, - charmresource=None, - application=None, - id_=None, - pending_id=None, - timestamp=None, - username=None, - name=None, - origin=None, - **unknown_fields, - ): - """Charmresource : CharmResource - application : str - id_ : str - pending_id : str - timestamp : str - username : str - name: str - origin : str - """ - if charmresource: - self.charmresource = _client.CharmResource.from_json(charmresource) - else: - self.charmresource = None - self.application = application - self.id_ = id_ - self.pending_id = pending_id - self.timestamp = timestamp - self.username = username - self.name = name - self.origin = origin - self.unknown_fields = unknown_fields - - -class Macaroon(Type): - _toSchema = { - "signature": "signature", - "caveats": "caveats", - "location": "location", - "identifier": "identifier", - } - _toPy = { - "signature": "signature", - "caveats": "caveats", - "location": "location", - "identifier": "identifier", - } - - def __init__( - self, signature="", caveats=None, location=None, identifier="", **unknown_fields - ): - """Signature : str - caveats : typing.Sequence<+T_co>[~RemoteSpace]<~RemoteSpace> - location : str - identifier : str - """ - self.signature = signature - self.caveats = caveats - self.location = location - self.identifier = identifier - self.unknown_fields = unknown_fields - - -class Caveat(Type): - _toSchema = {"cid": "cid"} - _toPy = {"cid": "cid"} - - def __init__(self, cid="", **unknown_fields): - """Cid : str""" - self.cid = cid - self.unknown_fields = unknown_fields diff --git a/build/lib/juju/client/proxy/__init__.py b/build/lib/juju/client/proxy/__init__.py deleted file mode 100644 index f59d24d5..00000000 --- a/build/lib/juju/client/proxy/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright 2024 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -"""Juju client proxy.""" diff --git a/build/lib/juju/client/proxy/factory.py b/build/lib/juju/client/proxy/factory.py deleted file mode 100644 index 24f76081..00000000 --- a/build/lib/juju/client/proxy/factory.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -from juju.client.proxy.kubernetes.proxy import KubernetesProxy - - -def proxy_from_config(conf): - if conf is None: - return None - - if "type" not in conf: - return None - - proxy_type = conf["type"] - if proxy_type != "kubernetes-port-forward": - raise ValueError("unknown proxy type %s" % proxy_type) - - return _construct_kube_proxy(conf["config"]) - - -def _construct_kube_proxy(config): - return KubernetesProxy( - config.get("api-host", ""), - config.get("namespace", ""), - config.get("remote-port", ""), - config.get("service", ""), - config.get("service-account-token", ""), - config.get("ca-cert", None), - ) diff --git a/build/lib/juju/client/proxy/kubernetes/__init__.py b/build/lib/juju/client/proxy/kubernetes/__init__.py deleted file mode 100644 index 0b3faa2d..00000000 --- a/build/lib/juju/client/proxy/kubernetes/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright 2024 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -"""Juju client proxy for k8s.""" diff --git a/build/lib/juju/client/proxy/kubernetes/proxy.py b/build/lib/juju/client/proxy/kubernetes/proxy.py deleted file mode 100644 index 3d4153b1..00000000 --- a/build/lib/juju/client/proxy/kubernetes/proxy.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -import logging -import tempfile - -from kubernetes import client -from kubernetes.stream import portforward - -from juju.client.proxy.proxy import Proxy, ProxyNotConnectedError - -log = logging.getLogger("juju.client.connection") - - -class KubernetesProxy(Proxy): - def __init__( - self, - api_host, - namespace, - remote_port, - service, - service_account_token, - ca_cert=None, - ): - config = client.Configuration() - config.host = api_host - config.ssl_ca_cert = ca_cert - config.api_key = {"authorization": "Bearer " + service_account_token} - - self.namespace = namespace - self.remote_port = remote_port - self.service = service - - try: - self.remote_port = int(remote_port) - except ValueError: - raise ValueError(f"Invalid port number: {remote_port}") - - self.port_forwarder = None - - if ca_cert: - self.temp_ca_file = tempfile.NamedTemporaryFile() # noqa: SIM115 - self.temp_ca_file.write(bytes(ca_cert, "utf-8")) - self.temp_ca_file.flush() - config.ssl_ca_cert = self.temp_ca_file.name - else: - self.temp_ca_file = None - - self.api_client = client.ApiClient(config) - - def connect(self): - corev1 = client.CoreV1Api(self.api_client) - service = corev1.read_namespaced_service(self.service, self.namespace) - - label_selector = ",".join(k + "=" + v for k, v in service.spec.selector.items()) - - pods = corev1.list_namespaced_pod( - namespace=self.namespace, - label_selector=label_selector, - ) - - self.port_forwarder = portforward( - corev1.connect_get_namespaced_pod_portforward, - pods.items[0].metadata.name, - self.namespace, - ports=str(self.remote_port), - ) - - def __del__(self): - self.close() - - def close(self): - try: - if self.port_forwarder: - self.port_forwarder.close() - if self.temp_ca_file: - self.temp_ca_file.close() - except AttributeError: - pass - - def socket(self): - if self.port_forwarder is not None: - return self.port_forwarder.socket(self.remote_port)._socket - raise ProxyNotConnectedError() diff --git a/build/lib/juju/client/proxy/proxy.py b/build/lib/juju/client/proxy/proxy.py deleted file mode 100644 index 3a521e10..00000000 --- a/build/lib/juju/client/proxy/proxy.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -from abc import abstractmethod - -from juju.errors import AbstractMethodError - - -class ProxyNotConnectedError(Exception): - pass - - -class Proxy: - """Abstract class to represent a generic controller connection proxy""" - - @abstractmethod - def connect(self): - raise AbstractMethodError() - - @abstractmethod - def close(self): - raise AbstractMethodError() - - @abstractmethod - def socket(self): - raise AbstractMethodError() diff --git a/build/lib/juju/client/runner.py b/build/lib/juju/client/runner.py deleted file mode 100644 index 87518b2c..00000000 --- a/build/lib/juju/client/runner.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - - -class AsyncRunner: - async def __call__(self, facade_method, *args, **kwargs): - await self.connection.rpc(facade_method(*args, **kwargs)) - - -class ThreadedRunner: - pass - - -# Methods are descriptors?? -# get is called with params -# set gets called with the result? -# This could let us fake the protocol we want -# while decoupling the protocol from the RPC and the IO/Process context - -# The problem is leaking the runtime impl details to the top levels of the API -# with async def By handling the Marshal/Unmarshal side of RPC as a protocol we -# can leave the RPC running to a specific delegate without altering the method -# signatures. This still isn't quite right though as async is co-op -# multitasking and the methods still need to know not to block or they will -# pause other execution diff --git a/build/lib/juju/constraints.py b/build/lib/juju/constraints.py deleted file mode 100644 index 246bd4c7..00000000 --- a/build/lib/juju/constraints.py +++ /dev/null @@ -1,254 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -# -# Module that parses constraints -# -# The current version of juju core expects the client to take -# constraints given in the form "mem=10G foo=bar" and parse them into -# json that looks like {"mem": 10240, "foo": "bar"}. This module helps us -# accomplish that task. -# -# We do not attempt to duplicate the checking done in -# client/_client.py:Value here. That class will verify that the -# constraints keys are valid, and that we can successfully dump the -# constraints dict to json. -# -# Once https://bugs.launchpad.net/juju/+bug/1645402 is addressed, this -# module should be deprecated. -# - -import re -from typing import Dict, List, Mapping, Optional, TypedDict, Union - -from typing_extensions import NotRequired, Required - -# Matches on a string specifying memory size -MEM = re.compile("^[1-9][0-9]*[MGTP]$") - -# Multiplication factors to get Megabytes -# https://github.com/juju/juju/blob/master/constraints/constraints.go#L666 -FACTORS = { - "M": 1024**0, - "G": 1024**1, - "T": 1024**2, - "P": 1024**3, - "E": 1024**4, - "Z": 1024**5, - "Y": 1024**6, -} - -# List of supported constraint keys, see -# http://github.com/cderici/juju/blob/2.9/core/constraints/constraints.go#L20-L39 -SUPPORTED_KEYS = [ - "arch", - "container", - "cpu_cores", - "cores", - "cpu_power", - "mem", - "root_disk", - "root_disk_source", - "tags", - "instance_role", - "instance_type", - "spaces", - "virt_type", - "zones", - "allocate_public_ip", -] - -LIST_KEYS = {"tags", "spaces", "zones"} - -SNAKE1 = re.compile(r"(.)([A-Z][a-z]+)") -SNAKE2 = re.compile("([a-z0-9])([A-Z])") - - -ParsedValue = Union[int, bool, str] - - -class ConstraintsDict(TypedDict, total=False): - allocate_public_ip: ParsedValue - arch: ParsedValue - container: ParsedValue - cores: ParsedValue - cpu_cores: ParsedValue - cpu_power: ParsedValue - instance_role: ParsedValue - instance_type: ParsedValue - mem: ParsedValue - root_disk: ParsedValue - root_dist_source: ParsedValue - spaces: List[ParsedValue] - tags: List[ParsedValue] - virt_type: ParsedValue - zones: List[ParsedValue] - - -def parse(constraints: Union[str, ConstraintsDict]) -> Optional[ConstraintsDict]: - """Constraints must be expressed as a string containing only spaces - and key value pairs joined by an '='. - - """ - if not constraints: - return None - - if isinstance(constraints, dict): - # Forwards compatibility: already parsed - return constraints - - normalized_constraints: ConstraintsDict = {} - for s in constraints.split(" "): - if "=" not in s: - raise ValueError("malformed constraint %s" % s) - - k, v = s.split("=") - normalized_constraints[normalize_key(k)] = ( - normalize_list_value(v) if k in LIST_KEYS else normalize_value(v) - ) - - return normalized_constraints - - -def normalize_key(orig_key: str) -> str: - key = orig_key.strip() - - key = key.replace("-", "_") # Our _client lib wants "_" in place of "-" - - # Convert camelCase to snake_case - key = SNAKE1.sub(r"\1_\2", key) - key = SNAKE2.sub(r"\1_\2", key).lower() - - if key not in SUPPORTED_KEYS: - raise ValueError("unknown constraint in %s" % orig_key) - return key - - -def normalize_value(value: str) -> Union[int, bool, str]: - value = value.strip() - - if MEM.match(value): - # Translate aliases to Megabytes. e.g. 1G = 10240 - return int(value[:-1]) * FACTORS[value[-1:]] - - if value.isdigit(): - return int(value) - - if value.lower() == "true": - return True - if value.lower() == "false": - return False - - return value - - -def normalize_list_value(value: str) -> List[ParsedValue]: - values = value.strip().split(",") - return [normalize_value(value) for value in values] - - -STORAGE = re.compile( - # original regex: - # '(?:(?:^|(?<=,))(?:|(?P[a-zA-Z]+[-?a-zA-Z0-9]*)|(?P-?[0-9]+)|(?:(?P-?[0-9]+(?:\\.[0-9]+)?)(?P[MGTPEZY])(?:i?B)?))(?:$|,))' - # with formatting and explanation -- note that this regex is used with re.finditer: - "(?:" - "(?:^|(?<=,))" # start of string or previous match ends with ',' - "(?:" # match one of the following: - "|(?P[a-zA-Z]+[-?a-zA-Z0-9]*)" # * pool: a sequence starting with a letter, ending with a letter or number, - # ------- and including letters, numbers and hyphens (no more than one in a row) - "|(?P-?[0-9]+)" # * count: an optional minus sign followed by one or more digits - "|(?:" # * size (number) and size_exp (units): - "(?P-?[0-9]+(?:\\.[0-9]+)?)" # -- * an optional minus sign followed by one or more digits, optionally with decimal point and more digits - "(?P[MGTPEZY])(?:i?B)?)" # -- * one of MGTPEZY, optionally followed by iB or B, for example 1M or 2.0MB or -3.3MiB - ")" - "(?:$|,)" # end of string or ',' - ")" -) - - -class StorageConstraintDict(TypedDict): - count: Required[int] # >= 1 - pool: NotRequired[str] - size: NotRequired[int] - - -def parse_storage_constraint(constraint: str) -> StorageConstraintDict: - storage: StorageConstraintDict = {"count": 1} - for m in STORAGE.finditer(constraint): - pool = m.group("pool") - if pool: - if "pool" in storage: - raise ValueError("pool already specified") - storage["pool"] = pool - count = m.group("count") - if count: - count = int(count) - storage["count"] = count if count > 0 else 1 - size = m.group("size") - if size: - storage["size"] = int(float(size) * FACTORS[m.group("size_exp")]) - return storage - - -def parse_storage_constraints( - constraints: Optional[Mapping[str, Union[str, StorageConstraintDict]]] = None, -) -> Dict[str, StorageConstraintDict]: - if constraints is None: - return {} - parsed: dict[str, StorageConstraintDict] = {} - for label, storage_constraint in constraints.items(): - if isinstance(storage_constraint, str): - parsed[label] = parse_storage_constraint(storage_constraint) - elif isinstance(storage_constraint, dict): # pyright: ignore[reportUnnecessaryIsInstance] - parsed[label] = storage_constraint - else: - raise ValueError( - f"Unexpected constraint {storage_constraint!r}" - f" for label {label!r} in {constraints}" - ) - return parsed - - -DEVICE = re.compile( - # original regex: - # '^(?P[0-9]+)?(?:^|,)(?P[^,]+)(?:$|,(?!$))(?P(?:[^=]+=[^;]+)+)*$' - # with formatting and explanation -- note this regex is used with re.match: - "^" # start of string - "(?P[0-9]+)?" # count is 1+ digits, and is optional - "(?:^|,)" # match start of string or a comma - # -- so type can be at the start or comma separated from count - "(?P[^,]+)" # type is 1+ anything not a comma (including digits), and is required - "(?:$|,(?!$))" # match end of string | or a non-trailing comma - # -- so type can be at the end or followed by attrs - "(?P(?:[^=]+=[^;]+)+)*" # attrs is any number of semicolon separated key=value items - # -- value can have spare '=' inside, possible not intended - # -- attrs will be matched with ATTR.finditer afterwards in parse_device_constraint - "$" # end of string -) -ATTR = re.compile(";?(?P[^=]+)=(?P[^;]+)") - - -class DeviceConstraintDict(TypedDict): - count: Required[int] - type: Required[str] - attributes: NotRequired[Dict[str, str]] - - -def parse_device_constraint(constraint: str) -> DeviceConstraintDict: - m = DEVICE.match(constraint) - if m is None: - raise ValueError("device constraint does not match") - device: DeviceConstraintDict = {} - count = m.group("count") - if count: - count = int(count) - device["count"] = count if count > 0 else 1 - else: - device["count"] = 1 - device["type"] = m.group("type") - attrs = m.group("attrs") - if attrs: - device["attributes"] = { - match.group("key"): match.group("value") for match in ATTR.finditer(attrs) - } - return device diff --git a/build/lib/juju/controller.py b/build/lib/juju/controller.py deleted file mode 100644 index c5b077da..00000000 --- a/build/lib/juju/controller.py +++ /dev/null @@ -1,1009 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import json -import logging -from concurrent.futures import CancelledError -from pathlib import Path - -import websockets - -from . import errors, jasyncio, tag, utils -from .client import client, connector -from .errors import JujuAPIError -from .offerendpoints import ParseError as OfferParseError -from .offerendpoints import parse_offer_endpoint, parse_offer_url -from .user import User - -log = logging.getLogger(__name__) - - -class RemoveError(Exception): - def __init__(self, message): - self.message = message - - -class Controller: - def __init__( - self, - max_frame_size=None, - bakery_client=None, - jujudata=None, - ): - """Instantiate a new Controller. - - One of the connect_* methods will need to be called before this - object can be used for anything interesting. - - If jujudata is None, jujudata.FileJujuData will be used. - - :param max_frame_size: See - `juju.client.connection.Connection.MAX_FRAME_SIZE` - :param bakery_client httpbakery.Client: The bakery client to use - for macaroon authorization. - :param jujudata JujuData: The source for current controller - information. - """ - self._connector = connector.Connector( - max_frame_size=max_frame_size, - bakery_client=bakery_client, - jujudata=jujudata, - ) - self._controller_name = None - - async def __aenter__(self): - await self.connect() - return self - - async def __aexit__(self, exc_type, exc, tb): - await self.disconnect() - - async def connect(self, *args, **kwargs): - """Connect to a Juju controller. - - This supports two calling conventions: - - The controller and (optionally) authentication information can be - taken from the data files created by the Juju CLI. This convention - will be used if a ``controller_name`` is specified, or if the - ``endpoint`` is not. - - Otherwise, both the ``endpoint`` and authentication information - (``username`` and ``password``, or ``bakery_client`` and/or - ``macaroons``) are required. - - If a single positional argument is given, it will be assumed to be - the ``controller_name``. Otherwise, the first positional argument, - if any, must be the ``endpoint``. - - Available parameters are: - - :param str controller_name: Name of controller registered with the - Juju CLI. - :param str endpoint: The hostname:port of the controller to connect to. - :param str username: The username for controller-local users (or None - to use macaroon-based login.) - :param str password: The password for controller-local users. - :param str cacert: The CA certificate of the controller - (PEM formatted). - :param httpbakery.Client bakery_client: The macaroon bakery client to - to use when performing macaroon-based login. Macaroon tokens - acquired when logging will be saved to bakery_client.cookies. - If this is None, a default bakery_client will be used. - :param list macaroons: List of macaroons to load into the - ``bakery_client``. - :param int max_frame_size: The maximum websocket frame size to allow. - :param specified_facades: (deprecated) overwrite the facades with a series of - specified facades. - """ - await self.disconnect() - if "endpoint" not in kwargs and len(args) < 2: - if args and "model_name" in kwargs: - raise TypeError("connect() got multiple values for controller_name") - elif args: - controller_name = args[0] - else: - controller_name = kwargs.pop("controller_name", None) - await self._connector.connect_controller(controller_name, **kwargs) - else: - if "controller_name" in kwargs: - raise TypeError( - "connect() got values for both controller_name and endpoint" - ) - if args and "endpoint" in kwargs: - raise TypeError("connect() got multiple values for endpoint") - has_userpass = len(args) >= 3 or {"username", "password"}.issubset(kwargs) - has_macaroons = len(args) >= 5 or not { - "bakery_client", - "macaroons", - }.isdisjoint(kwargs) - if not (has_userpass or has_macaroons): - raise TypeError("connect() missing auth params") - arg_names = [ - "endpoint", - "username", - "password", - "cacert", - "bakery_client", - "macaroons", - "max_frame_size", - ] - for i, arg in enumerate(args): - kwargs[arg_names[i]] = arg - if "endpoint" not in kwargs: - raise ValueError("endpoint is required if controller_name not given") - if not ( - {"username", "password"}.issubset(kwargs) - or {"bakery_client", "macaroons"}.intersection(kwargs) - ): - raise ValueError( - "Authentication parameters are required " - "if controller_name not given" - ) - await self._connector.connect(**kwargs) - await self.update_endpoints() - - async def update_endpoints(self): - try: - info = await self.info() - self._connector._connection.endpoints = [ - (e, info.results[0].cacert) for e in info.results[0].addresses - ] - except errors.JujuPermissionError: - log.warning( - "This user doesn't have at least read access to the controller model, so endpoints are not updated after connection." - ) - pass - - async def connect_current(self): - """.. deprecated:: 0.7.3 - Use :meth:`.connect()` instead. - """ - return await self.connect() - - async def connect_controller(self, controller_name): - """.. deprecated:: 0.7.3 - Use :meth:`.connect(controller_name)` instead. - """ - return await self.connect(controller_name) - - async def _connect_direct(self, **kwargs): - await self.disconnect() - await self._connector.connect(**kwargs) - - def is_connected(self): - """Reports whether the Controller is currently connected.""" - return self._connector.is_connected() - - def connection(self): - """Return the current Connection object. It raises an exception - if the Controller is disconnected - """ - return self._connector.connection() - - @property - def controller_name(self): - if not self._controller_name: - try: - self._controller_name = ( - self._connector.jujudata.controller_name_by_endpoint( - self._connector.connection().endpoint - ) - ) - except FileNotFoundError: - raise errors.PylibjujuError( - "Unable to determine controller name. controllers.yaml not found." - ) - return self._controller_name - - @property - def controller_uuid(self): - return self._connector.controller_uuid - - @property - async def api_endpoints(self): - """Get API endpoints - - :return list string: List of API Endpoints - """ - info = await self.info() - return info.results[0].addresses - - async def disconnect(self): - """Shut down the watcher task and close websockets.""" - await self._connector.disconnect(entity="controller") - - async def add_credential( - self, name=None, credential=None, cloud=None, owner=None, force=False - ): - """Add or update a credential to the controller. - - :param str name: Name of new credential. If None, the default - local credential is used. Name must be provided if a credential - is given. - :param CloudCredential credential: Credential to add. If not given, - it will attempt to read from local data, if available. - :param str cloud: Name of cloud to associate the credential with. - Defaults to the same cloud as the controller. - :param str owner: Username that will own the credential. Defaults to - the current user. - :param bool force: Force indicates whether the update should be forced. - It's only supported for facade v3 or later. - Defaults to false. - :returns: Name of credential that was uploaded. - """ - if not cloud: - cloud = await self.get_cloud() - - if not owner: - owner = self.connection().info["user-info"]["identity"] - - if credential and not name: - raise errors.JujuError("Name must be provided for credential") - - if not credential: - name, credential = self._connector.jujudata.load_credential(cloud, name) - if credential is None: - raise errors.JujuError(f"Unable to find credential: {name}") - - if credential.auth_type == "jsonfile" and "file" in credential.attrs: - # file creds have to be loaded before being sent to the controller - try: - # it might already be JSON - json.loads(credential.attrs["file"]) - except json.JSONDecodeError: - # not valid JSON, so maybe it's a file - cred_path = Path(credential.attrs["file"]) - if cred_path.exists(): - # make a copy - cred_json = credential.to_json() - credential = client.CloudCredential.from_json(cred_json) - # inline the cred - credential.attrs["file"] = cred_path.read_text() - - log.debug("Uploading credential %s", name) - cloud_facade = client.CloudFacade.from_connection(self.connection()) - tagged_credentials = [ - client.TaggedCredential( - tag=tag.credential(cloud, tag.untag("user-", owner), name), - credential=credential, - ) - ] - if cloud_facade.version >= 3: - # UpdateCredentials was renamed to UpdateCredentialsCheckModels - # in facade version 3. - await cloud_facade.UpdateCredentialsCheckModels( - credentials=tagged_credentials, - force=force, - ) - else: - await cloud_facade.UpdateCredentials(credentials=tagged_credentials) - return name - - async def add_cloud(self, name, cloud): - """Add a cloud to this controller. - - :param str name: Name to give the new cloud. - :param Cloud cloud: Cloud configuration. - :return Cloud: Cloud that was created. - """ - log.debug("Adding cloud %s", name) - cloud_facade = client.CloudFacade.from_connection(self.connection()) - await cloud_facade.AddCloud(cloud=cloud, name=name) - result = await self.cloud(name=name) - return result.cloud - - async def info(self): - """Show Controller Info from connection - - :return ControllerAPIInfoResult - """ - log.debug("Getting information") - uuids = await self.model_uuids() - if "controller" not in uuids: - raise errors.JujuPermissionError("Requires access to controller model.") - controller_facade = client.ControllerFacade.from_connection(self.connection()) - params = [client.Entity(tag.model(uuids["controller"]))] - return await controller_facade.ControllerAPIInfoForModels(entities=params) - - async def remove_cloud(self, name): - """Remove a cloud from this controller. - - :param str name: Name of the cloud to remove. - """ - log.debug("Removing cloud %s", name) - cloud_facade = client.CloudFacade.from_connection(self.connection()) - await cloud_facade.RemoveClouds(entities=[client.Entity(tag.cloud(name))]) - - async def add_model( - self, - model_name, - cloud_name=None, - credential_name=None, - owner=None, - config=None, - region=None, - ): - """Add a model to this controller. - - :param str model_name: Name to give the new model. - :param str cloud_name: Name of the cloud in which to create the - model, e.g. 'aws'. Defaults to same cloud as controller. - :param str credential_name: Name of the credential to use when - creating the model. If not given, it will attempt to find a - default credential. - :param str owner: Username that will own the model. Defaults to - the current user. - :param dict config: Model configuration. - :param str region: Region in which to create the model. - :return Model: A connection to the newly created model. - """ - model_facade = client.ModelManagerFacade.from_connection(self.connection()) - - owner = owner or self.connection().info["user-info"]["identity"] - cloud_name = cloud_name or await self.get_cloud() - - try: - # attempt to add/update the credential from local data if available - credential_name = await self.add_credential( - name=credential_name, cloud=cloud_name, owner=owner - ) - except errors.JujuError: - # if it's not available locally, assume it's on the controller - pass - - if credential_name: - credential = tag.credential( - cloud_name, tag.untag("user-", owner), credential_name - ) - else: - credential = None - - log.debug("Creating model %s", model_name) - - if not config or "authorized-keys" not in config: - config = config or {} - config["authorized-keys"] = await utils.read_ssh_key() - - model_info = await model_facade.CreateModel( - cloud_tag=tag.cloud(cloud_name), - config=config, - credential=credential, - name=model_name, - owner_tag=owner, - region=region, - ) - from juju.model import Model - - model = Model(jujudata=self._connector.jujudata) - kwargs = self.connection().connect_params() - kwargs["uuid"] = model_info.uuid - model._info = model_info - await model._connect_direct(**kwargs) - - return model - - async def destroy_models( - self, *models, destroy_storage=False, force=False, max_wait=None - ): - """Destroy one or more models. - - :param str *models: Names or UUIDs of models to destroy - :param bool destroy_storage: Whether or not to destroy storage when - destroying the models. Defaults to false. - :param bool force: Whether or not to force when destroying the models. - Defaults to false. - :param int max_wait : Max time in seconds to wait when destroying the models. - - """ - uuids = await self.model_uuids() - models = [uuids.get(model) or model for model in models] - - model_facade = client.ModelManagerFacade.from_connection(self.connection()) - - log.debug( - "Destroying model%s %s", "" if len(models) == 1 else "s", ", ".join(models) - ) - - if model_facade.version >= 5: - params = [ - client.DestroyModelParams( - model_tag=tag.model(model), - destroy_storage=destroy_storage, - force=force, - max_wait=max_wait, - ) - for model in models - ] - await model_facade.DestroyModels(models=params) - else: - params = [client.Entity(tag.model(model)) for model in models] - - await model_facade.DestroyModels(entities=params) - - destroy_model = destroy_models - - async def add_user(self, username, password=None, display_name=None): - """Add a user to this controller. - - :param str username: Username - :param str password: Password - :param str display_name: Display name - :returns: A :class:`~juju.user.User` instance - """ - if not display_name: - display_name = username - user_facade = client.UserManagerFacade.from_connection(self.connection()) - users = [ - client.AddUser( - display_name=display_name, username=username, password=password - ) - ] - results = await user_facade.AddUser(users=users) - secret_key = results.results[0].secret_key - return await self.get_user(username, secret_key=secret_key) - - async def remove_user(self, username): - """Remove a user from this controller.""" - client_facade = client.UserManagerFacade.from_connection(self.connection()) - user = tag.user(username) - await client_facade.RemoveUser(entities=[client.Entity(user)]) - - async def change_user_password(self, username, password): - """Change the password for a user in this controller. - - :param str username: Username - :param str password: New password - - """ - user_facade = client.UserManagerFacade.from_connection(self.connection()) - entity = client.EntityPassword(password=password, tag=tag.user(username)) - return await user_facade.SetPassword(changes=[entity]) - - async def reset_user_password(self, username): - """Reset user password. - - :param str username: Username - :returns: A :class:`~juju.user.User` instance - """ - user_facade = client.UserManagerFacade.from_connection(self.connection()) - entity = client.Entity(tag.user(username)) - results = await user_facade.ResetPassword(entities=[entity]) - secret_key = results.results[0].secret_key - return await self.get_user(username, secret_key=secret_key) - - async def destroy(self, destroy_all_models=False, destroy_storage=False): - """Destroy this controller. - - :param bool destroy_all_models: Destroy all hosted models in the - controller. - :param bool destroy_storage: Destroy all hosted storage in the - controller. - """ - controller_facade = client.ControllerFacade.from_connection(self.connection()) - return await controller_facade.DestroyController( - destroy_models=destroy_all_models, destroy_storage=destroy_storage - ) - - async def disable_user(self, username): - """Disable a user. - - :param str username: Username - - """ - user_facade = client.UserManagerFacade.from_connection(self.connection()) - entity = client.Entity(tag.user(username)) - return await user_facade.DisableUser(entities=[entity]) - - async def enable_user(self, username): - """Re-enable a previously disabled user.""" - user_facade = client.UserManagerFacade.from_connection(self.connection()) - entity = client.Entity(tag.user(username)) - return await user_facade.EnableUser(entities=[entity]) - - async def get_model_info(self, model_name=None, model_uuid=None): - """Return a client.ModelInfo object for a given Model. - - Retrieves latest info for this Model from the api server. The - return value is cached on the Model.info attribute so that the - valued may be accessed again without another api call, if - desired. - - This method is called automatically when the Model is connected, - resulting in Model.info being initialized without requiring an - explicit call to this method. - - """ - if model_uuid is None and model_name is None: - raise errors.JujuError( - "get_model_info requires either a name or a uuid for a model" - ) - - facade = client.ModelManagerFacade.from_connection(self.connection()) - if model_uuid is None: - uuids = await self.model_uuids() - try: - model_uuid = uuids[model_name] - except KeyError: - raise errors.JujuError( - f"{model_name} is not among the models in the controller : {uuids}" - ) - entity = client.Entity(tag.model(model_uuid)) - _model_info_results = await facade.ModelInfo(entities=[entity]) - return _model_info_results.results[0].result - - async def cloud(self, name=None): - """Get Cloud - - :param str name: Cloud name. If not specified, the cloud where - the controller lives on is returned. - :returns: -> ~CloudResult - """ - if name is None: - name = await self.get_cloud() - entity = client.Entity(tag.cloud(name)) - cloud_facade = client.CloudFacade.from_connection(self.connection()) - cloud = await cloud_facade.Cloud(entities=[entity]) - if len(cloud.results) == 0: - log.error("No clouds found.") - raise - elif len(cloud.results) > 1: - log.error("More than one cloud found.") - raise - return cloud.results[0] - - async def clouds(self): - """Get all the clouds in the controller - - :returns: -> ~CloudsResult - """ - cloud_facade = client.CloudFacade.from_connection(self.connection()) - return await cloud_facade.Clouds() - - async def get_cloud(self): - """Get the name of the cloud that this controller lives on.""" - cloud_facade = client.CloudFacade.from_connection(self.connection()) - - result = await cloud_facade.Clouds() - cloud = next(iter(result.clouds)) - return tag.untag("cloud-", cloud) - - async def get_models(self, all=False, username=None): # noqa: A002 - """.. deprecated:: 0.7.0 - Use :meth:`.list_models` instead. - """ - return await self.list_models(username, all) - - async def model_uuids(self, username=None, all=False): # noqa: A002 - """Return a mapping of model names to UUIDs the given user can access. - - :param str username: Optional username argument, defaults to - current connected user. - - :param bool all: Flag to list all models, regardless of - user accessibility (administrative users only) - - :returns: {str name : str UUID} - """ - model_manager_facade = client.ModelManagerFacade.from_connection( - self.connection() - ) - u_name = username if username else self.get_current_username() - user = tag.user(u_name) - - user_model_list = await model_manager_facade.ListModelSummaries( - user_tag=user, all_=all - ) - model_summaries = [msr.result for msr in user_model_list.results] - return { - model_summary.name: model_summary.uuid for model_summary in model_summaries - } - - async def list_models(self, username=None, all=False): # noqa: A002 - """Return list of names of the available models on this controller. - - Equivalent to ``sorted((await self.model_uuids()).keys())`` - """ - uuids = await self.model_uuids(username, all) - return sorted(uuids.keys()) - - async def get_current_user(self, secret_key=None): - """Returns the user object associated with the current connection. - :param str secret_key: Issued by juju when add or reset user - password - - :returns: A :class:`~juju.user.User` instance - """ - return await self.get_user(self.connection().username) - - def get_current_username(self): - """Returns the username associated with the current connection. - - :returns: :str: username of the connected user - """ - return self.connection().username - - async def get_model(self, model): - """Get a model by name or UUID. - - :param str model: Model name or UUID - :returns Model: Connected Model instance. - """ - uuids = await self.model_uuids() - uuid = uuids.get(model) or model - - from juju.model import Model - - model = Model() - kwargs = self.connection().connect_params() - kwargs["uuid"] = uuid - await model._connect_direct(**kwargs) - return model - - async def get_user(self, username, secret_key=None): - """Get a user by name. - - :param str username: Username - :param str secret_key: Issued by juju when add or reset user - password - :returns: A :class:`~juju.user.User` instance - """ - client_facade = client.UserManagerFacade.from_connection(self.connection()) - user = tag.user(username) - args = [client.Entity(user)] - try: - response = await client_facade.UserInfo( - entities=args, include_disabled=True - ) - except errors.JujuError as e: - if "permission denied" in e.errors: - # apparently, trying to get info for a nonexistent user returns - # a "permission denied" error rather than an empty result set - return None - raise - if response.results and response.results[0].result: - return User(self, response.results[0].result, secret_key=secret_key) - return None - - async def get_users(self, include_disabled=False): - """Return list of users that can connect to this controller. - - :param bool include_disabled: Include disabled users - :returns: A list of :class:`~juju.user.User` instances - """ - client_facade = client.UserManagerFacade.from_connection(self.connection()) - response = await client_facade.UserInfo( - entities=None, include_disabled=include_disabled - ) - return [User(self, r.result) for r in response.results] - - async def grant(self, username, acl="login"): - """Grant access level of the given user on the controller. - Note that if the user already has higher permissions than the - provided ACL, this will do nothing (see revoke for a way to - remove permissions). - :param str username: Username - :param str acl: Access control ('login', 'add-model' or 'superuser') - :returns: True if new access was granted, False if user already had - requested access or greater. Raises JujuError if failed. - """ - controller_facade = client.ControllerFacade.from_connection(self.connection()) - user = tag.user(username) - changes = client.ModifyControllerAccess(acl, "grant", user) - try: - await controller_facade.ModifyControllerAccess(changes=[changes]) - return True - except errors.JujuError as e: - if "user already has" in str(e): - return False - else: - raise - - async def revoke(self, username, acl="login"): - """Removes some or all access of a user to from a controller - If 'login' access is revoked, the user will no longer have any - permissions on the controller. Revoking a higher privilege from - a user without that privilege will have no effect. - - :param str username: username - :param str acl: Access to remove ('login', 'add-model' or 'superuser') - """ - controller_facade = client.ControllerFacade.from_connection(self.connection()) - user = tag.user(username) - changes = client.ModifyControllerAccess(acl, "revoke", user) - return await controller_facade.ModifyControllerAccess(changes=[changes]) - - async def grant_model(self, username, model_uuid, acl="read"): - """Grant a user access to a model. Note that if the user - already has higher permissions than the provided ACL, - this will do nothing (see revoke_model for a way to remove - permissions). - - :param str username: Username - :param str model_uuid: The UUID of the model to change. - :param str acl: Access control ('read, 'write' or 'admin') - """ - model_facade = client.ModelManagerFacade.from_connection(self.connection()) - user = tag.user(username) - model = tag.model(model_uuid) - changes = client.ModifyModelAccess(acl, "grant", model, user) - return await model_facade.ModifyModelAccess(changes=[changes]) - - async def revoke_model(self, username, model_uuid, acl="read"): - """Revoke some or all of a user's access to a model. - If 'read' access is revoked, the user will no longer have any - permissions on the model. Revoking a higher privilege from - a user without that privilege will have no effect. - - :param str username: Username to revoke - :param str model_uuid: The UUID of the model to change. - :param str acl: Access control ('read, 'write' or 'admin') - """ - model_facade = client.ModelManagerFacade.from_connection(self.connection()) - user = tag.user(username) - model = tag.model(model_uuid) - changes = client.ModifyModelAccess(acl, "revoke", model, user) - return await model_facade.ModifyModelAccess(changes=[changes]) - - async def create_offer( - self, model_uuid, endpoint, offer_name=None, application_name=None - ): - """Offer a deployed application using a series of endpoints for use by - consumers. - - @param endpoint: holds the application and endpoint you want to offer - @param offer_name: override the offer name to help the consumer - @param application_name: overrides the application name in the endpoint - """ - # If we have both the offer_name and the application_name - # then we're coming from bundle/overlays, so no need to parse the endpoint - # Also we accept endpoints without a colon (:) in the overlays - if offer_name and application_name: - o_name = offer_name - a_name = application_name - eps = {endpoint: endpoint} - else: - try: - offer = parse_offer_endpoint(endpoint) - except OfferParseError as e: - log.error(e.message) - raise - - o_name = offer_name if offer_name else offer.application - a_name = application_name if application_name else offer.application - eps = {name: name for name in offer.endpoints} - - params = client.AddApplicationOffer() - params.application_name = a_name - params.endpoints = eps - params.offer_name = o_name - params.model_tag = tag.model(model_uuid) - - facade = client.ApplicationOffersFacade.from_connection(self.connection()) - return await facade.Offer(offers=[params]) - - async def list_offers(self, model_name): - """Offers list information about applications' endpoints that have been - shared and who is connected. - """ - params = client.OfferFilter() - params.model_name = model_name - - facade = client.ApplicationOffersFacade.from_connection(self.connection()) - return await facade.ListApplicationOffers(filters=[params]) - - async def remove_offer(self, model_uuid, offer, force=False): - """Remove offer for an application. - - Offers will also remove relations to those offers, use force to do - so, without an error. - """ - url = None - try: - url = parse_offer_url(offer) - except OfferParseError as e: - log.error(e.message) - raise - if url is None: - raise Exception - - offer_source = url.source - if offer_source == "": - offer_source = self.controller_name - if not force: - raise RemoveError( - "removing offer will also remove relations, use force and try again." - ) - - facade = client.ApplicationOffersFacade.from_connection(self.connection()) - return await facade.DestroyOffers(force=force, offer_urls=[url.string()]) - - async def get_consume_details(self, endpoint): - """get_consume_details returns the details necessary to pass to another - model to consume the specified offers represented by the urls. - """ - facade = client.ApplicationOffersFacade.from_connection(self.connection()) - offers = await facade.GetConsumeDetails( - offer_urls=client.OfferURLs(offer_urls=[endpoint]) - ) - if len(offers.results) != 1: - raise JujuAPIError("expected to find one result") - result = offers.results[0] - if result.error is not None: - raise JujuAPIError(result.error) - - return result - - async def watch_model_summaries(self, callback, as_admin=False): - """Watch the controller for model summary updates. - - If as_admin is true, a call will be made as the admin to watch - all models in the controller. If the user isn't a superuser they - will get a permission error. - """ - stop_event = jasyncio.Event() - - async def _watcher(stop_event): - try: - facade = client.ControllerFacade.from_connection(self.connection()) - watcher = client.ModelSummaryWatcherFacade.from_connection( - self.connection() - ) - if as_admin: - result = await facade.WatchAllModelSummaries() - watcher.Id = result.watcher_id - else: - result = await facade.WatchModelSummaries() - log.debug(f"watcher id: {result.watcher_id}") - watcher.Id = result.watcher_id - - while True: - try: - results = await utils.run_with_interrupt( - watcher.Next(), stop_event, log=log - ) - except JujuAPIError as e: - if "watcher was stopped" not in str(e): - raise - except websockets.ConnectionClosed: - break - if stop_event.is_set(): - try: - await watcher.Stop() - except websockets.ConnectionClosed: - pass # can't stop on a closed conn - break - for summary in results.models: - callback(summary) - except CancelledError: - pass - except Exception: - log.exception("Error in watcher") - raise - - log.debug("Starting watcher task for model summaries") - jasyncio.ensure_future(_watcher(stop_event)) - return stop_event - - async def add_secret_backends(self, id_, name, backend_type, config): - """Add a new secret backend. - - Parameters - ---------- - id : string - id for the backend - name : string - name of the backend - backend-type : string - config : dict - dictionary with the backend configuration values - - Returns - ------- - list - a list of errors if any - - """ - facade = client.SecretBackendsFacade.from_connection(self.connection()) - return await facade.AddSecretBackends([ - { - "id": id_, - "backend-type": backend_type, - "config": config, - "name": name, - "token-rotate-interval": config.get("token-rotate-interval", None), - } - ]) - - async def list_secret_backends(self, reveal=False): - """Return the list of secret backends - - Parameters - ---------- - reveal : boolean - include sensitive backend config content if true - - Returns - ------- - list - a list of available secret backends - - """ - facade = client.SecretBackendsFacade.from_connection(self.connection()) - return await facade.ListSecretBackends(None, reveal) - - async def remove_secret_backends(self, name, force=False): - """Remove a secrets backend. - - Parameters - ---------- - name : name of the backend - force : true if the operation is foced - - Returns - ------- - error if any - - """ - facade = client.SecretBackendsFacade.from_connection(self.connection()) - return await facade.RemoveSecretBackends([{"name": name, "force": force}]) - - async def update_secret_backends( - self, - name, - config=None, - force=False, - name_change=None, - token_rotate_interval=None, - ): - """Update a backend. - - Parameters - ---------- - name : string - the backend name - config : dict - key value dict with configuration parameters - force : boolean - true to force the update process - name_change : string - new name for the backend - token_rotate_interval : int - token rotation interval - - """ - facade = client.SecretBackendsFacade.from_connection(self.connection()) - return await facade.UpdateSecretBackends([ - { - "name": name, - "config": config, - "force": force, - "token-rotate-interval": token_rotate_interval, - "name-change": name_change, - } - ]) - - -class ConnectedController(Controller): - def __init__( - self, - connection, - max_frame_size=None, - bakery_client=None, - jujudata=None, - ): - super().__init__( - max_frame_size=max_frame_size, - bakery_client=bakery_client, - jujudata=jujudata, - ) - self._conn = connection - - async def __aenter__(self): - kwargs = self._conn.connect_params() - kwargs.pop("uuid") - await self._connect_direct(**kwargs) - return self - - async def __aexit__(self, exc_type, exc, tb): - await self.disconnect() diff --git a/build/lib/juju/delta.py b/build/lib/juju/delta.py deleted file mode 100644 index c82a20bd..00000000 --- a/build/lib/juju/delta.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -from . import model -from .client import client, overrides - - -def get_entity_delta(d: overrides.Delta): - return _delta_types[d.entity](d.deltas) - - -def get_entity_class(entity_type): - return _delta_types[entity_type].get_entity_class() - - -class EntityDelta(client.Delta): - data: dict[str, str] - - def get_id(self) -> str: - return self.data["id"] - - @classmethod - def get_entity_class(cls) -> type[model.ModelEntity]: - raise NotImplementedError() - - -class ActionDelta(EntityDelta): - @classmethod - def get_entity_class(cls): - from .action import Action - - return Action - - -class ApplicationDelta(EntityDelta): - def get_id(self): - return self.data["name"] - - @classmethod - def get_entity_class(cls): - from .application import Application - - return Application - - -class AnnotationDelta(EntityDelta): - def get_id(self): - return self.data["tag"] - - @classmethod - def get_entity_class(cls): - from .annotation import Annotation - - return Annotation - - -class ModelDelta(EntityDelta): - def get_id(self): - return self.data["model-uuid"] - - @classmethod - def get_entity_class(cls): - from .model import ModelInfo - - return ModelInfo - - -class MachineDelta(EntityDelta): - @classmethod - def get_entity_class(cls): - from .machine import Machine - - return Machine - - -class UnitDelta(EntityDelta): - def get_id(self): - return self.data["name"] - - @classmethod - def get_entity_class(cls): - from .unit import Unit - - return Unit - - -class RelationDelta(EntityDelta): - @classmethod - def get_entity_class(cls): - from .relation import Relation - - return Relation - - -class RemoteApplicationDelta(EntityDelta): - def get_id(self): - return self.data["name"] - - @classmethod - def get_entity_class(cls): - from .remoteapplication import RemoteApplication - - return RemoteApplication - - -class CharmDelta(EntityDelta): - def get_id(self): - return self.data["charm-url"] - - @classmethod - def get_entity_class(cls): - from .charm import Charm - - return Charm - - -class ApplicationOfferDelta(EntityDelta): - def get_id(self): - return self.data["application-name"] - - @classmethod - def get_entity_class(cls): - from .remoteapplication import ApplicationOffer - - return ApplicationOffer - - -_delta_types = { - "action": ActionDelta, - "annotation": AnnotationDelta, - "application": ApplicationDelta, - "applicationOffer": ApplicationOfferDelta, - "charm": CharmDelta, - "machine": MachineDelta, - "model": ModelDelta, - "relation": RelationDelta, - "remoteApplication": RemoteApplicationDelta, - "unit": UnitDelta, -} diff --git a/build/lib/juju/errors.py b/build/lib/juju/errors.py deleted file mode 100644 index 92418703..00000000 --- a/build/lib/juju/errors.py +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - - -class JujuError(Exception): - def __init__(self, *args, **kwargs): - self.message = "" - self.errors = [] - if args: - self.message = str(args[0]) - if isinstance(args[0], (list, tuple)): - self.errors = args[0] - elif len(args) > 1: - self.errors = list(args) - else: - self.errors = [self.message] - super().__init__(*args, **kwargs) - - -class JujuAPIError(JujuError): - def __init__(self, result): - self.result = result - self.message = result["error"] - self.error_code = result.get("error-code") - self.response = result["response"] - self.request_id = result["request-id"] - self.error_info = result.get("error-info") - super().__init__(self.message) - - -class JujuConnectionError(ConnectionError, JujuError): - pass - - -class JujuAuthError(JujuConnectionError): - pass - - -class JujuRedirectException(Exception): - """Exception indicating that a redirection was requested""" - - def __init__(self, redirect_info, follow_redirect=True): - self.redirect_info = redirect_info - self.follow_redirect = follow_redirect - - @property - def ca_cert(self): - return self.redirect_info["ca-cert"] - - @property - def endpoints(self): - return [ - ("{value}:{port}".format(**s), self.ca_cert) - for servers in self.redirect_info["servers"] - for s in servers - if s["scope"] == "public" or not self.follow_redirect - ] - - -class JujuEntityNotFoundError(JujuError): - """Exception indicating that an entity was not found in the state. It was - expected that the entity was found in state and this is a terminal - condition. - To fix this condition, you should disconnect and reconnect to ensure that - any missing entities are correctly picked up. - """ - - def __init__(self, entity_name, entity_types=None): - self.entity_name = entity_name - self.entity_types = entity_types - super().__init__(f"Entity not found: {entity_name}") - - -class JujuModelError(JujuError): - pass - - -class JujuMachineError(JujuError): - pass - - -class JujuAgentError(JujuError): - pass - - -class JujuAppError(JujuError): - pass - - -class JujuUnitError(JujuError): - pass - - -class JujuPermissionError(JujuError): - pass - - -class JujuBackupError(JujuError): - pass - - -class PylibjujuProgrammingError(Exception): - pass - - -class JujuNotSupportedError(JujuError): - pass - - -class JujuNotValid(JujuError): - def __init__(self, entity_type, entity_name): - self.entity_type = entity_type - self.entity_name = entity_name - super().__init__(f"Invalid {entity_type} : {entity_name}") - - -class JujuConfigError(JujuError): - """Exception raised during processing a configuration key-value pair - in a config set for an application. - """ - - def __init__(self, config, config_pair, message=None): - self.config = config - self.config_pair = config_pair - if message is None: - self.message = ( - "Couldn't process the value of a config pair : %s, value of type %s" - % (self.config_pair, type(self.config_pair[1])) - ) - else: - self.message = message - super().__init__(self.message) - - -class JujuApplicationConfigError(JujuConfigError): - pass - - -class JujuModelConfigError(JujuConfigError): - pass - - -class JujuControllerNotFoundError(JujuError): - pass - - -class AbstractMethodError(Exception): - pass - - -class PylibjujuError(JujuError): - pass - - -class JujuUnknownVersion(PylibjujuError): - pass diff --git a/build/lib/juju/exceptions.py b/build/lib/juju/exceptions.py deleted file mode 100644 index a8544993..00000000 --- a/build/lib/juju/exceptions.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - - -class DeadEntityException(Exception): - pass diff --git a/build/lib/juju/jasyncio.py b/build/lib/juju/jasyncio.py deleted file mode 100644 index 49d49914..00000000 --- a/build/lib/juju/jasyncio.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -# A compatibility layer on asyncio that ensures we have all the right -# bindings for the functions we need from asyncio. Reason for this -# layer is the frequent functional changes, additions and deprecations -# in asyncio across the different Python versions. - -# Any module that needs to use the asyncio should get the binding from -# this layer. - -import asyncio -import functools -import logging -import signal -from asyncio import ( - ALL_COMPLETED as ALL_COMPLETED, -) -from asyncio import ( - FIRST_COMPLETED as FIRST_COMPLETED, -) -from asyncio import ( - CancelledError, - Task, - create_task, - wait, -) - -# FIXME: integration tests don't use these, but some are used in this repo -# Use primitives from asyncio within this repo and remove these re-exports -from asyncio import ( - Event as Event, -) -from asyncio import ( - Lock as Lock, -) -from asyncio import ( - Queue as Queue, -) -from asyncio import ( - TimeoutError as TimeoutError, # noqa: A004 -) -from asyncio import ( - all_tasks as all_tasks, -) -from asyncio import ( - as_completed as as_completed, -) -from asyncio import ( - create_subprocess_exec as create_subprocess_exec, -) -from asyncio import ( - current_task as current_task, -) -from asyncio import ( - ensure_future as ensure_future, -) -from asyncio import ( - gather as gather, -) -from asyncio import ( - get_event_loop_policy as get_event_loop_policy, -) -from asyncio import ( - get_running_loop as get_running_loop, -) -from asyncio import ( - new_event_loop as new_event_loop, -) -from asyncio import ( - shield as shield, -) -from asyncio import ( - sleep as sleep, -) -from asyncio import ( - subprocess as subprocess, -) -from asyncio import ( - wait_for as wait_for, -) - -import websockets - -ROOT_LOGGER = logging.getLogger() - - -def create_task_with_handler(coro, task_name, logger=ROOT_LOGGER) -> Task: - """Wrapper around "asyncio.create_task" to make sure the task - exceptions are handled properly. - - asyncio loop event_handler is only called on task exceptions when - the Task object is cleared from memory. But the GC doesn't clear - the Task if we keep a reference for it (e.g. _pinger_task in - connection.py) until the very end. - - This makes sure the exceptions are retrieved and properly - handled/logged whenever the Task is destroyed. - """ - - def _task_result_exp_handler(task, task_name=task_name, logger=logger): - try: - task.result() - except CancelledError: - pass - except websockets.exceptions.ConnectionClosed: - return - except Exception as e: - # This really is an arbitrary exception we need to catch - # - # No need to re-raise, though, because after this point - # the only thing that can catch this is asyncio loop base - # event_handler, which won't do anything but yell 'Task - # exception was never retrieved' anyways. - logger.exception("Task %s raised an exception: %s" % (task_name, e)) - - task = create_task(coro) - task.add_done_callback( - functools.partial(_task_result_exp_handler, task_name=task_name, logger=logger) - ) - return task - - -class SingletonEventLoop: - """Single instance containing an event loop to be reused.""" - - loop = None - - def __new__(cls): - if not hasattr(cls, "instance"): - cls.instance = super().__new__(cls) - cls.instance.loop = asyncio.new_event_loop() - - return cls.instance - - -def run(*steps): - """Helper to run one or more async functions synchronously, with graceful - handling of SIGINT / Ctrl-C. - - Returns the return value of the last function. - """ - if not steps: - return - - task = None - run._sigint = False # function attr to allow setting from closure - # Use a singleton class to force a single event loop instance - loop = SingletonEventLoop().loop - - def abort(): - task.cancel() - run._sigint = True - - added = False - try: - loop.add_signal_handler(signal.SIGINT, abort) - added = True - except (ValueError, OSError, RuntimeError) as e: - # add_signal_handler doesn't work in a thread - if "main thread" not in str(e): - raise - try: - for step in steps: - task = loop.create_task(step) - loop.run_until_complete(wait([task])) - if run._sigint: - raise KeyboardInterrupt() - if task.exception(): - raise task.exception() - return task.result() - finally: - if added: - loop.remove_signal_handler(signal.SIGINT) diff --git a/build/lib/juju/juju.py b/build/lib/juju/juju.py deleted file mode 100644 index f77ab47a..00000000 --- a/build/lib/juju/juju.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -from juju.client.jujudata import FileJujuData -from juju.controller import Controller -from juju.errors import JujuError - - -class Juju: - def __init__(self, jujudata=None): - self.jujudata = jujudata or FileJujuData() - - def get_controllers(self): - """Return list of all available controllers.""" - return self.jujudata.controllers() - - async def get_controller(self, name, include_passwords=False): - """Get a controller by name. - - :param str name: Name of controller - :param bool include_passwords: Include passwords for accounts - - The returned controller will try and connect to be ready to use. - """ - # check if name is in the controllers.yaml - controllers = self.jujudata.controllers() - assert isinstance(controllers, dict) - if name not in controllers: - raise JujuError( - "%s is not among the controllers: %s" % (name, controllers.keys()) - ) - - # make a new Controller object that's connected to the - # controller with the given name - controller = Controller() - await controller.connect(name) - return controller diff --git a/build/lib/juju/loop.py b/build/lib/juju/loop.py deleted file mode 100644 index d9d2e8f8..00000000 --- a/build/lib/juju/loop.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -from .jasyncio import * # noqa - -import warnings - -warnings.warn( - "juju.loop module is being deprecated by 3.0, use juju.jasyncio instead", - DeprecationWarning, - stacklevel=2, -) diff --git a/build/lib/juju/machine.py b/build/lib/juju/machine.py deleted file mode 100644 index 70b7327b..00000000 --- a/build/lib/juju/machine.py +++ /dev/null @@ -1,310 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import ipaddress -import logging -import typing - -import pyrfc3339 - -from juju.utils import block_until, juju_ssh_key_paths - -from . import jasyncio, model, tag -from .annotationhelper import _get_annotations, _set_annotations -from .client import client -from .errors import JujuError - -log = logging.getLogger(__name__) - - -class Machine(model.ModelEntity): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - async def destroy(self, force=False): - """Remove this machine from the model. - - Blocks until the machine is actually removed. - - """ - if self.connection.is_using_old_client: - # Then we'll use the DestroyMachines from client.ClientFacade - facade = client.ClientFacade.from_connection(self.connection) - await facade.DestroyMachines(force=force, machine_names=[self.id]) - else: - facade = client.MachineManagerFacade.from_connection(self.connection) - await facade.DestroyMachineWithParams( - force=force, machine_tags=[tag.machine(self.id)] - ) - - log.debug("Destroying machine %s", self.id) - return await self.model._wait("machine", self.id, "remove") - - remove = destroy - - async def get_annotations(self): - """Get annotations on this machine. - - :return dict: The annotations for this application - """ - return await _get_annotations(self.tag, self.connection) - - async def set_annotations(self, annotations): - """Set annotations on this machine. - - :param annotations map[string]string: the annotations as key/value - pairs. - - """ - return await _set_annotations(self.tag, annotations, self.connection) - - def _format_addr(self, addr): - """Validate and format IP address. - - :param addr: IPv6 or IPv4 address - :type addr: str - :returns: Address string, optionally encapsulated in brackets ([]) - :rtype: str - :raises: ValueError - """ - ipaddr = ipaddress.ip_address(addr) - if isinstance(ipaddr, ipaddress.IPv6Address): - fmt = "[{}]" - else: - fmt = "{}" - return fmt.format(ipaddr) - - async def scp_to( - self, - source, - destination, - user="ubuntu", - proxy=False, - scp_opts="", - wait_for_active=False, - timeout=None, - ): - """Transfer files to this machine. - - :param str source: Local path of file(s) to transfer - :param str destination: Remote destination of transferred files - :param str user: Remote username - :param bool proxy: Proxy through the Juju API server - :param scp_opts: Additional options to the `scp` command - :type scp_opts: str or list - :param bool wait_for_active: Wait until the machine is ready to take in ssh commands. - :param int timeout: Time in seconds to wait until the machine becomes ready. - """ - if proxy: - raise NotImplementedError("proxy option is not implemented") - if wait_for_active: - await block_until(lambda: self.addresses, timeout=timeout) - try: - # if dns_name is an IP address format it appropriately - address = self._format_addr(self.dns_name) - except ValueError: - # otherwise we assume it to be a DNS resolvable string - address = self.dns_name - destination = f"{user}@{address}:{destination}" - await self._scp(source, destination, scp_opts) - - async def scp_from( - self, - source, - destination, - user="ubuntu", - proxy=False, - scp_opts="", - wait_for_active=False, - timeout=None, - ): - """Transfer files from this machine. - - :param str source: Remote path of file(s) to transfer - :param str destination: Local destination of transferred files - :param str user: Remote username - :param bool proxy: Proxy through the Juju API server - :param scp_opts: Additional options to the `scp` command - :type scp_opts: str or list - :param bool wait_for_active: Wait until the machine is ready to take in ssh commands. - :param int timeout: Time in seconds to wait until the machine becomes ready. - """ - if proxy: - raise NotImplementedError("proxy option is not implemented") - if wait_for_active: - await block_until(lambda: self.addresses, timeout=timeout) - try: - # if dns_name is an IP address format it appropriately - address = self._format_addr(self.dns_name) - except ValueError: - # otherwise we assume it to be a DNS resolvable string - address = self.dns_name - source = f"{user}@{address}:{source}" - await self._scp(source, destination, scp_opts) - - async def _scp(self, source, destination, scp_opts): - """Execute an scp command. Requires a fully qualified source and - destination. - """ - _, id_path = juju_ssh_key_paths() - cmd = ["scp", "-i", id_path, "-o", "StrictHostKeyChecking=no", "-q", "-B"] - cmd.extend(scp_opts.split() if isinstance(scp_opts, str) else scp_opts) - cmd.extend([source, destination]) - # There's a bit of a gap between the time that the machine is assigned an IP and the ssh - # service is up and listening, which creates a race for the ssh command. So we retry a - # couple of times until either we run out of attempts, or the ssh command succeeds to - # mitigate that effect. - # TODO (cderici): refactor the ssh and scp subcommand processing into a single method. - retry_backoff = 2 - retries = 10 - for _ in range(retries): - process = await jasyncio.create_subprocess_exec(*cmd) - await process.wait() - if process.returncode == 0: - break - await jasyncio.sleep(retry_backoff) - if process.returncode != 0: - raise JujuError(f"command failed after {retries} attempts: {cmd}") - - async def ssh( - self, - command, - user="ubuntu", - proxy=False, - ssh_opts=None, - wait_for_active=False, - timeout=None, - ): - """Execute a command over SSH on this machine. - - :param str command: Command to execute - :param str user: Remote username - :param bool proxy: Proxy through the Juju API server - :param str ssh_opts: Additional options to the `ssh` command - :param bool wait_for_active: Wait until the machine is ready to take in ssh commands. - :param int timeout: Time in seconds to wait until the machine becomes ready. - """ - if proxy: - raise NotImplementedError("proxy option is not implemented") - if wait_for_active: - await block_until(lambda: self.addresses, timeout=timeout) - address = self.dns_name - destination = f"{user}@{address}" - _, id_path = juju_ssh_key_paths() - cmd = [ - "ssh", - "-i", - id_path, - "-o", - "StrictHostKeyChecking=no", - "-q", - destination, - ] - if ssh_opts: - cmd.extend(ssh_opts.split() if isinstance(ssh_opts, str) else ssh_opts) - cmd.extend([command]) - - # There's a bit of a gap between the time that the machine is assigned an IP and the ssh - # service is up and listening, which creates a race for the ssh command. So we retry a - # couple of times until either we run out of attempts, or the ssh command succeeds to - # mitigate that effect. - retry_backoff = 2 - retries = 10 - for _ in range(retries): - process = await jasyncio.create_subprocess_exec( - *cmd, stdout=jasyncio.subprocess.PIPE, stderr=jasyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - if process.returncode == 0: - break - await jasyncio.sleep(retry_backoff) - if process.returncode != 0: - raise JujuError( - f"command failed: {cmd} after {retries} attempts, with {stderr.decode()}" - ) - # stdout is a bytes-like object, returning a string might be more useful - return stdout.decode() - - @property - def addresses(self) -> typing.List[str]: - """Returns the machine addresses.""" - return self.safe_data["addresses"] or [] - - @property - def agent_status(self): - """Returns the current Juju agent status string.""" - return self.safe_data["agent-status"]["current"] - - @property - def agent_status_since(self): - """Get the time when the `agent_status` was last updated.""" - return pyrfc3339.parse(self.safe_data["agent-status"]["since"]) - - @property - def agent_version(self): - """Get the version of the Juju machine agent. - - May return None if the agent is not yet available. - """ - version = self.safe_data["agent-status"]["version"] - if version: - return client.Number.from_json(version) - else: - return None - - @property - def status(self): - """Returns the current machine provisioning status string.""" - return self.safe_data["instance-status"]["current"] - - @property - def status_message(self): - """Returns the current machine provisioning status message.""" - return self.safe_data["instance-status"]["message"] - - @property - def status_since(self): - """Get the time when the `status` was last updated.""" - return pyrfc3339.parse(self.safe_data["instance-status"]["since"]) - - @property - def dns_name(self): - """Get the DNS name for this machine. This is a best guess based on the - addresses available in current data. - - May return None if no suitable address is found. - """ - ordered_scopes = ["public", "local-cloud", "local-fan"] - ordered_addresses = [ - address - for scope in ordered_scopes - for address in self.addresses - if scope == address["scope"] - ] - - for address in ordered_addresses: - scope = address["scope"] - for check_scope in ordered_scopes: - if scope == check_scope: - return address["value"] - return None - - @property - def hostname(self): - """Get the hostname for this machine as reported by the machine agent - running on it. This is only supported on 2.8.10+ controllers. - - May return None if no hostname information is available. - """ - if "hostname" in self.safe_data and self.safe_data["hostname"] != "": - return self.safe_data["hostname"] - return None - - @property - def series(self): - """Returns the series of the current machine""" - return self.safe_data["series"] - - @property - def tag(self): - return tag.machine(self.id) diff --git a/build/lib/juju/model.py b/build/lib/juju/model.py deleted file mode 100644 index c0f0758e..00000000 --- a/build/lib/juju/model.py +++ /dev/null @@ -1,3359 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -import base64 -import collections -import hashlib -import json -import logging -import os -import re -import stat -import sys -import tempfile -import warnings -import weakref -import zipfile -from concurrent.futures import CancelledError -from datetime import datetime, timedelta -from functools import partial -from pathlib import Path -from typing import TYPE_CHECKING, Any, Literal, Mapping, overload - -import websockets -import yaml -from typing_extensions import deprecated - -from . import jasyncio, provisioner, tag, utils -from .annotationhelper import _get_annotations, _set_annotations -from .bundle import BundleHandler, get_charm_series, is_local_charm -from .charmhub import CharmHub -from .client import client, connection, connector -from .client.overrides import Caveat, Macaroon -from .constraints import parse as parse_constraints -from .constraints import parse_storage_constraints -from .controller import ConnectedController, Controller -from .delta import get_entity_class, get_entity_delta -from .errors import ( - JujuAgentError, - JujuAPIError, - JujuAppError, - JujuBackupError, - JujuError, - JujuMachineError, - JujuModelConfigError, - JujuModelError, - JujuNotSupportedError, - JujuUnitError, - PylibjujuError, -) -from .exceptions import DeadEntityException -from .names import is_valid_application -from .offerendpoints import ParseError as OfferParseError -from .offerendpoints import parse_local_endpoint, parse_offer_url -from .origin import Channel, Source -from .placement import parse as parse_placement -from .secrets import create_secret_data, read_secret_data -from .tag import application as application_tag -from .url import URL, Schema -from .version import DEFAULT_ARCHITECTURE - -if TYPE_CHECKING: - from .application import Application - from .client._definitions import FullStatus - from .constraints import StorageConstraintDict - from .machine import Machine - from .relation import Relation - from .remoteapplication import ApplicationOffer, RemoteApplication - from .unit import Unit - -log = logging.getLogger(__name__) - - -class _Observer: - """Wrapper around an observer callable. - - This wrapper allows filter criteria to be associated with the - callable so that it's only called for changes that meet the criteria. - - """ - - def __init__(self, callable_, entity_type, action, entity_id, predicate): - self.callable_ = callable_ - self.entity_type = entity_type - self.action = action - self.entity_id = entity_id - self.predicate = predicate - if self.entity_id: - self.entity_id = str(self.entity_id) - if not self.entity_id.startswith("^"): - self.entity_id = "^" + self.entity_id - if not self.entity_id.endswith("$"): - self.entity_id += "$" - - async def __call__(self, delta, old, new, model): - await self.callable_(delta, old, new, model) - - def cares_about(self, delta): - """Return True if this observer "cares about" (i.e. wants to be - called) for a this delta. - - """ - return not ( - ( - self.entity_id - and delta.get_id() - and not re.match(self.entity_id, str(delta.get_id())) - ) - or (self.entity_type and self.entity_type != delta.entity) - or (self.action and self.action != delta.type) - or (self.predicate and not self.predicate(delta)) - ) - - -class ModelObserver: - """Base class for creating observers that react to changes in a model.""" - - async def __call__(self, delta, old, new, model): - handler_name = f"on_{delta.entity}_{delta.type}" - method = getattr(self, handler_name, self.on_change) - await method(delta, old, new, model) - - async def on_change(self, delta, old, new, model): - """Generic model-change handler. - - This should be overridden in a subclass. - - :param delta: :class:`juju.client.overrides.Delta` - :param old: :class:`juju.model.ModelEntity` - :param new: :class:`juju.model.ModelEntity` - :param model: :class:`juju.model.Model` - - """ - pass - - -class ModelState: - """Holds the state of the model, including the delta history of all - entities in the model. - - """ - - def __init__(self, model): - self.model = model - self.state = dict() - - @overload - def _live_entity_map( - self, entity_type: Literal["application"] - ) -> dict[str, Application]: ... - - @overload - def _live_entity_map( - self, entity_type: Literal["applicationOffer"] - ) -> dict[str, ApplicationOffer]: ... - - @overload - def _live_entity_map( - self, entity_type: Literal["machine"] - ) -> dict[str, Machine]: ... - - @overload - def _live_entity_map( - self, entity_type: Literal["relation"] - ) -> dict[str, Relation]: ... - - @overload - def _live_entity_map( - self, entity_type: Literal["remoteApplication"] - ) -> dict[str, RemoteApplication]: ... - - @overload - def _live_entity_map(self, entity_type: Literal["unit"]) -> dict[str, Unit]: ... - - def _live_entity_map(self, entity_type: str) -> Mapping[str, ModelEntity]: - """Return an id:Entity map of all the living entities of - type ``entity_type``. - - """ - return { - entity_id: self.get_entity(entity_type, entity_id) - for entity_id, history in self.state.get(entity_type, {}).items() - if history[-1] is not None - } - - @property - def applications(self) -> dict[str, Application]: - """Return a map of application-name:Application for all applications - currently in the model. - - """ - return self._live_entity_map("application") - - @property - def remote_applications(self) -> dict[str, RemoteApplication]: - """Return a map of application-name:Application for all remote - applications currently in the model. - - """ - return self._live_entity_map("remoteApplication") - - @property - def application_offers(self) -> dict[str, ApplicationOffer]: - """Return a map of application-name:Application for all applications - offers currently in the model. - """ - return self._live_entity_map("applicationOffer") - - @property - def machines(self) -> dict[str, Machine]: - """Return a map of machine-id:Machine for all machines currently in - the model. - - """ - return self._live_entity_map("machine") - - @property - def units(self) -> dict[str, Unit]: - """Return a map of unit-id:Unit for all units currently in - the model. - - """ - return self._live_entity_map("unit") - - @property - def subordinate_units(self) -> dict[str, Unit]: - """Return a map of unit-id:Unit for all subordinate units""" - return {u_name: u for u_name, u in self.units.items() if u.is_subordinate} - - @property - def relations(self) -> dict[str, Relation]: - """Return a map of relation-id:Relation for all relations currently in - the model. - - """ - return self._live_entity_map("relation") - - def entity_history(self, entity_type, entity_id): - """Return the history deque for an entity.""" - return self.state[entity_type][entity_id] - - def entity_data(self, entity_type, entity_id, history_index): - """Return the data dict for an entity at a specific index of its - history. - - """ - return self.entity_history(entity_type, entity_id)[history_index] - - def apply_delta(self, delta): - """Apply delta to our state and return a copy of the - affected object as it was before and after the update, e.g.: - - old_obj, new_obj = self.apply_delta(delta) - - old_obj may be None if the delta is for the creation of a new object, - e.g. a new application or unit is deployed. - - new_obj will never be None, but may be dead (new_obj.dead == True) - if the object was deleted as a result of the delta being applied. - - """ - history = self.state.setdefault(delta.entity, {}).setdefault( - delta.get_id(), collections.deque() - ) - - history.append(delta.data) - if delta.type == "remove": - history.append(None) - - entity = self.get_entity(delta.entity, delta.get_id()) - return entity.previous(), entity - - def get_entity( - self, entity_type, entity_id, history_index=-1, connected=True - ) -> ModelEntity | None: - """Return an object instance for the given entity_type and id. - - By default the object state matches the most recent state from - Juju. To get an instance of the object in an older state, pass - history_index, an index into the history deque for the entity. - - """ - if history_index < 0 and history_index != -1: - history_index += len(self.entity_history(entity_type, entity_id)) - if history_index < 0: - return None - - try: - self.entity_data(entity_type, entity_id, history_index) - except IndexError: - return None - - entity_class = get_entity_class(entity_type) - return entity_class( - entity_id, self.model, history_index=history_index, connected=connected - ) - - -class ModelEntity: - """An object in the Model tree""" - - entity_id: str - model: Model - _history_index: int - connected: bool - connection: connection.Connection - _status: str - - def __init__( - self, - entity_id: str, - model: Model, - history_index: int = -1, - connected: bool = True, - ): - """Initialize a new entity - - :param entity_id str: The unique id of the object in the model - :param model: The model instance in whose object tree this - entity resides - :history_index int: The index of this object's state in the model's - history deque for this entity - :connected bool: Flag indicating whether this object gets live updates - from the model. - - """ - self.entity_id = entity_id - self.model = model - self._history_index = history_index - self.connected = connected - self.connection = model.connection() - self._status = "unknown" - - def __repr__(self): - return f'<{type(self).__name__} entity_id="{self.entity_id}">' - - def __getattr__(self, name: str) -> Any: - """Fetch object attributes from the underlying data dict held in the - model. - - """ - try: - return self.safe_data[name] - except KeyError: - name = name.replace("_", "-") - if name in self.safe_data: - return self.safe_data[name] - else: - raise - - def __bool__(self): - return bool(self.data) - - def on_change(self, callable_): - """Add a change observer to this entity.""" - self.model.add_observer(callable_, self.entity_type, "change", self.entity_id) - - def on_remove(self, callable_): - """Add a remove observer to this entity.""" - self.model.add_observer(callable_, self.entity_type, "remove", self.entity_id) - - @property - def entity_type(self): - """A string identifying the entity type of this object, e.g. - 'application' or 'unit', etc. - - """ - # Allow the overriding of entity names from the type instead of from - # the class name. Useful because Model and ModelInfo clash and we really - # want ModelInfo to be called Model. - if hasattr(self.__class__, "type_name_override") and callable( - self.__class__.type_name_override - ): - return self.__class__.type_name_override() - - def first_lower(s): - if len(s) == 0: - return s - else: - return s[0].lower() + s[1:] - - return first_lower(self.__class__.__name__) - - @property - def current(self): - """Return True if this object represents the current state of the - entity in the underlying model. - - This will be True except when the object represents an entity at a - non-latest state in history, e.g. if the object was obtained by calling - .previous() on another object. - - """ - return self._history_index == -1 - - @property - def dead(self): - """Returns True if this entity no longer exists in the underlying - model. - - """ - return ( - self.data is None - or self.model.state.entity_data(self.entity_type, self.entity_id, -1) - is None - ) - - @property - def alive(self): - """Returns True if this entity still exists in the underlying - model. - - """ - return not self.dead - - @property - def data(self): - """The data dictionary for this entity.""" - return self.model.state.entity_data( - self.entity_type, self.entity_id, self._history_index - ) - - @property - def safe_data(self): - """The data dictionary for this entity. - - If this `ModelEntity` points to the dead state, it will - raise `DeadEntityException`. - - """ - if self.data is None: - raise DeadEntityException( - f"Entity {self.entity_type}:{self.entity_id} is dead - its attributes can no longer be " - "accessed. Use the .previous() method on this object to get " - "a copy of the object at its previous state." - ) - return self.data - - def previous(self): - """Return a copy of this object as was at its previous state in - history. - - Returns None if this object is new (and therefore has no history). - - The returned object is always "disconnected", i.e. does not receive - live updates. - - """ - return self.model.state.get_entity( - self.entity_type, self.entity_id, self._history_index - 1, connected=False - ) - - def next(self): - """Return a copy of this object at its next state in - history. - - Returns None if this object is already the latest. - - The returned object is "disconnected", i.e. does not receive - live updates, unless it is current (latest). - - """ - if self._history_index == -1: - return None - - new_index = self._history_index + 1 - connected = ( - new_index - == len(self.model.state.entity_history(self.entity_type, self.entity_id)) - - 1 - ) - return self.model.state.get_entity( - self.entity_type, - self.entity_id, - self._history_index - 1, - connected=connected, - ) - - def latest(self): - """Return a copy of this object at its current state in the model. - - Returns self if this object is already the latest. - - The returned object is always "connected", i.e. receives - live updates from the model. - - """ - if self._history_index == -1: - return self - - return self.model.state.get_entity(self.entity_type, self.entity_id) - - -class DeployTypeResult: - """DeployTypeResult represents the result of a deployment type after a - resolution. - """ - - def __init__(self, identifier, origin, app_name, is_local=False, is_bundle=False): - self.identifier = identifier - self.origin = origin - self.app_name = app_name - self.is_local = is_local - self.is_bundle = is_bundle - - -class LocalDeployType: - """LocalDeployType deals with local only deployments.""" - - async def resolve( - self, - charm_path, - architecture, - app_name=None, - channel=None, - series=None, - revision=None, - entity_url=None, - force=False, - model_conf=None, - ): - """Resolve attempts to resolve a local charm or bundle using the url - and architecture. If information is missing, it will attempt to backfill - that information, before sending the result back. - - -- revision flag is ignored for local charms - """ - entity_path = Path(charm_path) - if entity_path.suffix == ".yaml": - bundle_path = entity_path - else: - bundle_path = entity_path / "bundle.yaml" - - origin = client.CharmOrigin(source="local", architecture=architecture) - if not (entity_path.is_dir() or entity_path.is_file()): - raise JujuError(f"{entity_url} path not found") - - is_bundle = bundle_path.exists() - - if app_name is None: - if is_bundle: - bundle_with_overlays = [ - b for b in yaml.safe_load_all(bundle_path.read_text()) - ] - app_name = bundle_with_overlays[0].get("name", "") - else: - app_name = utils.get_local_charm_metadata(entity_path)["name"] - - return DeployTypeResult( - identifier=charm_path, - origin=origin, - app_name=app_name, - is_local=True, - is_bundle=is_bundle, - ) - - -class CharmhubDeployType: - """CharmhubDeployType defines a class for resolving and deploying charmhub - charms and bundles. - """ - - def __init__(self, charm_resolver): - self.charm_resolver = charm_resolver - - async def resolve( - self, - url, - architecture, - app_name=None, - channel=None, - series=None, - revision=None, - entity_url=None, - force=False, - model_conf=None, - ): - """Resolve attempts to resolve charmhub charms or bundles. A request to - the charmhub API is required to correctly determine the charm url and - underlying origin. - """ - if revision and not channel: - raise JujuError( - "specifying a revision requires a channel for future upgrades. Please use --channel" - ) - - ch = Channel("latest", "stable") - if channel is not None: - ch = Channel.parse(channel).normalize() - - base = client.Base() - if series: - base.channel = ch.normalize().compute_base_channel(series=series) - base.name = "ubuntu" - - origin = client.CharmOrigin( - source=Source.CHARM_HUB.value, - architecture=architecture, - risk=ch.risk, - track=ch.track, - base=base, - revision=revision, - ) - - charm_url, origin = await self.charm_resolver( - url, origin, force, series, model_conf - ) - - is_bundle = origin.type_ == "bundle" - if is_bundle and revision and channel: - raise JujuError( - "revision and channel are mutually exclusive when deploying a bundle. Please choose one." - ) - - if app_name is None: - app_name = charm_url.name - - return DeployTypeResult( - identifier=str(charm_url), - app_name=app_name, - origin=origin, - is_bundle=is_bundle, - ) - - -class Model: - """The main API for interacting with a Juju model.""" - - connector: connector.Connector - state: ModelState - - def __init__( - self, - max_frame_size=None, - bakery_client=None, - jujudata=None, - ): - """Instantiate a new Model. - - The connect method will need to be called before this - object can be used for anything interesting. - - If jujudata is None, jujudata.FileJujuData will be used. - - :param max_frame_size: See - `juju.client.connection.Connection.MAX_FRAME_SIZE` - :param bakery_client httpbakery.Client: The bakery client to use - for macaroon authorization. - :param jujudata JujuData: The source for current controller information - """ - self._connector = connector.Connector( - max_frame_size=max_frame_size, - bakery_client=bakery_client, - jujudata=jujudata, - ) - self._observers = weakref.WeakValueDictionary() - self.state = ModelState(self) - self._info = None - self._mode = None - self._watch_stopping = jasyncio.Event() - self._watch_stopped = jasyncio.Event() - self._watch_received = jasyncio.Event() - self._watch_stopped.set() - - self._charmhub = CharmHub(self) - - self.deploy_types = { - Schema.LOCAL: LocalDeployType(), - Schema.CHARM_HUB: CharmhubDeployType(self._resolve_charm), - } - - def is_connected(self): - """Reports whether the Model is currently connected.""" - return self._connector.is_connected() - - def connection(self) -> connection.Connection: - """Return the current Connection object. It raises an exception - if the Model is disconnected - """ - return self._connector.connection() - - async def get_controller(self): - """Return a Controller instance for the currently connected model. - :return Controller: - """ - from juju.controller import Controller - - controller = Controller(jujudata=self._connector.jujudata) - kwargs = self.connection().connect_params() - kwargs.pop("uuid") - await controller._connect_direct(**kwargs) - return controller - - async def __aenter__(self): - await self.connect() - return self - - async def __aexit__(self, exc_type, exc, tb): - await self.disconnect() - - async def connect(self, *args, **kwargs): - """Connect to a juju model. - - This supports two calling conventions: - - The model and (optionally) authentication information can be taken - from the data files created by the Juju CLI. This convention will - be used if a ``model_name`` is specified, or if the ``endpoint`` - and ``uuid`` are not. - - Otherwise, all of the ``endpoint``, ``uuid``, and authentication - information (``username`` and ``password``, or ``bakery_client`` and/or - ``macaroons``) are required. - - If a single positional argument is given, it will be assumed to be - the ``model_name``. Otherwise, the first positional argument, if any, - must be the ``endpoint``. - - Available parameters are: - - :param model_name: Format [controller:][user/]model - :param str endpoint: The hostname:port of the controller to connect to. - :param str uuid: The model UUID to connect to. - :param str username: The username for controller-local users (or None - to use macaroon-based login.) - :param str password: The password for controller-local users. - :param str cacert: The CA certificate of the controller - (PEM formatted). - :param httpbakery.Client bakery_client: The macaroon bakery client to - to use when performing macaroon-based login. Macaroon tokens - acquired when logging will be saved to bakery_client.cookies. - If this is None, a default bakery_client will be used. - :param list macaroons: List of macaroons to load into the - ``bakery_client``. - :param int max_frame_size: The maximum websocket frame size to allow. - :param specified_facades: (deprecated) overwrite the facades with a series of - specified facades. - """ - is_debug_log_conn = "debug_log_conn" in kwargs - if not is_debug_log_conn: - await self.disconnect() - model_name = model_uuid = None - if "endpoint" not in kwargs and len(args) < 2: - # Then we're using the model_name to pick the model - if args and "model_name" in kwargs: - raise TypeError("connect() got multiple values for model_name") - elif args: - model_name = args[0] - else: - model_name = kwargs.pop("model_name", None) - model_uuid = await self._connector.connect_model(model_name, **kwargs) - else: - # Then we're using the endpoint to pick the model - if "model_name" in kwargs: - raise TypeError("connect() got values for both model_name and endpoint") - if args and "endpoint" in kwargs: - raise TypeError("connect() got multiple values for endpoint") - if len(args) < 2 and "uuid" not in kwargs: - raise TypeError("connect() missing value for uuid") - has_userpass = len(args) >= 4 or {"username", "password"}.issubset(kwargs) - has_macaroons = len(args) >= 6 or not { - "bakery_client", - "macaroons", - }.isdisjoint(kwargs) - if not (has_userpass or has_macaroons): - raise TypeError("connect() missing auth params") - arg_names = [ - "endpoint", - "uuid", - "username", - "password", - "cacert", - "bakery_client", - "macaroons", - "max_frame_size", - ] - for i, arg in enumerate(args): - kwargs[arg_names[i]] = arg - model_uuid = kwargs["uuid"] - if not {"endpoint", "uuid"}.issubset(kwargs): - raise ValueError( - "endpoint and uuid are required if model_name not given" - ) - if not ( - {"username", "password"}.issubset(kwargs) - or {"bakery_client", "macaroons"}.intersection(kwargs) - ): - raise ValueError( - "Authentication parameters are required if model_name not given" - ) - await self._connector.connect(**kwargs) - if not is_debug_log_conn: - await self._after_connect(model_name, model_uuid) - - async def connect_model(self, model_name, **kwargs): - """.. deprecated:: 0.6.2 - Use ``connect(model_name=model_name)`` instead. - """ - return await self.connect(model_name=model_name, **kwargs) - - async def connect_current(self): - """.. deprecated:: 0.6.2 - Use ``connect()`` instead. - """ - return await self.connect() - - @deprecated("Model.connect_to() is deprecated and will be removed soon") - async def connect_to(self, connection): - conn_params = connection.connect_params() - await self._connect_direct(**conn_params) - - async def _connect_direct(self, **kwargs): - if self._info: - uuid = self._info.uuid - elif "uuid" in kwargs: - uuid = kwargs["uuid"] - else: - raise PylibjujuError("Unable to find uuid for the model") - await self.disconnect() - await self._connector.connect(**kwargs) - await self._after_connect(model_uuid=uuid) - - async def _after_connect(self, model_name=None, model_uuid=None): - self._watch() - - # Wait for the first packet of data from the AllWatcher, - # which contains all information on the model. - # TODO this means that we can't do anything until - # we've received all the model data, which might be - # a whole load of unneeded data if all the client wants - # to do is make one RPC call. - async def watch_received_waiter(): - await self._watch_received.wait() - - waiter = jasyncio.create_task(watch_received_waiter()) - - # If we just wait for the _watch_received event and the _all_watcher task - # fails (e.g. because API fails like migration is in progress), then - # we'll hang because the _watch_received will never be set - # Instead, we watch for two things, 1) _watch_received, 2) _all_watcher done - # If _all_watcher is done before the _watch_received, then we should see - # (and raise) an exception coming from the _all_watcher - # Otherwise (i.e. _watch_received is set), then we're good to go - done, _pending = await jasyncio.wait( - [waiter, self._watcher_task], return_when=jasyncio.FIRST_COMPLETED - ) - if self._watcher_task in done: - # Cancel the _watch_received.wait - waiter.cancel() - # If there's no exception, then why did the _all_watcher broke its loop? - if not self._watcher_task.exception(): - raise JujuError( - "AllWatcher task is finished abruptly without an exception." - ) - raise self._watcher_task.exception() - - if self._info is None: - # TODO (cderici): See if this can be optimized away, or at least - # be done lazily (i.e. not every time after_connect, but whenever - # self.info is needed -- which here can be bypassed if model_uuid - # is known) - async with ConnectedController(self.connection()) as contr: - self._info = await contr.get_model_info(model_name, model_uuid) - log.debug("Got ModelInfo: %s", vars(self.info)) - - self.uuid = self.info.uuid - - async def disconnect(self): - """Shut down the watcher task and close websockets.""" - if not self._watch_stopped.is_set(): - log.debug("Stopping watcher task") - self._watch_stopping.set() - # If the _all_watcher task is finished, - # check to see an exception, if yes, raise, - # otherwise we should see the _watch_stopped - # flag is set - if self._watcher_task.done() and self._watcher_task.exception(): - raise self._watcher_task.exception() - await self._watch_stopped.wait() - self._watch_stopping.clear() - - if self.is_connected(): - await self._connector.disconnect(entity="model") - self._info = None - - async def add_local_charm_dir(self, charm_dir, series): - """Upload a local charm to the model. - - This will automatically generate an archive from - the charm dir. - - :param charm_dir: Path to the charm directory - :param series: Charm series - - """ - charm_dir = Path(charm_dir) - if charm_dir.suffix == ".charm": - fn = charm_dir - else: - # FIXME this is probably a bug, the file is never removed - fn = tempfile.NamedTemporaryFile().name # noqa: SIM115 - CharmArchiveGenerator(str(charm_dir)).make_archive(fn) - with open(str(fn), "rb") as fh: - func = partial(self.add_local_charm, fh, series, os.stat(str(fn)).st_size) - loop = jasyncio.get_running_loop() - charm_url = await loop.run_in_executor(None, func) - - log.debug("Uploaded local charm: %s -> %s", charm_dir, charm_url) - return charm_url - - def add_local_charm(self, charm_file, series="", size=None): - """Upload a local charm archive to the model. - - Returns the 'local:...' url that should be used to deploy the charm. - - :param charm_file: Path to charm zip archive - :param series: Charm series - :param size: Size of the archive, in bytes - :return str: 'local:...' url for deploying the charm - :raises: :class:`JujuError` if the upload fails - - Uses an https endpoint at the same host:port as the wss. - Supports large file uploads. - - .. warning:: - - This method will block. Consider using :meth:`add_local_charm_dir` - instead. - - """ - conn, headers, path_prefix = self.connection().https_connection() - path = "%s/charms?series=%s" % (path_prefix, series) - headers["Content-Type"] = "application/zip" - if size: - headers["Content-Length"] = size - conn.request("POST", path, charm_file, headers) - response = conn.getresponse() - result = response.read().decode() - if not response.status == 200: - raise JujuError(result) - result = json.loads(result) - return result["charm-url"] - - def all_units_idle(self): - """Return True if all units are idle.""" - for unit in self.units.values(): - unit_status = unit.data["agent-status"]["current"] - if unit_status != "idle": - return False - return True - - async def reset(self, force=False): - """Reset the model to a clean state. - - :param bool force: Force-terminate machines. - - This returns only after the model has reached a clean state. "Clean" - means no applications or machines exist in the model. - - """ - log.debug("Resetting model") - for app in self.applications.values(): - await app.destroy() - await self.block_until(lambda: len(self.applications) == 0) - for machine in self.machines.values(): - await machine.destroy(force=force) - await self.block_until(lambda: len(self.machines) == 0) - - async def create_storage_pool(self, name, provider_type, attributes=""): - """Create or define a storage pool. - - :param str name: a pool name - :param str provider_type: provider type (defaults to "kubernetes" for - Kubernetes models) - :param str attributes: attributes for configuration as space-separated pairs, - e.g. tags, size, path, etc. - :return: - """ - _attrs = [splt.split("=") for splt in attributes.split()] - - storage_facade = client.StorageFacade.from_connection(self.connection()) - return await storage_facade.CreatePool( - pools=[ - client.StoragePool( - name=name, provider=provider_type, attrs=dict(_attrs) - ) - ] - ) - - async def remove_storage_pool(self, name): - """Remove an existing storage pool. - - :param str name: - :return: - """ - storage_facade = client.StorageFacade.from_connection(self.connection()) - return await storage_facade.RemovePool( - pools=[client.StoragePoolDeleteArg(name)] - ) - - async def update_storage_pool(self, name, attributes=""): - """Update storage pool attributes. - - :param name: - :param attributes: "key=value key=value ..." - :return: - """ - _attrs = dict([splt.split("=") for splt in attributes.split()]) - if len(_attrs) == 0: - raise JujuError("Expected at least one attribute to update") - - storage_facade = client.StorageFacade.from_connection(self.connection()) - return await storage_facade.UpdatePool( - pools=[ - client.StoragePool( - name=name, - attrs=_attrs, - ) - ] - ) - - async def list_storage(self, filesystem=False, volume=False): - """Lists storage details. - - :param bool filesystem: List filesystem storage - :param bool volume: List volume storage - :return: - """ - storage_facade = client.StorageFacade.from_connection(self.connection()) - - if filesystem and volume: - raise JujuError("--filesystem and --volume can not be used together") - if filesystem: - _res = await storage_facade.ListFilesystems( - filters=[client.FilesystemFilter()] - ) - elif volume: - _res = await storage_facade.ListVolumes(filters=[client.VolumeFilter()]) - else: - _res = await storage_facade.ListStorageDetails( - filters=[client.StorageFilter()] - ) - - err = _res.results[0].error - res = _res.results[0].result - - if err is not None: - raise JujuError(err.message) - - return [details.serialize() for details in res] - - async def show_storage_details(self, *storage_ids): - """Shows storage instance information. - - :param []str storage_ids: - :return: - """ - if not storage_ids: - raise JujuError("Expected at least one storage ID") - - storage_facade = client.StorageFacade.from_connection(self.connection()) - res = await storage_facade.StorageDetails( - entities=[client.Entity(tag.storage(s)) for s in storage_ids] - ) - return [s.result.serialize() for s in res.results] - - async def list_storage_pools(self): - """List storage pools. - - :return: - """ - # TODO (cderici): Filter on pool type, name. - storage_facade = client.StorageFacade.from_connection(self.connection()) - res = await storage_facade.ListPools(filters=[client.StoragePoolFilter()]) - err = res.results[0].error - if err: - raise JujuError(err.message) - return [p.serialize() for p in res.results[0].storage_pools] - - async def remove_storage(self, *storage_ids, force=False, destroy_storage=False): - """Removes storage from the model. - - :param bool force: Remove storage even if it is currently attached - :param bool destroy_storage: Remove the storage and destroy it - :param []str storage_ids: - :return: - """ - if not storage_ids: - raise JujuError("Expected at least one storage ID") - - storage_facade = client.StorageFacade.from_connection(self.connection()) - ret = await storage_facade.Remove( - storage=[ - client.RemoveStorageInstance( - destroy_storage=destroy_storage, - force=force, - tag=s, - ) - for s in storage_ids - ] - ) - if ret.results[0].error: - raise JujuError(ret.results[0].error.message) - - async def remove_application( - self, - app_name, - block_until_done=False, - force=False, - destroy_storage=False, - no_wait=False, - timeout=None, - ): - """Removes the given application from the model. - - :param str app_name: Name of the application - :param bool force: Completely remove an application and all its dependencies. (=false) - :param bool destroy_storage: Destroy storage attached to application unit. (=false) - :param bool no_wait: Rush through application removal without waiting for each individual step to complete (=false) - :param bool block_until_done: Ensure the app is removed from the - model when returned - :param int timeout: Raise asyncio.exceptions.TimeoutError if the application is not removed - within the timeout period. - """ - if app_name not in self.applications: - raise JujuError( - "Given application doesn't seem to appear in the\ - model: %s\nCurrent applications are: %s" - % (app_name, self.applications) - ) - await self.applications[app_name].remove( - destroy_storage=destroy_storage, - force=force, - no_wait=no_wait, - ) - if block_until_done: - await self.block_until( - lambda: app_name not in self.applications, timeout=timeout - ) - - async def block_until(self, *conditions, timeout=None, wait_period=0.5): - """Return only after all conditions are true. - - Raises `websockets.ConnectionClosed` if disconnected. - """ - - def _disconnected(): - return not (self.is_connected() and self.connection().is_open) - - def done(): - return _disconnected() or all(c() for c in conditions) - - await utils.block_until(done, timeout=timeout, wait_period=wait_period) - if _disconnected(): - raise websockets.ConnectionClosed(1006, "no reason") - - @property - def tag(self): - return tag.model(self.uuid) - - @property - def applications(self) -> dict[str, Application]: - """Return a map of application-name:Application for all applications - currently in the model. - - """ - return self.state.applications - - @property - def remote_applications(self) -> dict[str, RemoteApplication]: - """Return a map of application-name:Application for all remote - applications currently in the model. - - """ - return self.state.remote_applications - - @property - def application_offers(self) -> dict[str, ApplicationOffer]: - """Return a map of application-name:Application for all applications - offers currently in the model. - """ - return self.state.application_offers - - @property - def machines(self) -> dict[str, Machine]: - """Return a map of machine-id:Machine for all machines currently in - the model. - - """ - return self.state.machines - - @property - def units(self) -> dict[str, Unit]: - """Return a map of unit-id:Unit for all units currently in - the model. - - """ - return self.state.units - - @property - def subordinate_units(self) -> dict[str, Unit]: - """Return a map of unit-id:Unit for all subordiante units currently in - the model. - - """ - return self.state.subordinate_units - - @property - def relations(self) -> list[Relation]: - """Return a list of all Relations currently in the model.""" - return list(self.state.relations.values()) - - @property - def charmhub(self): - """Return a charmhub repository for requesting charm information using - the charm-hub-url model config. - - """ - return self._charmhub - - @property - def name(self): - """Return the name of this model""" - if self._info is None: - raise JujuModelError("Model is not connected") - return self._info.name - - @property - def info(self) -> ModelInfo: - """Return the cached client.ModelInfo object for this Model. - - If Model.get_info() has not been called, this will raise an error. - """ - if not self.is_connected(): - raise JujuModelError("Model is not connected") - - assert self._info is not None - return self._info - - @property - def strict_mode(self): - return self._mode is not None and "strict" in self._mode - - def add_observer( - self, callable_, entity_type=None, action=None, entity_id=None, predicate=None - ): - """Register an "on-model-change" callback - - Once the model is connected, ``callable_`` - will be called each time the model changes. ``callable_`` should - be Awaitable and accept the following positional arguments: - - delta - An instance of :class:`juju.delta.EntityDelta` - containing the raw delta data recv'd from the Juju - websocket. - - old_obj - If the delta modifies an existing object in the model, - old_obj will be a copy of that object, as it was before the - delta was applied. Will be None if the delta creates a new - entity in the model. - - new_obj - A copy of the new or updated object, after the delta - is applied. Will be None if the delta removes an entity - from the model. - - model - The :class:`Model` itself. - - Events for which ``callable_`` is called can be specified by passing - entity_type, action, and/or entitiy_id filter criteria, e.g.:: - - add_observer( - myfunc, - entity_type='application', action='add', entity_id='ubuntu') - - For more complex filtering conditions, pass a predicate function. It - will be called with a delta as its only argument. If the predicate - function returns True, the ``callable_`` will be called. - - """ - observer = _Observer(callable_, entity_type, action, entity_id, predicate) - self._observers[observer] = callable_ - - def _watch(self): - """Start an asynchronous watch against this model. - - See :meth:`add_observer` to register an onchange callback. - - """ - - def _post_step(obj): - # Once we get the model, ensure we're running in the correct state - # as a post step. - if isinstance(obj, ModelInfo) and obj.safe_data is not None: - model_config = obj.safe_data["config"] - if "mode" in model_config: - self._mode = model_config["mode"] - - async def _all_watcher(): - # First attempt to get the model config so we know what mode the - # library should be running in. - model_config = await self.get_config() - if "mode" in model_config: - self._mode = model_config["mode"]["value"] - - try: - allwatcher = client.AllWatcherFacade.from_connection(self.connection()) - while not self._watch_stopping.is_set(): - try: - results = await utils.run_with_interrupt( - allwatcher.Next(), self._watch_stopping, log=log - ) - except JujuAPIError as e: - if "watcher was stopped" not in str(e): - raise - if self._watch_stopping.is_set(): - # this shouldn't ever actually happen, because - # the event should trigger before the controller - # has a chance to tell us the watcher is stopped - # but handle it gracefully, just in case - break - # controller stopped our watcher for some reason - # but we're not actually stopping, so just restart it - log.warning("Watcher: watcher stopped, restarting") - del allwatcher.Id - continue - except websockets.ConnectionClosed: - if self.connection().monitor.status == connection.Monitor.ERROR: - # closed unexpectedly, try to reopen - log.warning("Watcher: connection closed, reopening") - await self.connection().reconnect() - if ( - self.connection().monitor.status - != connection.Monitor.CONNECTED - ): - # reconnect failed; abort and shutdown - log.error( - "Watcher: automatic reconnect " - "failed; stopping watcher" - ) - break - del allwatcher.Id - continue - else: - # closed on request, go ahead and shutdown - break - if self._watch_stopping.is_set(): - try: - await allwatcher.Stop() - except websockets.ConnectionClosed: - pass # can't stop on a closed conn - break - for delta in results.deltas: - entity = None - try: - entity = get_entity_delta(delta) - except KeyError: - if self.strict_mode: - raise JujuError(f"unknown delta type '{delta.entity}'") - - if not self.strict_mode and entity is None: - continue - old_obj, new_obj = self.state.apply_delta(entity) - await self._notify_observers(entity, old_obj, new_obj) - # Post step ensure that we can handle any settings - # that need to be correctly set as a post step. - _post_step(new_obj) - self._watch_received.set() - except CancelledError: - pass - except Exception: - log.exception("Error in watcher") - raise - finally: - self._watch_stopped.set() - - log.debug("Starting watcher task") - self._watch_received.clear() - self._watch_stopping.clear() - self._watch_stopped.clear() - self._watcher_task = jasyncio.create_task(_all_watcher()) - - async def _notify_observers(self, delta, old_obj, new_obj): - """Call observing callbacks, notifying them of a change in model state - - :param delta: The raw change from the watcher - (:class:`juju.client.overrides.Delta`) - :param old_obj: The object in the model that this delta updates. - May be None. - :param new_obj: The object in the model that is created or updated - by applying this delta. - - """ - if new_obj and not old_obj: - delta.type = "add" - - log.debug("Model changed: %s %s %s", delta.entity, delta.type, delta.get_id()) - - for o in self._observers: - if o.cares_about(delta): - jasyncio.ensure_future(o(delta, old_obj, new_obj, self)) - - async def _wait(self, entity_type, entity_id, action, predicate=None): - """Block the calling routine until a given action has happened to the - given entity - - :param entity_type: The entity's type. - :param entity_id: The entity's id. - :param action: the type of action (e.g., 'add', 'change', or 'remove') - :param predicate: optional callable that must take as an - argument a delta, and must return a boolean, indicating - whether the delta contains the specific action we're looking - for. For example, you might check to see whether a 'change' - has a 'completed' status. See the _Observer class for details. - - """ - q = jasyncio.Queue() - - async def callback(delta, old, new, model): - await q.put(delta.get_id()) - - self.add_observer(callback, entity_type, action, entity_id, predicate) - entity_id = await q.get() - # object might not be in the entity_map if we were waiting for a - # 'remove' action - return self.state._live_entity_map(entity_type).get(entity_id) - - async def _wait_for_new(self, entity_type, entity_id): - """Wait for a new object to appear in the Model and return it. - - Waits for an object of type ``entity_type`` with id ``entity_id`` - to appear in the model. This is similar to watching for the - object using ``block_until``, but uses the watcher rather than - polling. - - """ - # if the entity is already in the model, just return it - if entity_id in self.state._live_entity_map(entity_type): - return self.state._live_entity_map(entity_type)[entity_id] - return await self._wait(entity_type, entity_id, None) - - async def wait_for_action(self, action_id): - """Given an action, wait for it to complete.""" - if action_id.startswith("action-"): - # if we've been passed action.tag, transform it into the - # id that the api deltas will use. - action_id = action_id[7:] - - def predicate(delta): - return delta.data["status"] in ("completed", "failed") - - return await self._wait("action", action_id, None, predicate) - - async def get_annotations(self): - """Get annotations on this model. - - :return dict: The annotations for this model - """ - return await _get_annotations(self.tag, self.connection()) - - async def set_annotations(self, annotations): - """Set annotations on this model. - - :param annotations map[string]string: the annotations as key/value - pairs. - - """ - return await _set_annotations(self.tag, annotations, self.connection()) - - async def add_machine( - self, spec: str | None = None, constraints=None, disks=None, series=None - ): - """Start a new, empty machine and optionally a container, or add a - container to a machine. - - :param str spec: Machine specification - Examples:: - - (None) - starts a new machine - 'lxd' - starts a new machine with one lxd container - 'lxd:4' - starts a new lxd container on machine 4 - 'ssh:user@10.10.0.3:/path/to/private/key' - manually provision - a machine with ssh and the private key used for authentication - 'zone=us-east-1a' - starts a machine in zone us-east-1s on AWS - 'maas2.name' - acquire machine maas2.name on MAAS - - :param dict constraints: Machine constraints, which can contain the - the following keys:: - - arch : str - container : str - cores : int - cpu_power : int - instance_type : str - mem : int - root_disk : int - spaces : list(str) - tags : list(str) - virt_type : str - - Example:: - - constraints={ - 'mem': 256 * MB, - 'tags': ['virtual'], - } - - :param list disks: List of disk constraint dictionaries, which can - contain the following keys:: - - count : int - pool : str - size : int - - Example:: - - disks=[{ - 'pool': 'rootfs', - 'size': 10 * GB, - 'count': 1, - }] - - :param str series: Series, e.g. 'xenial' - - Supported container types are: lxd, kvm - - When deploying a container to an existing machine, constraints cannot - be used. - - """ - params = client.AddMachineParams() - - if spec: - if spec.startswith("ssh:"): - placement, target, private_key_path = spec.split(":") - user, host = target.split("@") - - prov = provisioner.SSHProvisioner( - host=host, - user=user, - private_key_path=private_key_path, - ) - - params = prov.provision_machine() - else: - placement = parse_placement(spec) - if placement: - params.placement = placement[0] - - params.jobs = ["JobHostUnits"] - - if constraints: - params.constraints = client.Value.from_json(constraints) - - if disks: - params.disks = [client.Constraints.from_json(o) for o in disks] - - if series: - params.series = series - - # Submit the request. - client_facade = client.MachineManagerFacade.from_connection(self.connection()) - results = await client_facade.AddMachines(params=[params]) - error = results.machines[0].error - if error: - raise ValueError("Error adding machine: %s" % error.message) - machine_id = results.machines[0].machine - - if spec and spec.startswith("ssh:"): - # Need to run this after AddMachines has been called, - # as we need the machine_id - await prov.install_agent( - self.connection(), - params.nonce, - machine_id, - ) - - log.debug("Added new machine %s", machine_id) - return await self._wait_for_new("machine", machine_id) - - async def add_relation(self, relation1, relation2): - """.. deprecated:: 2.9.9 - Use ``relate()`` instead - """ - return await self.relate(relation1, relation2) - - async def integrate(self, relation1, relation2): - """Add a relation between two applications. - - :param str relation1: '[:]' - :param str relation2: '[:]' - - """ - # attempt to validate any url that are passed in. - endpoints = [] - remote_endpoint = None - for ep in [relation1, relation2]: - try: - url = parse_offer_url(ep) - except OfferParseError: - pass - else: - if remote_endpoint is not None: - raise JujuError("move than one remote endpoints not supported") - remote_endpoint = url - endpoints.append(url.application) - continue - - try: - parse_local_endpoint(ep) - except OfferParseError: - raise - else: - endpoints.append(ep) - if len(endpoints) != 2: - raise JujuError("error validating one of the endpoints") - - facade_cls = client.ApplicationFacade - if remote_endpoint is not None: - if facade_cls.best_facade_version(self.connection()) < 5: - # old clients don't support cross model capability - raise JujuError( - f"cannot add relation to {remote_endpoint.string()}: remote endpoints not supported" - ) - - if remote_endpoint.has_empty_source(): - async with ConnectedController(self.connection()) as current: - remote_endpoint.source = current.controller_name - # consume the remote endpoint - await self.consume( - remote_endpoint.string(), application_alias=remote_endpoint.application - ) - - log.debug("Adding relation %s <-> %s", endpoints[0], endpoints[1]) - - def _find_relation(*specs): - for rel in self.relations: - if rel.matches(*specs): - return rel - return None - - app_facade = facade_cls.from_connection(self.connection()) - try: - result = await app_facade.AddRelation(endpoints=endpoints, via_cidrs=None) - except JujuAPIError as e: - if "relation already exists" not in e.message: - raise - rel = _find_relation(endpoints[0], endpoints[1]) - if rel: - return rel - raise JujuError( - f"Relation {endpoints[0]} {endpoints[1]} exists but not in model" - ) - - specs = [ - "{}:{}".format(app, data["name"]) for app, data in result.endpoints.items() - ] - - await self.block_until(lambda: _find_relation(*specs) is not None) - return _find_relation(*specs) - - async def relate(self, relation1, relation2): - """The relate function is deprecated in favor of integrate. - - The logic is the same. - """ - log.warning("relate is deprecated and will be removed. Use integrate instead.") - return await self.integrate(relation1, relation2) - - async def add_space(self, name, cidrs=None, public=True): - """Add a new network space. - - Adds a new space with the given name and associates the given - (optional) list of existing subnet CIDRs with it. - - :param str name: Name of the space - :param List[str] cidrs: Optional list of existing subnet CIDRs - """ - space_facade = client.SpacesFacade.from_connection(self.connection()) - spaces = [{"cidrs": cidrs, "space-tag": tag.space(name), "public": public}] - return await space_facade.CreateSpaces(spaces=spaces) - - async def add_ssh_key(self, user, key): - """Add a public SSH key to this model. - - :param str user: The username of the user - :param str key: The public ssh key - - """ - key_facade = client.KeyManagerFacade.from_connection(self.connection()) - return await key_facade.AddKeys(ssh_keys=[key], user=user) - - add_ssh_keys = add_ssh_key - - async def get_backups(self): - """Retrieve metadata for backups in this model. - - :return [dict]: List of metadata for the stored backups - """ - backups_facade = client.BackupsFacade.from_connection(self.connection()) - _backups_metadata = await backups_facade.List() - backups_metadata = _backups_metadata.serialize() - if "list" not in backups_metadata: - raise JujuAPIError("Unexpected response metadata : %s" % backups_metadata) - return backups_metadata["list"] - - async def create_backup(self, notes=None): - """Create a backup of this model. - - :param str note: A note to store with the backup - :param bool keep_copy: Keep a copy of the archive on the controller - :param bool no_download: Do not download the backup archive - :return str, dict: Filename for the downloaded archive file, Extra metadata for the created backup - - """ - backups_facade = client.BackupsFacade.from_connection(self.connection()) - results = await backups_facade.Create(notes=notes) - - if results is None: - raise JujuAPIError("unable to create a backup") - - backup_metadata = results.serialize() - - if "error" in backup_metadata: - raise JujuBackupError( - "unable to create a backup, got %s from Juju API" % backup_metadata - ) - - backup_id = backup_metadata["filename"] - - file_name = self.download_backup(backup_id) - - return file_name, backup_metadata - - async def debug_log( - self, - target=sys.stdout, - no_tail=False, - exclude_module=[], - include_module=[], - include=[], - level="", - limit=sys.maxsize, - lines=10, - exclude=[], - ): - """Get log messages for this model. - - :param bool no_tail: Stop after returning existing log messages - :param list exclude_module: Do not show log messages for these logging - modules - :param list include_module: Only show log messages for these logging - modules - :param list include: Only show log messages for these entities - :param str level: Log level to show, valid options are 'TRACE', - 'DEBUG', 'INFO', 'WARNING', 'ERROR, - :param int limit: Return this many of the most recent (possibly - filtered) lines are shown - :param int lines: Yield this many of the most recent lines, and keep - yielding - :param list exclude: Do not show log messages for these entities - - """ - if not self.is_connected(): - await self.connect() - - params = { - "no_tail": no_tail, - "exclude_module": exclude_module, - "include_module": include_module, - "include": include, - "level": level, - "limit": limit, - "lines": lines, - "exclude": exclude, - } - await self.connect(debug_log_conn=target, debug_log_params=params) - - async def deploy( - self, - entity_url, - application_name=None, - bind=None, - channel=None, - config=None, - constraints=None, - force=False, - num_units=1, - overlays=[], - base=None, - resources=None, - series=None, - revision=None, - storage: Mapping[str, str | StorageConstraintDict] | None = None, - to=None, - devices=None, - trust=False, - attach_storage=[], - ): - """Deploy a new service or bundle. - - :param str entity_url: Charm or bundle to deploy. Charm url or file path - :param str application_name: Name to give the service - :param dict bind: : pairs - :param str channel: Charm store channel from which to retrieve - the charm or bundle, e.g. 'edge' - :param dict config: Charm configuration dictionary - :param constraints: Service constraints - :type constraints: :class:`juju.Constraints` - :param bool force: Allow charm to be deployed to a machine running - an unsupported series - :param int num_units: Number of units to deploy - :param [] overlays: Bundles to overlay on the primary bundle, applied in order - :param str base: The base on which to deploy - :param dict resources: : pairs - :param str series: Series on which to deploy DEPRECATED: use --base (with Juju 3.1) - :param int revision: specifying a revision requires a channel for future upgrades for charms. - For bundles, revision and channel are mutually exclusive. - :param dict storage: optional storage constraints, in the form of `{label: constraint}`. - The label is a string specified by the charm, while the constraint is - a constraints.StorageConstraintsDict, or a string following - `the juju storage constraint directive format `_, - specifying the storage pool, number of volumes, and size of each volume. - :param to: Placement directive as a string. For example: - - '23' - place on machine 23 - 'lxd:7' - place in new lxd container on machine 7 - '24/lxd/3' - place in container 3 on machine 24 - - If None, a new machine is provisioned. - :param devices: charm device constraints - :param bool trust: Trust signifies that the charm should be deployed - with access to trusted credentials. Hooks run by the charm can access - cloud credentials and other trusted access credentials. - - :param str[] attach_storage: Existing storage to attach to the deployed unit - (not available on k8s models) - """ - if trust and (self.info.agent_version < client.Number.from_json("2.4.0")): - raise NotImplementedError( - f"trusted is not supported on model version {self.info.agent_version}" - ) - - if not all([isinstance(st, str) for st in attach_storage]): - raise JujuError( - f"Expected attach_storage to be a list of strings, given {attach_storage}" - ) - - # Ensure what we pass in, is a string. - entity = str(entity_url) - if is_local_charm(entity): - if entity.startswith("local:"): - entity = entity[6:] - architecture = await self._resolve_architecture() - schema = Schema.LOCAL - - else: - if client.CharmsFacade.best_facade_version(self.connection()) < 3: - url = URL.parse(entity, default_store=Schema.CHARM_STORE) - else: - url = URL.parse(entity) - entity = str(url) - - architecture = await self._resolve_architecture(url) - schema = url.schema - name = url.name - - if schema not in self.deploy_types: - raise JujuError(f"unknown deploy type {schema}, expected charmhub or local") - - model_conf = await self.get_config() - res = await self.deploy_types[schema].resolve( - entity, - architecture, - application_name, - channel, - series, - revision, - entity_url, - force, - model_conf, - ) - - if res.identifier is None: - raise JujuError(f"unknown charm or bundle {entity_url}") - identifier = res.identifier - - charm_series = series - charm_origin = res.origin - if base: - charm_origin.base = utils.parse_base_arg(base) - - server_side_deploy = False - - if res.is_bundle: - handler = BundleHandler(self, trusted=trust, forced=force) - await handler.fetch_plan(entity, charm_origin, overlays=overlays) - await handler.execute_plan() - extant_apps = {app for app in self.applications} - pending_apps = handler.applications - extant_apps - if pending_apps: - # new apps will usually be in the model by now, but if some - # haven't made it yet we'll need to wait on them to be added - await jasyncio.gather(*[ - jasyncio.ensure_future(self._wait_for_new("application", app_name)) - for app_name in pending_apps - ]) - return [ - app - for name, app in self.applications.items() - if name in handler.applications - ] - else: - if overlays: - raise JujuError( - "options provided but not supported when deploying a charm: overlays=%s" - % overlays - ) - # XXX: we're dropping local resources here, but we don't - # actually support them yet anyway - if not res.is_local: - add_charm_res = await self._add_charm(identifier, charm_origin) - if isinstance(add_charm_res, dict): - # This is for backwards compatibility for older - # versions where AddCharm returns a dictionary - charm_origin = add_charm_res.get("charm_origin", charm_origin) - else: - charm_origin = add_charm_res.charm_origin - if Schema.CHARM_HUB.matches(schema): - if ( - client.ApplicationFacade.best_facade_version(self.connection()) - >= 19 - ): - server_side_deploy = True - else: - # TODO (cderici): this is an awkward workaround for basically not calling - # the AddPendingResources in case this is a server side deploy. - # If that's the case, then the store resources (and revisioned local - # resources) are handled at the server side if this is a server side deploy - # (local uploads are handled right after we get the pendingIDs returned - # from the facade call). - resources = await self._add_charmhub_resources( - res.app_name, identifier, add_charm_res.charm_origin - ) - - is_sub = await self.charmhub.is_subordinate(name) - if is_sub: - if num_units > 1: - raise JujuError( - "cannot use num_units with subordinate application" - ) - num_units = 0 - - else: - # We have a local charm dir that needs to be uploaded - charm_dir = os.path.abspath(os.path.expanduser(identifier)) - metadata = utils.get_local_charm_metadata(charm_dir) - charm_series = charm_series or await get_charm_series(metadata, self) - - base = utils.get_local_charm_base(charm_series, charm_dir, client.Base) - charm_origin.base = base - - if not application_name: - application_name = metadata["name"] - if not application_name: - application_name = metadata["name"] - if base is None and charm_series is None: - raise JujuError( - "Either series or base is needed to deploy the " - f"charm at {charm_dir}. " - ) - - identifier = await self.add_local_charm_dir(charm_dir, charm_series) - resources = await self.add_local_resources( - application_name, identifier, metadata, resources=resources - ) - - if config is None: - config = {} - if trust: - config["trust"] = True - - return await self._deploy( - charm_url=identifier, - application=res.app_name, - series=charm_series, - config=config, - constraints=constraints, - endpoint_bindings=bind, - resources=resources, - storage=storage, - channel=channel, - num_units=num_units, - placement=parse_placement(to), - devices=devices, - charm_origin=charm_origin, - attach_storage=attach_storage, - force=force, - server_side_deploy=server_side_deploy, - ) - - async def _add_charm(self, charm_url, origin): - """_add_charm sends the given origin and the url to the Juju API too add the charm to the - state. Either calls the CharmsFacade.AddCharm for (> version 2), or the - ClientFacade.AddCharm (for older versions). - - :param str charm_url: the url of the charm to be added - :param client.CharmOrigin origin: the origin for the charm to be added - - :returns client.CharmOriginResult - """ - # client facade is deprecated with in Juju, and smaller, more focused - # facades have been created and we'll use that if it's available. - charms_cls = client.CharmsFacade - if charms_cls.best_facade_version(self.connection()) > 2: - charms_facade = charms_cls.from_connection(self.connection()) - return await charms_facade.AddCharm( - charm_origin=origin, url=charm_url, force=False - ) - - client_facade = client.ClientFacade.from_connection(self.connection()) - return await client_facade.AddCharm( - channel=str(origin.risk), url=charm_url, force=False - ) - - async def _resolve_charm( - self, url, origin, force=False, series=None, model_config=None - ): - """Calls Charms.ResolveCharms to resolve all the fields of the - charm_origin and also the url and the supported_series - - :param str url: The url of the charm - :param client.CharmOrigin origin: The manually constructed origin - based on what we know about the charm and the deployment so far - - Returns the confirmed origin returned by the Juju API to be used in - calls like ApplicationFacade.Deploy - - :returns url.URL, client.CharmOrigin, [str] - """ - charms_cls = client.CharmsFacade - if charms_cls.best_facade_version(self.connection()) < 3: - raise JujuError("resolve charm") - - charms_facade = charms_cls.from_connection(self.connection()) - - # TODO (cderici): following part can be refactored out, since the - # origin should be set (including the base) before calling this, - # though all tests need to run (in earlier versions too) before - # committing to make sure there's no regression - source = Source.CHARM_HUB.value - - resolve_origin = { - "source": source, - "architecture": origin.architecture, - "track": origin.track, - "risk": origin.risk, - "base": origin.base, - "revision": origin.revision, - } - - resp = await charms_facade.ResolveCharms( - resolve=[{"reference": str(url), "charm-origin": resolve_origin}] - ) - if len(resp.results) != 1: - raise JujuError(f"expected one result, received {resp.results}") - - result = resp.results[0] - if result.error: - raise JujuError(f"resolving {url} : {result.error.message}") - - # TODO (cderici) : supported_bases - supported_series = result.get( - "supported_series", result.unknown_fields["supported-series"] - ) - resolved_origin = result.charm_origin - charm_url = URL.parse(result.url) - - # run the series selector to get a series for the base - selected_series = utils.series_selector( - series, charm_url, model_config, supported_series, force - ) - result.charm_origin.base = utils.get_base_from_origin_or_channel( - resolved_origin, selected_series - ) - charm_url.series = selected_series - - return charm_url, resolved_origin - - async def _resolve_architecture(self, url=None): - """_resolve_architecture returns the architecture for a given charm url. - If the charm url is absent, or doesn't specific an arch, we return the - default architecture from the model. - - :param str url: the client.URL to determine the arch for - - :returns str architecture for the given url - """ - if url is not None and url.architecture: - return url.architecture - - constraints = await self.get_constraints() - if "arch" in constraints: - return constraints["arch"] - - return DEFAULT_ARCHITECTURE - - async def _add_charmhub_resources( - self, application, entity_url, origin, overrides=None - ): - """_add_charmhub_resources is called by the deploy to add pending resources requested by - the charm being deployed. It calls the ResourcesFacade.AddPendingResources. - - :param str application: the name of the application - :param client.CharmURL entity_url: url for the charm that we add resources for - :param client.CharmOrigin origin: origin for the charm that we add resources for - - :returns [string]string resource_map that is a map of resources to their assigned - pendingIDs. - """ - charm_facade = client.CharmsFacade.from_connection(self.connection()) - res = await charm_facade.CharmInfo(entity_url) - - resources = [ - { - "description": resource.description, - "name": resource.name, - "path": resource.path, - "type_": resource.type_, - "origin": "store", - "revision": -1, - } - for resource in res.meta.resources.values() - ] - - if overrides: - names = {r["name"] for r in resources} - unknown = overrides.keys() - names - if unknown: - raise JujuError( - "Unrecognized resource{}: {}".format( - "s" if len(unknown) > 1 else "", ", ".join(unknown) - ) - ) - for resource in resources: - if resource["name"] in overrides: - resource["revision"] = overrides[resource["name"]] - - resources_facade = client.ResourcesFacade.from_connection(self.connection()) - response = await resources_facade.AddPendingResources( - application_tag=tag.application(application), - charm_url=entity_url, - charm_origin=origin, - resources=[client.CharmResource(**resource) for resource in resources], - ) - - # response.pending_ids always exists but it can itself be None - # see juju/client/_definitions.py for AddPendingResourcesResult - resource_map = { - resource["name"]: pid - for resource, pid in zip(resources, response.pending_ids or {}) - } - - return resource_map - - async def add_local_resources(self, application, entity_url, metadata, resources): - """_add_local_resources is called by the deploy to add pending local resources requested by - the charm being deployed. It calls the ResourcesFacade.AddPendingResources. After getting - the pending IDs from the controller it sends an HTTP PUT request to actually upload local - resources. - - :param str application: the name of the application - :param client.CharmURL entity_url: url for the charm that we add resources for - :param [string]string metadata: metadata for the charm that we add resources for - :param dict[str, str] resources: the paths for the local files (or oci-images) to - be added as local resources - - :returns [string]string resource_map that is a map of resources to their assigned - pendingIDs. - """ - if not resources: - return None - - resource_map = dict() - - for name, path in resources.items(): - resource_type = metadata["resources"][name]["type"] - if resource_type not in {"oci-image", "file"}: - log.info(f"Resource {name} of type {resource_type} is not supported") - continue - - charmresource = { - "description": "", - "fingerprint": "", - "name": name, - "path": Path(path).name, - "revision": 0, - "size": 0, - "type_": resource_type, - "origin": "upload", - } - - resources_facade = client.ResourcesFacade.from_connection(self.connection()) - response = await resources_facade.AddPendingResources( - application_tag=tag.application(application), - charm_url=entity_url, - resources=[client.CharmResource(**charmresource)], - ) - pending_id = response.pending_ids[0] - resource_map[name] = pending_id - - if resource_type == "oci-image": - # TODO Docker Image validation and support for local images. - docker_image_details = { - "registrypath": path, - "username": "", - "password": "", - } - data = yaml.dump(docker_image_details).encode("utf-8") - else: - p = Path(path) - data = p.read_bytes() if p.exists() else b"" - - self._upload(data, path, application, name, resource_type, pending_id) - - return resource_map - - def _upload( - self, - data: bytes, - path: str | Path, - app_name: str, - res_name: str, - res_type: str, - pending_id: str, - ) -> None: - conn, headers, path_prefix = self.connection().https_connection() - - query = f"?pendingid={pending_id}" - url = f"{path_prefix}/applications/{app_name}/resources/{res_name}{query}" - if res_type == "oci-image": - disp = f'multipart/form-data; filename="{path}"' - else: - disp = f'form-data; filename="{path}"' - - headers["Content-Type"] = "application/octet-stream" - headers["Content-Length"] = str(len(data)) - headers["Content-Sha384"] = hashlib.sha384(data).hexdigest() - headers["Content-Disposition"] = disp - - conn.request("PUT", url, data, headers) - - response = conn.getresponse() - result = response.read().decode() - if not response.status == 200: - raise JujuError(result) - - async def _deploy( - self, - charm_url, - application, - series, - config, - constraints, - endpoint_bindings, - resources, - storage: Mapping[str, str | StorageConstraintDict] | None, - channel=None, - num_units=None, - placement=None, - devices=None, - charm_origin=None, - attach_storage=[], - force=False, - server_side_deploy=False, - ): - """Logic shared between `Model.deploy` and `BundleHandler.deploy`. - - :param dict storage: optional storage constraints, in the form of `{label: constraint}`. - The label is a string specified by the charm, while the constraint is - either a constraints.StorageConstraintDict, or a string following - `the juju storage constraint directive format `_, - specifying the storage pool, number of volumes, and size of each volume. - """ - log.info("Deploying %s", charm_url) - - storage = parse_storage_constraints(storage) - - trust = config.get("trust", False) - # stringify all config values for API, and convert to YAML - config = {k: str(v) for k, v in config.items()} - config = yaml.dump({application: config}, default_flow_style=False) - - app_facade = client.ApplicationFacade.from_connection(self.connection()) - - if server_side_deploy: - # Call DeployFromRepository - app = client.DeployFromRepositoryArg( - applicationname=application, - attachstorage=attach_storage, - charmname=charm_url, - configyaml=config, - cons=parse_constraints(constraints), - devices=devices, - dryrun=False, - placement=placement, - storage=storage, - trust=trust, - base=charm_origin.base, - channel=channel, - endpoint_bindings=endpoint_bindings, - force=force, - num_units=num_units, - resources=resources, - revision=charm_origin.revision, - ) - result = await app_facade.DeployFromRepository([app]) - # Collect the errors - errors = [] - for r in result.results: - if r.errors: - errors.extend([e.message for e in r.errors]) - # Upload pending local resources if any - for _result in result.results: - for pending_upload_resource in getattr( - _result, "pendingresourceuploads", [] - ): - _path = pending_upload_resource.filename - p = Path(_path) - data = p.read_text() if p.exists() else "" - self._upload( - data, - _path, - application, - pending_upload_resource.name, - "file", - "", - ) - else: - app = client.ApplicationDeploy( - charm_url=charm_url, - application=application, - series=series, - channel=channel, - charm_origin=charm_origin, - config_yaml=config, - constraints=parse_constraints(constraints), - endpoint_bindings=endpoint_bindings, - num_units=num_units, - resources=resources, - storage=storage, - placement=placement, - devices=devices, - attach_storage=attach_storage, - force=force, - ) - result = await app_facade.Deploy(applications=[app]) - errors = [r.error.message for r in result.results if r.error] - if errors: - raise JujuError("\n".join(errors)) - - return await self._wait_for_new("application", application) - - async def destroy_unit( - self, unit_id, destroy_storage=False, dry_run=False, force=False, max_wait=None - ): - """Destroy units by name.""" - return await self.destroy_units( - unit_id, - destroy_storage=destroy_storage, - dry_run=dry_run, - force=force, - max_wait=max_wait, - ) - - async def destroy_units( - self, - *unit_names, - destroy_storage=False, - dry_run=False, - force=False, - max_wait=None, - ): - """Destroy several units at once.""" - connection = self.connection() - app_facade = client.ApplicationFacade.from_connection(connection) - - units_to_destroy = [] - for unit_id in unit_names: - unit_tag = tag.unit(unit_id) - if unit_tag is None: - log.error("Error converting %s to a valid unit tag", unit_id) - raise JujuUnitError("Error converting %s to a valid unit tag", unit_id) - units_to_destroy.append({ - "unit-tag": unit_tag, - "destroy-storage": destroy_storage, - "force": force, - "max-wait": max_wait, - "dry-run": dry_run, - }) - log.debug("Destroying units %s", unit_names) - return await app_facade.DestroyUnit(units=units_to_destroy) - - def download_backup(self, archive_id, target_filename=None): - """Download a backup archive file. - - :param str archive_id: The id of the archive to download - :param str (optional) target_filename: A custom name for the target file - :return str: Path to the archive file - - """ - conn, headers, path_prefix = self.connection().https_connection() - path = "%s/backups" % path_prefix - headers["Content-Type"] = "application/json" - args = {"id": archive_id} - conn.request("GET", path, json.dumps(args, indent=2), headers=headers) - response = conn.getresponse() - result = response.read() - if not response.status == 200: - raise JujuBackupError( - "unable to download the backup ID : %s -- got : %s from the JujuAPI with a HTTP response code : %s" - % (archive_id, result, response.status) - ) - - if target_filename: - file_name = target_filename - else: - # check if archive_id is a filename - if re.match(r".*\.tar\.gz", archive_id): - # if so, use the same ID generated & sent by the Juju API - archive_id = re.compile("[0-9]+").findall(archive_id)[0] - - file_name = "juju-backup-%s.tar.gz" % archive_id - - with open(str(file_name), "wb") as f: - try: - f.write(result) - except OSError as e: - raise JujuBackupError( - "backup ID : %s was fetched, but : %s" % (archive_id, e) - ) - - log.info("Backup archive downloaded in : %s" % file_name) - return file_name - - async def get_config(self): - """Return the configuration settings for this model. - - :returns: A ``dict`` mapping keys to `ConfigValue` instances, - which have `source` and `value` attributes. - """ - config_facade = client.ModelConfigFacade.from_connection(self.connection()) - result = await config_facade.ModelGet() - config = result.config - for key, value in config.items(): - config[key] = client.ConfigValue.from_json(value) - return config - - async def get_constraints(self): - """Return the machine constraints for this model. - - :returns: A ``dict`` of constraints. - """ - constraints = {} - facade_cls = client.ModelConfigFacade - - facade = facade_cls.from_connection(self.connection()) - result = await facade.GetModelConstraints() - - # GetModelConstraints returns GetConstraintsResults which has a - # 'constraints' attribute. If no constraints have been set - # GetConstraintsResults.constraints is None. Otherwise - # GetConstraintsResults.constraints has an attribute for each possible - # constraint, each of these in turn will be None if they have not been - # set. - if result.constraints: - constraint_types = [ - a for a in dir(result.constraints) if a in client.Value._toSchema - ] - for constraint in constraint_types: - value = getattr(result.constraints, constraint) - if value is not None: - constraints[constraint] = getattr(result.constraints, constraint) - return constraints - - async def get_machines(self): - """Return list of machines in this model.""" - return list(self.state.machines.keys()) - - async def get_spaces(self): - """Return list of all known spaces, including associated subnets. - - Returns a List of :class:`~juju._definitions.Space` instances. - """ - space_facade = client.SpacesFacade.from_connection(self.connection()) - response = await space_facade.ListSpaces() - return response.results - - async def get_ssh_key(self, raw_ssh=False): - """Return known SSH keys for this model. - :param bool raw_ssh: if True, returns the raw ssh key, - else it's fingerprint - - """ - key_facade = client.KeyManagerFacade.from_connection(self.connection()) - entity = {"tag": tag.model(self.info.uuid)} - entities = client.Entities([entity]) - return await key_facade.ListKeys(entities=entities, mode=raw_ssh) - - get_ssh_keys = get_ssh_key - - async def remove_backup(self, backup_id): - """Delete a backup. - - :param str backup_id: The id of the backup to remove - - """ - backups_facade = client.BackupsFacade.from_connection(self.connection()) - return await backups_facade.Remove([backup_id]) - - async def remove_backups(self, backup_ids): - """Delete the given backups. - - :param [str] backup_ids: The list of ids of the backups to remove - - """ - backups_facade = client.BackupsFacade.from_connection(self.connection()) - return await backups_facade.Remove(backup_ids) - - async def remove_ssh_key(self, user, key): - """Remove a public SSH key(s) from this model. - - :param str key: Full ssh key - :param str user: Juju user to which the key is registered - - """ - key_facade = client.KeyManagerFacade.from_connection(self.connection()) - key = base64.b64decode(bytes(key.strip().split()[1].encode("ascii"))) - key = hashlib.md5(key).hexdigest() # noqa: S324 - key = ":".join(a + b for a, b in zip(key[::2], key[1::2])) - await key_facade.DeleteKeys(ssh_keys=[key], user=user) - - remove_ssh_keys = remove_ssh_key - - def restore_backup( - self, - backup_id, - bootstrap=False, - constraints=None, - archive=None, - upload_tools=False, - ): - """Restore a backup archive to a new controller. - - :param str backup_id: Id of backup to restore - :param bool bootstrap: Bootstrap a new state machine - :param constraints: Model constraints - :type constraints: :class:`juju.Constraints` - :param str archive: Path to backup archive to restore - :param bool upload_tools: Upload tools if bootstrapping a new machine - - """ - raise DeprecationWarning( - "juju restore-backup is deprecated in favor of the stand-alone 'juju-restore' tool: https://github.com/juju/juju-restore" - ) - - async def set_config(self, config): - """Set configuration keys on this model. - - :param dict config: Mapping of config keys to either string values or - `ConfigValue` instances, as returned by `get_config`. - """ - config_facade = client.ModelConfigFacade.from_connection(self.connection()) - - new_conf = {} - for key, value in config.items(): - if isinstance(value, client.ConfigValue): - new_conf[key] = value.value - elif isinstance(value, str): - new_conf[key] = value - else: - raise JujuModelConfigError( - "Expected either a string or a ConfigValue as a config value, found : %s of type %s" - % (value, type(value)) - ) - await config_facade.ModelSet(config=new_conf) - - async def set_constraints(self, constraints): - """Set machine constraints on this model. - - :param dict config: Mapping of model constraints - """ - facade_cls = client.ModelConfigFacade - - facade = facade_cls.from_connection(self.connection()) - - await facade.SetModelConstraints(application="", constraints=constraints) - - async def get_action_output(self, action_uuid, wait=None): - """Get the results of an action by ID. - - :param str action_uuid: Id of the action - :param int wait: Time in seconds to wait for action to complete. - :return dict: Output from action - :raises: :class:`JujuError` if invalid action_uuid - """ - action = await self._get_completed_action(action_uuid, wait=wait) - # ActionResult.output is None if the action produced no output - return {} if action.output is None else action.output - - async def _get_completed_action(self, action_uuid, wait=None): - """Get the completed internal _definitions.Action object. - - :param str action_uuid: Id of the action - :param int wait: Time in seconds to wait for action to complete. - :return dict: Output from action - :raises: :class:`JujuError` if invalid action_uuid - """ - action_facade = client.ActionFacade.from_connection(self.connection()) - entity = [{"tag": tag.action(action_uuid)}] - # Cannot use self.wait_for_action as the action event has probably - # already happened and self.wait_for_action works by processing - # model deltas and checking if they match our type. If the action - # has already occurred then the delta has gone. - - async def _wait_for_action_status(): - while True: - action_output = await action_facade.Actions(entities=entity) - if action_output.results[0].status in ("completed", "failed"): - return - else: - await jasyncio.sleep(1) - - await jasyncio.wait_for(_wait_for_action_status(), timeout=wait) - action_results = await action_facade.Actions(entities=entity) - return action_results.results[0] - - # FIXME: this function seems dead, the facade methods don't seem to exist - async def get_action_status(self, uuid_or_prefix=None, name=None): - """Get the status of all actions, filtered by ID, ID prefix, or name. - - :param str uuid_or_prefix: Filter by action uuid or prefix - :param str name: Filter by action name - - """ - results = {} - action_results = [] - action_facade = client.ActionFacade.from_connection(self.connection()) - if name: - name_results = await action_facade.FindActionsByNames(names=[name]) - action_results.extend(name_results.actions[0].actions) - if uuid_or_prefix: - # Collect list of actions matching uuid or prefix - matching_actions = await action_facade.FindActionTagsByPrefix( - prefixes=[uuid_or_prefix] - ) - entities = [] - for actions in matching_actions.matches.values(): - entities = [{"tag": a.tag} for a in actions] - # Get action results matching action tags - uuid_results = await action_facade.Actions(entities=entities) - action_results.extend(uuid_results.results) - for a in action_results: - results[tag.untag("action-", a.action.tag)] = a.status - return results - - async def get_status(self, filters=None, utc=False) -> FullStatus: - """Return the status of the model. - - :param str filters: Optional list of applications, units, or machines - to include, which can use wildcards ('*'). - :param bool utc: Display time as UTC in RFC3339 format - - """ - client_facade = client.ClientFacade.from_connection(self.connection()) - return await client_facade.FullStatus(patterns=filters) - - async def get_metrics(self, *tags): - """Retrieve metrics. - - :param str *tags: Tags of entities from which to retrieve metrics. - No tags retrieves the metrics of all units in the model. - :return: Dictionary of unit_name:metrics - - """ - log.debug("Retrieving metrics for %s", ", ".join(tags) if tags else "all units") - - metrics_facade = client.MetricsDebugFacade.from_connection(self.connection()) - - entities = [client.Entity(tag) for tag in tags] - metrics_result = await metrics_facade.GetMetrics(entities=entities) - - metrics = collections.defaultdict(list) - - for entity_metrics in metrics_result.results: - error = entity_metrics.error - if error: - if "is not a valid tag" in error: - raise ValueError(error.message) - else: - raise Exception(error.message) - - for metric in entity_metrics.metrics: - metrics[metric.unit].append(vars(metric)) - - return metrics - - async def create_offer(self, endpoint, offer_name=None, application_name=None): - """Offer a deployed application using a series of endpoints for use by - consumers. - - @param endpoint: holds the application and endpoint you want to offer - @param offer_name: over ride the offer name to help the consumer - """ - async with ConnectedController(self.connection()) as controller: - return await controller.create_offer( - self.info.uuid, - endpoint, - offer_name=offer_name, - application_name=application_name, - ) - - async def list_offers(self): - """Offers list information about applications' endpoints that have been - shared and who is connected. - """ - async with ConnectedController(self.connection()) as controller: - return await controller.list_offers(self.name) - - async def remove_offer(self, endpoint, force=False): - """Remove offer for an application. - - Offers will also remove relations to those offers, use force to do - so, without an error. - """ - async with ConnectedController(self.connection()) as controller: - return await controller.remove_offer(self.info.uuid, endpoint, force) - - async def consume( - self, endpoint, application_alias="", controller_name=None, controller=None - ): - """Adds a remote offer to the model. Relations can be created later using - "juju relate". - - If consuming a relation from a model on different controller the - controller name must be included in the endpoint. The controller_name - argument is being deprecated. - """ - if controller and controller_name: - raise JujuError("cannot set both controller_name and controller") - try: - offer = parse_offer_url(endpoint) - except OfferParseError as e: - log.error(e.message) - raise - if offer.has_endpoint(): - raise JujuError(f"remote offer {endpoint} should not include an endpoint") - if offer.user == "": - offer.user = self.info.username - endpoint = offer.string() - - if controller_name: - log.warning( - "controller_name argument will soon be deprecated, controller " - "should be specified in endpoint url" - ) - if offer.source == "": - offer.source = controller_name - - if offer.source: - source = await self._get_source_api(offer) - else: - if controller: - source = controller - else: - source = Controller() - kwargs = self.connection().connect_params() - kwargs["uuid"] = None - await source._connect_direct(**kwargs) - - consume_details = await source.get_consume_details(offer.as_local().string()) - - # Only disconnect when the controller object has been created within - # with function We don't want to disconnect the object passed by the - # user in the controller argument - if not controller: - await source.disconnect() - if consume_details is None or consume_details.offer is None: - raise JujuAPIError(f"missing consuming offer url for {offer.string()}") - - offer_url = parse_offer_url(consume_details.offer.offer_url) - offer_url.source = offer.source - - consume_details.offer.offer_url = offer_url.string() - consume_details.offer.application_alias = application_alias - - arg = _create_consume_args( - consume_details.offer, - consume_details.macaroon, - consume_details.external_controller, - ) - - facade = client.ApplicationFacade.from_connection(self.connection()) - results = await facade.Consume(args=[arg]) - if len(results.results) != 1: - raise JujuAPIError(f"expected 1 result, received {len(results.results)}") - if results.results[0].error is not None: - raise JujuAPIError(results.results[0].error) - local_name = offer_url.application - if application_alias != "": - local_name = application_alias - return local_name - - async def remove_saas(self, name): - """Removing a consumed (SAAS) application will terminate any relations that - application has, potentially leaving any related local applications - in a non-functional state. - """ - if not is_valid_application(name): - raise JujuError(f"invalid SAAS application name {name}") - - arg = client.DestroyConsumedApplicationParams() - arg.application_tag = application_tag(name) - - facade = client.ApplicationFacade.from_connection(self.connection()) - return await facade.DestroyConsumedApplications(applications=[arg]) - - async def export_bundle(self, filename=None): - """Exports the current model configuration as a reusable bundle.""" - facade = client.BundleFacade.from_connection(self.connection()) - result = await facade.ExportBundle() - if result.error is not None: - raise JujuAPIError(result.error) - - if filename is None: - return result.result - - try: - with open(str(filename), "w") as file: - file.write(result.result) - except OSError: - raise - - async def add_secret(self, name, data_args, file="", info=""): - """Adds a secret with a list of key values - - Equivalent to the cli command: - juju add-secret [options] [key[#base64|#file]=value...] - - :param name str: The name of the secret to be added. - :param data_args []str: The key value pairs to be added into the secret. - :param file str: A path to a yaml file containing secret key values. - :param info str: The secret description. - """ - data = create_secret_data(data_args) - - if file: - data_from_file = read_secret_data(file) - for k, v in data_from_file.items(): - # Caution: key/value pairs in files overwrite the ones in the args. - data[k] = v - - if client.SecretsFacade.best_facade_version(self.connection()) < 2: - raise JujuNotSupportedError("user secrets") - - secrets_facade = client.SecretsFacade.from_connection(self.connection()) - results = await secrets_facade.CreateSecrets([ - { - "content": {"data": data}, - "description": info, - "label": name, - } - ]) - if len(results.results) != 1: - raise JujuAPIError(f"expected 1 result, got {len(results.results)}") - result = results.results[0] - if result.error is not None: - raise JujuAPIError(result.error.message) - return result.result - - async def update_secret(self, name, data_args=[], new_name="", file="", info=""): - """Update a secret with a list of key values, or info. - - Equivalent to the cli command: - juju add-secret [options] [key[#base64|#file]=value...] - - :param name str: The name of the secret to be added. - :param data_args []str: The key value pairs to be added into the secret. - :param file str: A path to a yaml file containing secret key values. - :param info str: The secret description. - """ - data = create_secret_data(data_args) - if file: - data_from_file = read_secret_data(file) - for k, v in data_from_file.items(): - # Caution: key/value pairs in files overwrite the ones in the args. - data[k] = v - - if client.SecretsFacade.best_facade_version(self.connection()) < 2: - raise JujuNotSupportedError("user secrets") - secrets_facade = client.SecretsFacade.from_connection(self.connection()) - results = await secrets_facade.UpdateSecrets([ - { - "content": {"data": data}, - "description": info, - "existing-label": name, - "label": new_name, - } - ]) - if len(results.results) != 1: - raise JujuAPIError(f"expected 1 result, got {len(results.results)}") - result_error = results.results[0] - if result_error.error is not None: - raise JujuAPIError(result_error.error) - - async def list_secrets(self, filter=None, show_secrets=False): # noqa: A002 - """Returns the list of available secrets.""" - facade = client.SecretsFacade.from_connection(self.connection()) - results = await facade.ListSecrets( - filter_=filter, - show_secrets=show_secrets, - ) - return results.results - - async def remove_secret(self, secret_name, revision=-1): - """Remove an existing secret. - - :param secret_name str: ID|name of the secret to remove. - :param revision int: remove the specified revision. - """ - if client.SecretsFacade.best_facade_version(self.connection()) < 2: - raise JujuNotSupportedError("user secrets") - remove_secret_arg = { - "label": secret_name, - } - if revision >= 0: - remove_secret_arg["revisions"] = [revision] - - secrets_facade = client.SecretsFacade.from_connection(self.connection()) - results = await secrets_facade.RemoveSecrets([remove_secret_arg]) - if len(results.results) != 1: - raise JujuAPIError(f"expected 1 result, got {len(results.results)}") - result_error = results.results[0] - if result_error.error is not None: - raise JujuAPIError(result_error.error) - - async def grant_secret(self, secret_name, application, *applications): - """Grants access to a secret to the specified applications. - - :param secret_name str: ID|name of the secret. - :param application str: name of an application for which the access is granted - :param applications []str: names of more applications to associate the secret with - """ - if client.SecretsFacade.best_facade_version(self.connection()) < 2: - raise JujuNotSupportedError("user secrets") - secrets_facade = client.SecretsFacade.from_connection(self.connection()) - results = await secrets_facade.GrantSecret( - applications=[application, *applications], label=secret_name - ) - if len(results.results) != 1: - raise JujuAPIError(f"expected 1 result, got {len(results.results)}") - result_error = results.results[0] - if result_error.error is not None: - raise JujuAPIError(result_error.error) - - async def revoke_secret(self, secret_name, application, *applications): - """Revoke access to a secret. - - Revoke applications' access to view the value of a specified secret. - - :param secret_name str: ID|name of the secret. - :param application str: name of an application for which the access to the secret is revoked - :param applications []str: names of more applications to disassociate the secret with - """ - if client.SecretsFacade.best_facade_version(self.connection()) < 2: - raise JujuNotSupportedError("user secrets") - secrets_facade = client.SecretsFacade.from_connection(self.connection()) - results = await secrets_facade.RevokeSecret( - applications=[application, *applications], label=secret_name - ) - if len(results.results) != 1: - raise JujuAPIError(f"expected 1 result, got {len(results.results)}") - result_error = results.results[0] - if result_error.error is not None: - raise JujuAPIError(result_error.error) - - async def _get_source_api(self, url): - controller = Controller() - if url.has_empty_source(): - async with ConnectedController(self.connection()) as current: - if current.controller_name is not None: - controller_name = current.controller_name - else: - controller_name = url.source - - await controller.connect(controller_name=controller_name) - return controller - - async def wait_for_idle( - self, - apps: list[str] | None = None, - raise_on_error: bool = True, - raise_on_blocked: bool = False, - wait_for_active: bool = False, - timeout: float | None = 10 * 60, - idle_period: float = 15, - check_freq: float = 0.5, - status: str | None = None, - wait_for_at_least_units: int | None = None, - wait_for_exact_units: int | None = None, - ) -> None: - """Wait for applications in the model to settle into an idle state. - - :param List[str] apps: Optional list of specific app names to wait on. - If given, all apps must be present in the model and idle, while other - apps in the model can still be busy. If not given, all apps currently - in the model must be idle. - - :param bool raise_on_error: If True, then any unit or app going into - "error" status immediately raises either a JujuAppError or a JujuUnitError. - Note that machine or agent failures will always raise an exception (either - JujuMachineError or JujuAgentError), regardless of this param. The default - is True. - - :param bool raise_on_blocked: If True, then any unit or app going into - "blocked" status immediately raises either a JujuAppError or a JujuUnitError. - The default is False. - - :param bool wait_for_active: If True, then also wait for all unit workload - statuses to be "active" as well. The default is False. - - :param float timeout: How long to wait, in seconds, for the bundle settles - before raising an asyncio.TimeoutError. If None, will wait forever. - The default is 10 minutes. - - :param float idle_period: How long, in seconds, the agent statuses of all - units of all apps need to be `idle`. This delay is used to ensure that - any pending hooks have a chance to start to avoid false positives. - The default is 15 seconds. - - :param float check_freq: How frequently, in seconds, to check the model. - The default is every half-second. - - :param str status: The status to wait for. If None, not waiting. - The default is None (not waiting for any status). - - :param int wait_for_at_least_units: The least number of units to go into the idle - state. wait_for_idle will return after that many units are available (across all the - given applications). - The default is 1 unit. - - :param int wait_for_exact_units: The exact number of units to be expected before - going into the idle state. (e.g. useful for scaling down). - When set, takes precedence over the `wait_for_units` parameter. - """ - if wait_for_active: - warnings.warn( - "wait_for_active is deprecated; use status", - DeprecationWarning, - stacklevel=2, - ) - status = "active" - - _wait_for_units = ( - wait_for_at_least_units if wait_for_at_least_units is not None else 1 - ) - - timeout = timedelta(seconds=timeout) if timeout is not None else None - idle_period = timedelta(seconds=idle_period) - start_time = datetime.now() - # Type check against the common error of passing a str for apps - if apps is not None and ( - not isinstance(apps, list) or any(not isinstance(o, str) for o in apps) - ): - raise JujuError(f"Expected a List[str] for apps, given {apps}") - - apps = apps or self.applications - idle_times: dict[str, datetime] = {} - units_ready: set[str] = set() # The units that are in the desired state - last_log_time: datetime | None = None - log_interval = timedelta(seconds=30) - - def _raise_for_status(entities: dict[str, list[str]], status: Any): - if not entities: - return - for entity_name, error_type in ( - ("Machine", JujuMachineError), - ("Agent", JujuAgentError), - ("Unit", JujuUnitError), - ("App", JujuAppError), - ): - errored = entities.get(entity_name, []) - if not errored: - continue - raise error_type( - "{}{} in {}: {}".format( - entity_name, - "s" if len(errored) > 1 else "", - status, - ", ".join(errored), - ) - ) - - if wait_for_exact_units is not None: - assert ( - isinstance(wait_for_exact_units, int) and wait_for_exact_units >= 0 - ), "Invalid value for wait_for_exact_units : %s" % wait_for_exact_units - - while True: - # The list 'busy' is what keeps this loop going, - # i.e. it'll stop when busy is empty after all the - # units are scanned - busy = [] - errors = {} - blocks = {} - for app_name in apps: - if app_name not in self.applications: - busy.append(app_name + " (missing)") - continue - app = self.applications[app_name] - app_status = await app.get_status() - if raise_on_error and app_status == "error": - errors.setdefault("App", []).append(app.name) - if raise_on_blocked and app_status == "blocked": - blocks.setdefault("App", []).append(app.name) - - # Check if wait_for_exact_units flag is used - if wait_for_exact_units is not None: - if len(app.units) != wait_for_exact_units: - busy.append( - app.name - + " (waiting for exactly %s units, current : %s)" - % (wait_for_exact_units, len(app.units)) - ) - continue - # If we have less # of units then required, then wait a bit more - elif len(app.units) < _wait_for_units: - busy.append( - app.name - + " (not enough units yet - %s/%s)" - % (len(app.units), _wait_for_units) - ) - continue - # User is waiting for at least a certain # of units, and we have enough - elif wait_for_at_least_units and len(units_ready) >= _wait_for_units: - # So no need to keep looking, we have the desired number of units ready to go, - # exit the loop. Don't just return here, though, we might still have some - # errors to raise at the end - break - for unit in app.units: - if ( - raise_on_error - and unit.machine is not None - and unit.machine.status == "error" - ): - errors.setdefault("Machine", []).append(unit.machine.id) - continue - if raise_on_error and unit.agent_status == "error": - errors.setdefault("Agent", []).append(unit.name) - continue - if raise_on_error and unit.workload_status == "error": - errors.setdefault("Unit", []).append(unit.name) - continue - if raise_on_blocked and unit.workload_status == "blocked": - blocks.setdefault("Unit", []).append(unit.name) - continue - # TODO (cderici): we need two versions of wait_for_idle, one for waiting on - # individual units, another one for waiting for an application. - # The convoluted logic below is the result of trying to do both at the same - # time - need_to_wait_more_for_a_particular_status = status and ( - unit.workload_status != status - ) - app_is_in_desired_status = (not status) or (app_status == status) - if ( - not need_to_wait_more_for_a_particular_status - and unit.agent_status == "idle" - and (wait_for_at_least_units or app_is_in_desired_status) - ): - # A unit is ready if either: - # 1) Don't need to wait more for a particular status and the agent is "idle" - # 2) We're looking for a particular status and the unit's workload, - # as well as the application, is in that status. If the user wants to - # see only a particular number of units in that state -- i.e. a subset of - # the units is needed, then we don't care about the application status - # (because e.g. app can be in 'waiting' while unit.0 is 'active' and unit.1 - # is 'waiting') - - # Either way, the unit is ready, start measuring the time period that - # it needs to stay in that state (i.e. idle_period) - units_ready.add(unit.name) - now = datetime.now() - idle_start = idle_times.setdefault(unit.name, now) - - if now - idle_start < idle_period: - busy.append( - f"{unit.name} [{unit.agent_status}] {unit.workload_status}: {unit.workload_status_message}" - ) - else: - idle_times.pop(unit.name, None) - busy.append( - f"{unit.name} [{unit.agent_status}] {unit.workload_status}: {unit.workload_status_message}" - ) - _raise_for_status(errors, "error") - _raise_for_status(blocks, "blocked") - if not busy: - break - busy = "\n ".join(busy) - if timeout is not None and datetime.now() - start_time > timeout: - raise jasyncio.TimeoutError("Timed out waiting for model:\n" + busy) - if last_log_time is None or datetime.now() - last_log_time > log_interval: - log.info("Waiting for model:\n " + busy) - last_log_time = datetime.now() - await jasyncio.sleep(check_freq) - - -def _create_consume_args(offer, macaroon, controller_info): - """Convert a typed object that has been normalised to a overridden typed - definition. - - @param offer: takes an offer and serialises it into a valid type - @param macaroon: takes a macaroon and serialises it into a valid type - @param controller_info: takes a controller information and serialises it into - a valid type. - """ - endpoints = [ - client.RemoteEndpoint( - interface=ep.interface, limit=ep.limit, name=ep.name, role=ep.role - ) - for ep in offer.endpoints - ] - users = [ - client.OfferUserDetails( - access=u.access, display_name=u.display_name, user=u.user - ) - for u in offer.users - ] - external_controller = client.ExternalControllerInfo( - addrs=controller_info.addrs, - ca_cert=controller_info.ca_cert, - controller_alias=controller_info.controller_alias, - controller_tag=controller_info.controller_tag, - ) - caveats = [Caveat(cid=c["cid"]) for c in macaroon.unknown_fields["caveats"]] - macaroon = Macaroon( - signature=macaroon.unknown_fields["signature"], - caveats=caveats, - location=macaroon.unknown_fields["location"], - identifier=macaroon.unknown_fields["identifier"], - ) - - arg = client.ConsumeApplicationArg() - arg.application_description = offer.application_description - arg.endpoints = endpoints - arg.offer_name = offer.offer_name - arg.offer_url = offer.offer_url - arg.offer_uuid = offer.offer_uuid - arg.source_model_tag = offer.source_model_tag - arg.users = users - arg.application_alias = offer.application_alias - arg.external_controller = external_controller - arg.macaroon = macaroon - - return arg - - -class CharmArchiveGenerator: - """Create a Zip archive of a local charm directory for upload to a controller. - - This is used automatically by - `Model.add_local_charm_dir <#juju.model.Model.add_local_charm_dir>`_. - """ - - def __init__(self, path): - self.path = os.path.abspath(os.path.expanduser(path)) - - def make_archive(self, path): - """Create archive of directory and write to ``path``. - - :param path: Path to archive - - Ignored:: - - * build/* - This is used for packing the charm itself and any - similar tasks. - * */.* - Hidden files are all ignored for now. This will most - likely be changed into a specific ignore list - (.bzr, etc) - - """ - zf = zipfile.ZipFile(str(path), "w", zipfile.ZIP_DEFLATED) - for dirpath, dirnames, filenames in os.walk(self.path): - relative_path = dirpath[len(self.path) + 1 :] - if relative_path and not self._ignore(relative_path): - zf.write(dirpath, relative_path) - for dirname in dirnames: - archive_name = os.path.join(relative_path, dirname) - real_path = os.path.join(dirpath, dirname) - if os.path.islink(real_path): - self._check_link(real_path) - self._write_symlink(zf, os.readlink(real_path), archive_name) - for name in filenames: - archive_name = os.path.join(relative_path, name) - if not self._ignore(archive_name): - real_path = os.path.join(dirpath, name) - self._check_type(real_path) - if os.path.islink(real_path): - self._check_link(real_path) - self._write_symlink(zf, os.readlink(real_path), archive_name) - else: - zf.write(real_path, archive_name) - zf.close() - return path - - def _check_type(self, path: str) -> str: - """Check the path""" - s = os.stat(path) - if stat.S_ISDIR(s.st_mode) or stat.S_ISREG(s.st_mode): - return path - raise ValueError( - "Invalid Charm at %s %s" % (path, "Invalid file type for a charm") - ) - - def _check_link(self, path: str) -> None: - link_path = os.readlink(path) - if link_path[0] == "/": - raise ValueError( - "Invalid Charm at %s: %s" % (path, "Absolute links are invalid") - ) - path_dir = os.path.dirname(path) - link_path = os.path.join(path_dir, link_path) - if not link_path.startswith(os.path.abspath(self.path)): - raise ValueError( - "Invalid charm at %s %s" % (path, "Only internal symlinks are allowed") - ) - - def _write_symlink( - self, zf: zipfile.ZipFile, link_target: str, link_path: str - ) -> None: - """Package symlinks with appropriate zipfile metadata.""" - info = zipfile.ZipInfo() - info.filename = link_path - info.create_system = 3 - # Magic code for symlinks / py2/3 compat - # 27166663808 = (stat.S_IFLNK | 0755) << 16 - info.external_attr = 2716663808 - zf.writestr(info, link_target) - - def _ignore(self, path: str) -> bool: - return path == "build" or path.startswith("build/") or path.startswith(".") - - -class ModelInfo(ModelEntity): - @property - def tag(self): - return tag.model(self.uuid) - - @staticmethod - def type_name_override(): - return "model" diff --git a/build/lib/juju/names.py b/build/lib/juju/names.py deleted file mode 100644 index 0e9270fc..00000000 --- a/build/lib/juju/names.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import re -from enum import Enum, unique - - -@unique -class MatchType(Enum): - MATCH = 1 - SEARCH = 2 - - -MODEL = re.compile("^[a-z0-9]+[a-z0-9-]*$") - - -def match_model(val, match_type=None): - if match_type is MatchType.SEARCH: - return re.search(MODEL, val) - else: - return re.match(MODEL, val) - - -APPLICATION = re.compile("^(?:[a-z][a-z0-9]*(?:-[a-z0-9]*[a-z][a-z0-9]*)*)$") - - -def match_application(val, match_type=None): - if match_type is MatchType.SEARCH: - return re.search(APPLICATION, val) - else: - return re.match(APPLICATION, val) - - -def is_valid_application(val): - return match_application(val) is not None - - -ENDPOINT = re.compile( - "/?((?P[^\\.]*)\\.)?(?P[^:]*)(:(?P.*))?" -) - - -def match_endpoint(val, match_type=None): - if match_type is MatchType.SEARCH: - return re.search(ENDPOINT, val) - else: - return re.match(ENDPOINT, val) - - -SOURCE_ENDPOINT = re.compile("^[a-zA-Z0-9]+$") - - -def match_source_endpoint(val, match_type=None): - if match_type is MatchType.SEARCH: - return re.search(SOURCE_ENDPOINT, val) - else: - return re.match(SOURCE_ENDPOINT, val) - - -MODEL_APPLICATION = re.compile( - "(/?((?P[^/]+)/)?(?P[^.]*)(.(?P[^:]*(:.*)?))?)?" -) - - -def match_model_application(val, match_type=None): - if match_type is MatchType.SEARCH: - return re.search(MODEL_APPLICATION, val) - else: - return re.match(MODEL_APPLICATION, val) - - -valid_user_name_snippet = "[a-zA-Z0-9][a-zA-Z0-9.+-]*[a-zA-Z0-9]" -valid_user_snippet = f"(?:{valid_user_name_snippet}(?:@{valid_user_name_snippet})?)" -USER = re.compile( - f"^(?P{valid_user_name_snippet})(?:@(?P{valid_user_name_snippet}))?$" -) - - -def match_user(val, match_type=None): - if match_type is MatchType.SEARCH: - return re.search(USER, val) - else: - return re.match(USER, val) - - -RELATION = re.compile("[a-z][a-z0-9]*(?:[_-][a-z0-9]+)*") - - -def match_relation(val, match_type=None): - if match_type is MatchType.SEARCH: - return re.search(RELATION, val) - else: - return re.match(RELATION, val) diff --git a/build/lib/juju/offerendpoints.py b/build/lib/juju/offerendpoints.py deleted file mode 100644 index 3a39556d..00000000 --- a/build/lib/juju/offerendpoints.py +++ /dev/null @@ -1,216 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -# -# Module that parses offer endpoints -# - - -from .names import ( - MatchType, - match_application, - match_endpoint, - match_model, - match_model_application, - match_relation, - match_source_endpoint, - match_user, -) - - -class ParseError(Exception): - def __init__(self, message, *args, **kwargs): - self.message = message - super().__init__(*args, **kwargs) - - -class OfferEndpoints: - def __init__(self, application, endpoints, model=None, qualified_model_name=None): - self.application = application - self.endpoints = endpoints - self.model = model - self.qualified_model_name = qualified_model_name - - def __eq__(self, other): - if not isinstance(other, OfferEndpoints): - return NotImplemented - return ( - self.application == other.application - and self.endpoints == other.endpoints - and self.model == other.model - and self.qualified_model_name == other.qualified_model_name - ) - - -def parse_offer_endpoint(endpoint): - if ":" not in endpoint: - raise ParseError( - 'endpoint must conform to format ":"' - ) - - matches = match_endpoint(endpoint, MatchType.SEARCH) - if matches is None: - raise ParseError( - 'endpoint must conform to format ":"' - ) - model_group = matches.group("model") - application_group = matches.group("appname") - endpoints_group = matches.group("endpoints") - - qualified_model_name = None - if (model_group is not None) and (model_group != ""): - if "/" not in model_group: - raise NotImplementedError() - qualified_model_name = model_group - model_group = model_group.split("/")[0] - - if (model_group is not None) and (not match_model(model_group)): - raise ParseError(f"model name {model_group}") - if not match_application(application_group): - raise ParseError(f"application name {application_group}") - - model = model_group - application = application_group - - endpoints = endpoints_group.split(",") - if len(endpoints) == 0 or len(endpoints_group) == 0: - raise ParseError(f"specify endpoints for {application}") - - return OfferEndpoints( - application, endpoints, model=model, qualified_model_name=qualified_model_name - ) - - -class OfferURL: - def __init__(self, source="", user="", model="", application=""): - self.source = source - self.user = user - self.model = model - self.application = application - - def __eq__(self, other): - if not isinstance(other, OfferURL): - return NotImplemented - return ( - self.source == other.source - and self.user == other.user - and self.model == other.model - and self.application == other.application - ) - - def has_empty_source(self): - return self.source is None or self.source == "" - - def has_endpoint(self): - return ":" in self.application - - def as_local(self): - return OfferURL("", self.user, self.model, self.application) - - def string(self): - parts = [] - if self.user != "": - parts.append(self.user) - if self.model != "": - parts.append(self.model) - path = "/".join(parts) - path = f"{path}.{self.application}" - if self.has_empty_source(): - return path - return f"{self.source}:{path}" - - -def parse_offer_url(url): - source, rest = maybe_parse_offer_url_source(url) - - valid = url[0] != ":" - valid = valid and match_model_application(rest) - if not valid: - raise ParseError( - f"application offer URL has invalid form, must be [.: {url}" - ) - - offer_source = source - - matches = match_model_application(rest, MatchType.SEARCH) - offer_user = _get_or_else(matches.group("user"), "") - offer_model = _get_or_else(matches.group("model"), "") - offer_application = _get_or_else(matches.group("application"), "") - - if valid and (("/" in offer_model) or ("/" in offer_application)): - raise ParseError( - f"application offer URL has invalid form, must be [.: {url}" - ) - if not offer_model: - raise ParseError("application offer URL is missing model") - if not offer_application: - raise ParseError("application offer URL is missing application") - - if offer_user and not match_user(offer_user): - raise ParseError(f"user name {offer_user} not valid") - if offer_model and not match_model(offer_model): - raise ParseError(f"model name {offer_model} not valid") - - app_name = offer_application.split(":")[0] - if app_name and not match_application(app_name): - raise ParseError(f"application name {app_name} not valid") - - return OfferURL( - source=offer_source, - user=offer_user, - model=offer_model, - application=offer_application, - ) - - -def _get_or_else(val, res): - if val is None: - return res - return val - - -def maybe_parse_offer_url_source(url): - parts = url.split(":") - if len(parts) > 2: - return parts[0], ":".join(parts[slice(1, len(parts))]) - elif len(parts) == 2: - if match_source_endpoint(parts[1]): - return "", url - return (parts[0], parts[1]) - return "", url - - -class LocalEndpoint: - def __init__(self, application="", relation=None): - self.application = application - self.relation = relation - - def __eq__(self, other): - if not isinstance(other, LocalEndpoint): - return NotImplemented - return self.relation == other.relation and self.application == other.application - - -def parse_local_endpoint(url): - application_name = url - relation = None - - if ":" in url: - if url[0] == ":" or url[-1] == ":": - raise ParseError(f"endpoint {url} not valid") - - parts = url.split(":") - if len(parts) != 2: - raise ParseError(f"endpoint {url} not valid") - - application_name = parts[0] - - if not match_relation(parts[1]): - raise ParseError(f"endpoint {url} not valid") - - relation = parts[1] - - if not match_application(application_name): - raise ParseError(f"endpoint {application_name} not valid") - - return LocalEndpoint(application=application_name, relation=relation) diff --git a/build/lib/juju/origin.py b/build/lib/juju/origin.py deleted file mode 100644 index 6d87ef62..00000000 --- a/build/lib/juju/origin.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -from enum import Enum - -from . import utils -from .errors import JujuError - - -class Source(Enum): - """Source defines a origin source. Providing a hint to the controller about - what the charm identity is from the URL and origin source. - - """ - - LOCAL = "local" - CHARM_HUB = "charm-hub" - - def __str__(self): - return self.value - - -class Origin: - def __init__(self, source, channel, platform): - self.source = source - self.channel = channel - self.platform = platform - - def __str__(self): - return f"origin using source {self.source!s} for channel {self.channel} and platform {self.platform}" - - -class Risk(Enum): - STABLE = "stable" - CANDIDATE = "candidate" - BETA = "beta" - EDGE = "edge" - - def __str__(self): - return self.value - - @staticmethod - def valid(potential): - for risk in [Risk.STABLE, Risk.CANDIDATE, Risk.BETA, Risk.EDGE]: - if str(risk) == potential: - return True - return False - - -class Channel: - """Channel identifies and describes completely a store channel. - - A channel consists of, and is subdivided by, tracks, risk-levels and - - Tracks enable snap developers to publish multiple supported releases of - their application under the same snap name. - - Risk-levels represent a progressive potential trade-off between stability - and new features. - - The complete channel name can be structured as three distinct parts separated - by slashes: - - / - - """ - - def __init__(self, track=None, risk=None): - if not Risk.valid(risk): - raise JujuError(f"unexpected risk {risk}") - - self.track = track or "" - self.risk = risk - - @staticmethod - def parse(s: str): - """Parse a channel from a given string. - Parse does not take into account branches. - - """ - if not s: - raise JujuError("channel cannot be empty") - - p = s.split("/") - - risk = None - track = None - if len(p) == 1: - if Risk.valid(p[0]): - risk = p[0] - else: - track = p[0] - risk = str(Risk.STABLE) - elif len(p) == 2: - track = p[0] - risk = p[1] - else: - raise JujuError(f"channel is malformed and has too many components {s}") - - if risk is not None and not Risk.valid(risk): - raise JujuError(f"risk in channel {s} is not valid") - if track is not None and track == "": - raise JujuError(f"track in channel {s} is not valid") - - return Channel(track, risk) - - def normalize(self): - track = self.track if self.track != "" else "" - risk = self.risk if self.risk != "" else "" - return Channel(track, risk) - - def __eq__(self, other): - if isinstance(other, self.__class__): - return self.track == other.track and self.risk == other.risk - return False - - def __str__(self): - path = self.risk - if self.track != "": - path = f"{self.track}/{path}" - return path - - def compute_base_channel(self, series): - """Determines the channel for a client.Base - A base channel is a track/risk/branch - - """ - _ch = [self.risk] - tr = utils.get_series_version(series) - if tr: - _ch = [tr, *_ch] - return "/".join(_ch) - - -class Platform: - """ParsePlatform parses a string representing a store platform. - Serialized version of platform can be expected to conform to the following: - - 1. Architecture is mandatory. - 2. OS is optional and can be dropped. Series is mandatory if OS wants - to be displayed. - 3. Series is also optional. - - To indicate something is missing `unknown` can be used in place. - - Examples: - 1. `//` - 2. `` - 3. `/` - 4. `/unknown/` - - """ - - def __init__(self, arch, series=None, os=None): - self.arch = arch - self.series = series - self.os = os - - @staticmethod - def parse(s): - if not s: - raise JujuError("platform cannot be empty") - - p = s.split("/") - - arch = None - os = None - series = None - if len(p) == 1: - arch = p[0] - elif len(p) == 2: - arch = p[0] - series = p[1] - elif len(p) == 3: - arch = p[0] - os = p[1] - series = p[2] - else: - raise JujuError(f"platform is malformed and has too many components {s}") - - if not arch: - raise JujuError(f"architecture in platform {s} is not valid") - if os is not None and os == "": - raise JujuError(f"os in platform {s} is not valid") - if series is not None and series == "": - raise JujuError(f"series in platform {s} is not valid") - - return Platform(arch, series, os) - - def normalize(self): - os = self.os if self.os is not None or self.os != "unknown" else None - series = self.series - if series is None or series == "unknown": - os = None - series = None - - return Platform(self.arch, series, os) - - def __eq__(self, other): - if isinstance(other, self.__class__): - return ( - self.arch == other.arch - and self.os == other.os - and self.series == other.series - ) - return False - - def __str__(self): - path = self.arch - if self.os is not None and self.os != "": - path = f"{path}/{self.os}" - if self.series is not None and self.series != "": - path = f"{path}/{self.series}" - return path diff --git a/build/lib/juju/placement.py b/build/lib/juju/placement.py deleted file mode 100644 index 3dbc99a3..00000000 --- a/build/lib/juju/placement.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -# -# This module allows us to parse a machine placement directive into a -# Placement object suitable for passing through the websocket API. -# -# Once https://bugs.launchpad.net/juju/+bug/1645480 is addressed, this -# module should be deprecated. -# - -from .client import client - -MACHINE_SCOPE = "#" - - -def parse(directive): - """Given a string in the format `scope:directive`, or simply `scope` - or `directive`, return a Placement object suitable for passing - back over the websocket API. - - """ - if not directive: - # Handle null case - return None - - if isinstance(directive, (list, tuple)): - results = [] - for d in directive: - results.extend(parse(d)) - return results - - if isinstance(directive, (dict, client.Placement)): - # We've been handed something that we can simply hand back to - # the api. (Forwards compatibility) - return [directive] - - # Juju 2.0 can't handle lxc containers. - directive = directive.replace("lxc", "lxd") - - if ":" in directive: - # Planner has given us a scope and directive in string form - scope, directive = directive.split(":") - return [client.Placement(scope=scope, directive=directive)] - - if directive.isdigit(): - # Planner has given us a machine id (we rely on juju core to - # verify its validity.) - return [client.Placement(scope=MACHINE_SCOPE, directive=directive)] - - if "/" in directive: - # e.g. "0/lxd/0" - # https://github.com/juju/juju/blob/master/instance/placement_test.go#L29 - return [ - client.Placement(scope=MACHINE_SCOPE, directive=directive), - ] - - # Planner has probably given us a container type. Leave it up to - # juju core to verify that it is valid. - return [client.Placement(scope=directive)] diff --git a/build/lib/juju/provisioner.py b/build/lib/juju/provisioner.py deleted file mode 100644 index 9f034234..00000000 --- a/build/lib/juju/provisioner.py +++ /dev/null @@ -1,344 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -import os -import re -import shlex -import tempfile -import uuid -from subprocess import CalledProcessError - -import paramiko - -from .client import client - -arches = [ - [re.compile(r"amd64|x86_64"), "amd64"], - [re.compile(r"i?[3-9]86"), "i386"], - [re.compile(r"(arm$)|(armv.*)"), "armhf"], - [re.compile(r"aarch64"), "arm64"], - [re.compile(r"ppc64|ppc64el|ppc64le"), "ppc64el"], - [re.compile(r"s390x?"), "s390x"], -] - - -def normalize_arch(raw_arch): - """Normalize the architecture string.""" - for arch in arches: - if arch[0].match(raw_arch): - return arch[1] - - -DETECTION_SCRIPT = """#!/bin/bash -set -e -os_id=$(grep '^ID=' /etc/os-release | tr -d '"' | cut -d= -f2) -if [ "$os_id" = 'centos' ]; then - os_version=$(grep '^VERSION_ID=' /etc/os-release | tr -d '"' | cut -d= -f2) - echo "centos$os_version" -else - lsb_release -cs -fi -uname -m -grep MemTotal /proc/meminfo -cat /proc/cpuinfo -""" - -INITIALIZE_UBUNTU_SCRIPT = """set -e -(id ubuntu &> /dev/null) || useradd -m ubuntu -s /bin/bash -umask 0077 -temp=$(mktemp) -echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > $temp -install -m 0440 $temp /etc/sudoers.d/90-juju-ubuntu -rm $temp -su ubuntu -c 'install -D -m 0600 /dev/null ~/.ssh/authorized_keys' -export authorized_keys="{}" -if [ ! -z "$authorized_keys" ]; then - su ubuntu -c 'echo $authorized_keys >> ~/.ssh/authorized_keys' -fi -""" - - -class SSHProvisioner: - """Provision a manually created machine via SSH.""" - - user = "" - host = "" - private_key_path = "" - - def __init__(self, user, host, private_key_path): - self.host = host - self.user = user - self.private_key_path = private_key_path - - def _get_ssh_client(self, host: str, user: str, key: str) -> paramiko.SSHClient: - """Return a connected Paramiko ssh object. - - :param str host: The host to connect to. - :param str user: The user to connect as. - :param str key: The private key to authenticate with. - - :return: object: A paramiko.SSHClient - :raises: :class:`paramiko.ssh_exception.SSHException` if the - connection failed - """ - ssh = paramiko.SSHClient() - # Ruff warns about insecure policy of automatically accepting unknown keys - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # noqa: S507 - - pkey = None - - # Read the private key into a paramiko.RSAKey - if os.path.exists(key): - with open(key) as f: - pkey = paramiko.RSAKey.from_private_key(f) - - ####################################################################### - # There is a bug in some versions of OpenSSH 4.3 (CentOS/RHEL5) where # - # the server may not send the SSH_MSG_USERAUTH_BANNER message except # - # when responding to an auth_none request. For example, paramiko will # - # attempt to use password authentication when a password is set, but # - # the server could deny that, instead requesting keyboard-interactive.# - # The hack to workaround this is to attempt a reconnect, which will # - # receive the right banner, and authentication can proceed. See the # - # following for more info: # - # https://github.com/paramiko/paramiko/issues/432 # - # https://github.com/paramiko/paramiko/pull/438 # - ####################################################################### - - try: - ssh.connect(host, port=22, username=user, pkey=pkey) - except paramiko.ssh_exception.SSHException as e: - if str(e) == "Error reading SSH protocol banner": - # Once more, with feeling - ssh.connect(host, port=22, username=user, pkey=pkey) - else: - # Reraise the original exception - raise e - - return ssh - - def _run_command( - self, ssh: paramiko.SSHClient, cmd: str | list[str], pty: bool = True - ) -> tuple[str, str]: - """Run a command remotely via SSH. - - :param object ssh: The SSHClient - :param str cmd: The command to execute - :param list cmd: The `shlex.split` command to execute - :param bool pty: Whether to allocate a pty - - :return: tuple: The stdout and stderr of the command execution - :raises: :class:`CalledProcessError` if the command fails - """ - if isinstance(cmd, str): - cmd = shlex.split(cmd) - - if isinstance(cmd, str): - cmds = cmd - else: - cmds = " ".join(cmd) - - _stdin, stdout, stderr = ssh.exec_command(cmds, get_pty=pty) - retcode = stdout.channel.recv_exit_status() - - if retcode > 0: - output = stderr.read().strip() - raise CalledProcessError(returncode=retcode, cmd=cmd, output=output) - return ( - stdout.read().decode("utf-8").strip(), - stderr.read().decode("utf-8").strip(), - ) - - def _init_ubuntu_user(self): - """Initialize the ubuntu user. - - :return: bool: If the initialization was successful - :raises: :class:`paramiko.ssh_exception.AuthenticationException` - if the authentication fails - """ - ssh = None - try: - # Run w/o allocating a pty, so we fail if sudo prompts for a passwd - ssh = self._get_ssh_client( - self.host, - self.user, - self.private_key_path, - ) - _stdout, _stderr = self._run_command(ssh, "sudo -n true", pty=False) - except paramiko.ssh_exception.AuthenticationException as e: - raise e - finally: - if ssh: - ssh.close() - - # Infer the public key - public_key = None - public_key_path = f"{self.private_key_path}.pub" - - if not os.path.exists(public_key_path): - raise FileNotFoundError(f"Public key '{public_key_path}' doesn't exist.") - - with open(public_key_path) as f: - public_key = f.readline() - - script = INITIALIZE_UBUNTU_SCRIPT.format(public_key) - - try: - ssh = self._get_ssh_client( - self.host, - self.user, - self.private_key_path, - ) - - self._run_command( - ssh, ["sudo", "/bin/bash -c " + shlex.quote(script)], pty=True - ) - except paramiko.ssh_exception.AuthenticationException as e: - raise e - finally: - ssh.close() - - return True - - def _detect_hardware_and_os(self, ssh): - """Detect the target hardware capabilities and OS series. - - :param object ssh: The SSHClient - :return: str: A raw string containing OS and hardware information. - """ - info = { - "series": "", - "arch": "", - "cpu-cores": "", - "mem": "", - } - - stdout, _stderr = self._run_command( - ssh, - ["sudo", "/bin/bash -c " + shlex.quote(DETECTION_SCRIPT)], - pty=True, - ) - - lines = stdout.split("\n") - info["series"] = lines[0].strip() - info["arch"] = normalize_arch(lines[1].strip()) - - mem_kb = re.split(r"\s+", lines[2])[1] - - # Convert to MB - info["mem"] = int(mem_kb) // 1024 - - # Detect available CPUs - recorded = {} - for line in lines[3:]: - physical_id = "" - print(line) - - if line.find("physical id") == 0: - physical_id = line.split(":")[1].strip() - elif line.find("cpu cores") == 0: - cores = line.split(":")[1].strip() - - if physical_id not in recorded: - info["cpu-cores"] += cores - recorded[physical_id] = True - - return info - - def provision_machine(self): - """Perform the initial provisioning of the target machine. - - :return: bool: The client.AddMachineParams - :raises: :class:`paramiko.ssh_exception.AuthenticationException` - if the upload fails - """ - params = client.AddMachineParams() - - if self._init_ubuntu_user(): - try: - ssh = self._get_ssh_client(self.host, self.user, self.private_key_path) - - hw = self._detect_hardware_and_os(ssh) - params.series = hw["series"] - params.instance_id = f"manual:{self.host}" - params.nonce = f"manual:{self.host}:{uuid.uuid4()}" - params.hardware_characteristics = { - "arch": hw["arch"], - "mem": int(hw["mem"]), - "cpu-cores": int(hw["cpu-cores"]), - } - params.addresses = [ - { - "value": self.host, - "type": "ipv4", - "scope": "public", - } - ] - - except paramiko.ssh_exception.AuthenticationException as e: - raise e - finally: - ssh.close() - - return params - - async def install_agent(self, connection, nonce, machine_id): - """:param object connection: Connection to Juju API - :param str nonce: The nonce machine specification - :param str machine_id: The id assigned to the machine - - :return: bool: If the initialization was successful - """ - # The path where the Juju agent should be installed. - data_dir = "/var/lib/juju" - - # Disabling this prevents `apt-get update` from running initially, so - # charms will fail to deploy - disable_package_commands = False - - facade_cls = client.MachineManagerFacade - if connection.is_using_old_client: - facade_cls = client.ClientFacade - - facade = facade_cls.from_connection(connection) - - results = await facade.ProvisioningScript( - data_dir=data_dir, - disable_package_commands=disable_package_commands, - machine_id=machine_id, - nonce=nonce, - ) - - self._run_configure_script(results.script) - - def _run_configure_script(self, script): - """Run the script to install the Juju agent on the target machine. - - :param str script: The script returned by the ProvisioningScript API - :raises: :class:`paramiko.ssh_exception.AuthenticationException` - if the upload fails - """ - with tempfile.NamedTemporaryFile("w") as tmp_file: - tmp_file.write(script) - tmp_file.flush() - - # get ssh client - ssh = self._get_ssh_client( - self.host, - "ubuntu", - self.private_key_path, - ) - - try: - # copy the local copy of the script to the remote machine - sftp = paramiko.SFTPClient.from_transport(ssh.get_transport()) - sftp.put(tmp_file.name, tmp_file.name) - - # run the provisioning script - _stdout, _stderr = self._run_command( - ssh, - f"sudo /bin/bash {tmp_file.name}", - ) - finally: - ssh.close() diff --git a/build/lib/juju/py.typed b/build/lib/juju/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/build/lib/juju/relation.py b/build/lib/juju/relation.py deleted file mode 100644 index 93e80f81..00000000 --- a/build/lib/juju/relation.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import logging - -from . import model -from .errors import JujuEntityNotFoundError - -log = logging.getLogger(__name__) - - -class Endpoint: - def __init__(self, model, data): - self.model = model - self.data = data - - def __repr__(self): - return "".format(self.data["application-name"], self.name) - - @property - def application_name(self): - return self.data["application-name"] - - @property - def application(self): - """Application returns the underlying application model from the state. - If no application is found, then a JujuEntityNotFoundError is raised, in - this scenario it is expected that you disconnect and reconnect to the - model. - """ - app_name = self.data["application-name"] - if app_name in self.model.applications: - return self.model.applications[app_name] - raise JujuEntityNotFoundError(app_name, ["application"]) - - @property - def name(self): - return self.data["relation"]["name"] - - @property - def interface(self): - return self.data["relation"]["interface"] - - @property - def role(self): - return self.data["relation"]["role"] - - @property - def scope(self): - return self.data["relation"]["scope"] - - -class Relation(model.ModelEntity): - def __repr__(self): - return f"" - - @property - def endpoints(self): - return [Endpoint(self.model, data) for data in self.safe_data["endpoints"]] - - @property - def provides(self): - """The endpoint on the provides side of this relation, or None.""" - for endpoint in self.endpoints: - if endpoint.role == "provider": - return endpoint - return None - - @property - def requires(self): - """The endpoint on the requires side of this relation, or None.""" - for endpoint in self.endpoints: - if endpoint.role == "requirer": - return endpoint - return None - - @property - def peers(self): - """The peers endpoint of this relation, or None.""" - for endpoint in self.endpoints: - if endpoint.role == "peer": - return endpoint - return None - - @property - def is_subordinate(self): - return any(ep.scope == "container" for ep in self.endpoints) - - @property - def is_peer(self): - return any(ep.role == "peer" for ep in self.endpoints) - - def matches(self, *specs): - """Check if this relation matches relationship specs. - - Relation specs are strings that would be given to Juju to establish a - relation, and should be in the form ``[:]`` - where the ``:`` suffix is optional. If the suffix is - omitted, this relation will match on any endpoint as long as the given - application is involved. - - In other words, this relation will match a spec if that spec could have - created this relation. - - :return: True if all specs match. - """ - - # Matches expects that the underlying application exists when it walks - # over the endpoints. - # This isn't directly required, but it validates that the framework - # has all the information available to it, when you walk over all the - # relations. - # The one exception is remote- applications aren't real - # applications in the general sense of a application, but are more akin - # to a shadow application. - def model_application_exists(app_name): - model_app_name = None - if app_name in self.model.applications: - model_app_name = self.model.applications[app_name].name - elif app_name in self.model.remote_applications: - model_app_name = self.model.remote_applications[app_name].name - elif app_name in self.model.application_offers: - model_app_name = self.model.application_offers[app_name].name - return model_app_name == app_name - - for spec in specs: - if ":" in spec: - app_name, endpoint_name = spec.split(":") - else: - app_name, endpoint_name = spec, None - for endpoint in self.endpoints: - if ( - app_name == endpoint.application_name - and model_application_exists(app_name) - and endpoint_name in (endpoint.name, None) - ): - # found a match for this spec, so move to next one - break - else: - # no match for this spec - return False - return True - - @property - def applications(self): - """All applications involved in this relation.""" - return [ep.application for ep in self.endpoints] diff --git a/build/lib/juju/remoteapplication.py b/build/lib/juju/remoteapplication.py deleted file mode 100644 index 4cd16cdc..00000000 --- a/build/lib/juju/remoteapplication.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import logging - -from . import model, tag - -log = logging.getLogger(__name__) - - -class RemoteApplication(model.ModelEntity): - @property - def status(self): - """Get the application status, as set by the charm's leader.""" - return self.safe_data["status"]["current"] - - @property - def status_message(self): - """Get the application status message, as set by the charm's leader.""" - return self.safe_data["status"]["message"] - - @property - def tag(self): - return tag.application(self.name) - - -class ApplicationOffer(model.ModelEntity): - @property - def tag(self): - return tag.application(self.name) - - @property - def offer_name(self): - return self.safe_data["offer-name"] - - @property - def application_name(self): - return self.safe_data["application-name"] diff --git a/build/lib/juju/secrets.py b/build/lib/juju/secrets.py deleted file mode 100644 index 5a47f1ef..00000000 --- a/build/lib/juju/secrets.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -"""A utility module for secrets such as reading secret data from yaml and creating data bag for secrets.""" - -import base64 -import json -import re -from pathlib import Path - -import yaml - -from . import errors - -file_suffix = "#file" -max_value_size_bytes = 5 * 1024 -max_content_size_bytes = 64 * 1024 - - -def create_secret_data(args): - """CreateSecretData creates a secret data bag from a list of arguments. - If a key has the #base64 suffix, then the value is already base64 encoded,otherwise the value is base64 encoded as it is added to the data bag. - - If a key has the '#file' suffix, the value is read from the corresponding file. - - :return []str: bag of key value pairs for a secret - """ - data = {} - for val in args: - # Remove any base64 padding ("=") before splitting the key=value. - stripped = val.rstrip(base64.b64encode(b"=").decode("utf-8")) - idx = stripped.find("=") - if idx < 1: - raise ValueError(f"Invalid key value {val}") - - key = stripped[0:idx] - value = stripped[idx + 1 :] - - # If the key doesn't have the #file suffix, then add it to the bag and continue. - if not key.endswith(file_suffix): - data[key] = value - continue - - key = key.rstrip(file_suffix) - path = Path(value).resolve() - try: - fs = path.stat() - if fs.st_size > max_value_size_bytes: - raise ValueError( - f"Secret content in file {path} too large: {fs.st_size} bytes" - ) - content = path.read_text() - data[key] = content - except Exception as e: - raise ValueError(f"Error processing key {key}: {e}") - - return encode_values_base64(data) - - -def read_secret_data(file): - """ReadSecretData reads secret data from a YAML or JSON file as key value pairs. - - :param file str: Path to a YAML or JSON file to read values from. - - :return []str: bag of key value pairs for a secret - """ - data = {} - path = Path(file).resolve() - - try: - fs = path.stat() - if fs.st_size > max_content_size_bytes: - raise ValueError( - f"Secret content in file {path} too large: {fs.st_size} bytes" - ) - except FileNotFoundError: - raise FileNotFoundError(f"The file {path} does not exist.") - except OSError: - raise - - try: - with open(path, encoding="utf-8") as file: - data = file.read() - except Exception: - raise - - try: - data = json.loads(data) - except json.JSONDecodeError: - try: - data = yaml.safe_load(data) - except yaml.YAMLError: - raise errors.JujuNotValid(f"Invalid data file at: {file}") - - return encode_values_base64(data) - - -base64_suffix = "#base64" -key_reg_exp = re.compile("^([a-z](?:-?[a-z0-9]){2,})$") - - -def encode_values_base64(data): - """Encodes the values in the given data bag for a secret - - If a key has the #base64 suffix, then the value is already base64 encoded,otherwise the value is base64 encoded as it is added to the data bag. - """ - out = {} - content_size = 0 - for k, v in data.items(): - if len(v) > max_value_size_bytes: - raise ValueError(f"secret content for key {k} too large: {len(v)} bytes") - content_size += len(v) - if k.endswith(base64_suffix): - k = k[:-7] - if not key_reg_exp.match(k): - raise ValueError(f"Not valid key: {k}") - out[k] = v - continue - if not key_reg_exp.match(k): - raise ValueError(f"Not valid key: {k}") - out[k] = base64.b64encode(str(v).encode()).decode() - if content_size > max_content_size_bytes: - raise ValueError(f"secret content too large: {content_size} bytes") - return out diff --git a/build/lib/juju/status.py b/build/lib/juju/status.py deleted file mode 100644 index 12336d47..00000000 --- a/build/lib/juju/status.py +++ /dev/null @@ -1,252 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -import logging -import sys -import warnings - -if sys.version_info >= (3, 11): - from enum import StrEnum -else: - from backports.strenum import StrEnum - -from .client import client - -log = logging.getLogger(__name__) - - -class StatusStr(StrEnum): - """Recognised status values. - - Please keep this set exact same as the severity map below. - """ - - error = "error" - blocked = "blocked" - waiting = "waiting" - maintenance = "maintenance" - active = "active" - terminated = "terminated" - unknown = "unknown" - - -""" severity_map holds status values with a severity measure. -Status values with higher severity are used in preference to others. -""" -severity_map: dict[StatusStr, int] = { - # FIXME: Juju defines a lot more status values #1204 - StatusStr.error: 100, - StatusStr.blocked: 90, - StatusStr.waiting: 80, - StatusStr.maintenance: 70, - StatusStr.active: 60, - StatusStr.terminated: 50, - StatusStr.unknown: 40, -} - - -def derive_status(statuses: list[str | StatusStr]) -> StatusStr: - """Derive status from a set. - - derive_status is used to determine the application status from a set of unit - status values. - - :param statuses: list of known unit workload statuses - """ - current: StatusStr = StatusStr.unknown - for status in statuses: - try: - status = StatusStr(status) - except ValueError: - # Unknown Juju status, let's assume it's least important - continue - - if severity_map[status] > severity_map[current]: - current = status - return current - - -async def formatted_status(model, target=None, raw=False, filters=None): - """Returns a string that mimics the content of the information - returned in the juju status command. If the raw parameter is - enabled, the function returns a FullStatus object. - :param Model model: model object to be used - :param Fileobject target: if set expects a file object such as - sys.stdout or a file descriptor. The obtained status will - be sent to the file using the write function. If set to - `None`, this function returns a string with the formatted - status. - :param bool raw: if `true` this functions returns the raw - `FullStatus` object returned by Juju. This is similar to - invoking `get_status`. - :param str filters: Optional list of applications, units, or machines - to include, which can use wildcards ('*'). - """ - warnings.warn( - "juju.status.formatted_status is deprecated, the implementation is likely broken", - DeprecationWarning, - stacklevel=2, - ) - client_facade = client.ClientFacade.from_connection(model.connection()) - result_status = await client_facade.FullStatus(patterns=filters) - - if raw: - result_str = str(result_status) - else: - result_str = _print_status_model(result_status) - result_str += "\n" - result_str += _print_status_apps(result_status) - result_str += "\n" - result_str += _print_status_units(result_status) - result_str += "\n" - result_str += _print_status_machines(result_status) - result_str += "\n" - if target is None: - return result_str - - try: - target.write(result_str) - except Exception as e: - logging.error(e) - - return None - - -def _print_status_model(result_status): - """Private function to print the status of a model""" - m = result_status.model - # print model - result_str = "{:<25} {:<25} {:<15} {:<15} {:<30} {:<30}\n".format( - "Model", "Cloud/Region", "Version", "SLA", "Timestamp", "Notes" - ) - sla = m.unknown_fields["sla"] - cloud = m.cloud_tag.split("-")[1] - timestamp = result_status.controller_timestamp - if m.available_version is not None and m.available_version != "": - available_version = f"upgrade available: {m.available_version}" - else: - available_version = "" - result_str += "{:<25} {:<25} {:<15} {:<15} {:<30} {:<30}".format( - m.name, cloud + "/" + m.region, m.version, sla, timestamp, available_version - ) - result_str += "\n" - return result_str - - -def _print_status_apps(result_status): - """Auxiliary function to print the apps received - in a status result - """ - apps = result_status.applications - if apps is None or len(apps) == 0: - return "" - - limits = "{:<25} {:<10} {:<10} {:<5} {:<20} {:<8}" - # print header - result_str = limits.format("App", "Version", "Status", "Scale", "Charm", "Channel") - - for name, app in apps.items(): - # extract charm name from the path - # like in ch:amd64/trusty/mediawiki-28 - charm_name = app.charm.split("/")[-1] - charm_name = charm_name.split("-")[0] - work_ver = "NA" if app.workload_version is None else app.workload_version - charm_channel = "NA" if app.charm_channel is None else app.charm_channel - app_units = "NA" if app.units is None else len(app.units) - app_status = "NA" if app.status.status is None else app.status.status - result_str += "\n" - result_str += limits.format( - name, work_ver, app_status, app_units, charm_name, charm_channel - ) - result_str += "\n" - return result_str - - -def _print_status_units(result_status): - """Auxiliary function to print the units received - in a status result - """ - apps = result_status.applications - if apps is None or len(apps) == 0: - return - - limits = "{:<15} {:<15} {:<20} {:<10} {:<15} {:<10} {:<30}" - summary = "" - for app in apps.values(): - units = app.units - if units is None or len(units) == 0: - continue - - for name, unit in units.items(): - addr = unit.public_address - if addr is None: - addr = "" - - if unit.opened_ports is None: - opened_ports = "" - else: - opened_ports = ",".join(unit.opened_ports) - - info = unit.workload_status.info - if info is None: - info = "" - - step = limits.format( - name, - unit.workload_status.status, - unit.agent_status.status, - unit.machine, - addr, - opened_ports, - info, - ) - if summary == "": - summary = step - else: - summary = summary + "\n" + step - - if len(summary) == 0: - return "" - result_str = limits.format( - "Unit", "Workload", "Agent", "Machine", "Public address", "Ports", "Message" - ) - result_str += "\n" - result_str += summary - result_str += "\n" - return result_str - - -def _print_status_machines(result_status): - machines = result_status.machines - if machines is None or len(machines) == 0: - return - - limits = "{:<15} {:<15} {:<15} {:<20} {:<15} {:<30}" - summary = "" - for name, machine in machines.items(): - dns = machine.dns_name - if dns is None: - dns = "" - step = limits.format( - name, - machine.agent_status.status, - dns, - machine.instance_id, - machine.series, - machine.agent_status.info, - ) - if summary == "": - summary = step - else: - summary = summary + "\n" + step - - if summary == "": - return - result_str = limits.format( - "Machine", "State", "DNS", "Inst id", "Series", "Message" - ) - result_str += "\n" - result_str += summary - result_str += "\n" - return result_str diff --git a/build/lib/juju/tag.py b/build/lib/juju/tag.py deleted file mode 100644 index 5057d639..00000000 --- a/build/lib/juju/tag.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -# TODO: Tags should be a proper class, so that we can distinguish whether -# something is already a tag or not. For example, 'user-foo' is a valid -# username, but is ambiguous with the already-tagged username 'foo'. - - -def _prefix(prefix: str, s: str) -> str: - if s and not s.startswith(prefix): - return f"{prefix}{s}" - return s - - -def untag(prefix: str, s: str) -> str: - if s and s.startswith(prefix): - return s[len(prefix) :] - return s - - -def cloud(cloud_name: str) -> str: - return _prefix("cloud-", cloud_name) - - -def controller(controller_uuid: str) -> str: - return _prefix("controller-", controller_uuid) - - -def credential(cloud: str, user: str, credential_name: str) -> str: - credential_string = f"{cloud}_{user}_{credential_name}" - return _prefix("cloudcred-", credential_string) - - -def model(model_uuid: str) -> str: - return _prefix("model-", model_uuid) - - -def machine(machine_id: str) -> str: - return _prefix("machine-", machine_id) - - -def user(username: str) -> str: - return _prefix("user-", username) - - -def application(app_name: str) -> str: - return _prefix("application-", app_name) - - -def storage(app_name: str) -> str: - return _prefix("storage-", app_name) - - -def unit(unit_name: str) -> str: - return _prefix("unit-", unit_name.replace("/", "-")) - - -def action(action_uuid: str) -> str: - return _prefix("action-", action_uuid) - - -def space(space_name: str) -> str: - return _prefix("space-", space_name) diff --git a/build/lib/juju/unit.py b/build/lib/juju/unit.py deleted file mode 100644 index 4cb8e259..00000000 --- a/build/lib/juju/unit.py +++ /dev/null @@ -1,446 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import logging - -import pyrfc3339 - -from juju.errors import JujuAPIError, JujuError - -from . import model, tag -from .annotationhelper import _get_annotations, _set_annotations -from .client import client - -log = logging.getLogger(__name__) - - -class Unit(model.ModelEntity): - name: str - - @property - def agent_status(self): - """Returns the current agent status string.""" - return self.safe_data["agent-status"]["current"] - - @property - def agent_status_since(self): - """Get the time when the `agent_status` was last updated.""" - return pyrfc3339.parse(self.safe_data["agent-status"]["since"]) - - @property - def is_subordinate(self): - """True if the unit is subordinate of another unit""" - return self.safe_data["subordinate"] - - @property - def principal_unit(self): - """Returns the name of the unit of which this unit is a subordinate to. - Returns '' for principal units themselves. - """ - return self.safe_data["principal"] - - @property - def agent_status_message(self): - """Get the agent status message.""" - return self.safe_data["agent-status"]["message"] - - @property - def workload_status(self): - """Returns the current workload status string.""" - return self.safe_data["workload-status"]["current"] - - @property - def workload_status_since(self): - """Get the time when the `workload_status` was last updated.""" - return pyrfc3339.parse(self.safe_data["workload-status"]["since"]) - - @property - def workload_status_message(self): - """Get the workload status message.""" - return self.safe_data["workload-status"]["message"] - - @property - def machine(self): - """Get the machine object for this unit.""" - machine_id = self.safe_data["machine-id"] - if machine_id: - return self.model.machines.get(machine_id, None) - else: - return None - - @property - def public_address(self): - """Get the public address. - - This property is deprecated, use get_public_address method. - """ - return self.safe_data["public-address"] or None - - @property - def tag(self): - return tag.unit(self.name) - - def get_subordinates(self): - """Returns the unit objects that are subordinates to this unit - - :return [Unit] - """ - return [ - u - for u_name, u in self.model.units.items() - if u.is_subordinate and u.principal_unit == self.name - ] - - async def destroy( - self, destroy_storage=False, dry_run=False, force=False, max_wait=None - ): - """Destroy this unit.""" - app_facade = client.ApplicationFacade.from_connection(self.connection) - - log.debug("Destroying %s", self.name) - - return await app_facade.DestroyUnit( - units=[ - { - "unit-tag": self.tag, - "destroy-storage": destroy_storage, - "force": force, - "max-wait": max_wait, - "dry-run": dry_run, - } - ] - ) - - remove = destroy - - async def get_public_address(self): - """Return the public address of this unit. - - :return int public-address - """ - addr = self.safe_data["public-address"] or None - if addr is not None: - return addr - - app_facade = client.ApplicationFacade.from_connection(self.connection) - def_result = await app_facade.UnitsInfo(entities=[client.Entity(self.tag)]) - if def_result is not None and len(def_result.results) > 1: - raise JujuAPIError("expected one result") - return def_result.results[0].result.get("public-address", None) - - async def resolved(self, retry=False): - """Mark unit errors resolved. - - :param bool retry: Re-execute failed hooks - :returns: A :class:`juju.client._definitions.ErrorResults` instance. - """ - app_facade = client.ApplicationFacade.from_connection(self.connection) - - log.debug("Resolving %s", self.name) - - return await app_facade.ResolveUnitErrors( - all_=False, retry=retry, tags={"entities": [{"tag": self.tag}]} - ) - - async def add_storage(self, storage_name, pool=None, count=1, size=1024): - """Creates a storage and adds it to this unit. - - :param: str storage_name: Name of the storage - :param: str pool: the storage pool to provision storage instances from. Must - be a name from 'juju storage-pools'. The default pool is available via - executing 'juju model-config storage-default-block-source'. - :param: int count: the number of storage instances to provision from of - . Must be a positive integer. The default count is "1". May be restricted - by the charm, which can specify a maximum number of storage instances per unit. - :param: int size: the required size of the storage instance, in MiB. - - :return: []str storage_tags - """ - constraints = client.StorageConstraints(count=count, size=size) - if pool: - constraints = client.StorageConstraints(pool=pool, count=count, size=size) - - storage_facade = client.StorageFacade.from_connection(self.connection) - res = await storage_facade.AddToUnit( - storages=[ - client.StorageAddParams( - name=storage_name, - unit=self.tag, - storage=constraints, - ) - ] - ) - result = res.results[0] - if result.error is not None: - raise JujuError(f"{result.error}") - storage_details = result.result - return storage_details.storage_tags - - async def attach_storage(self, storage_ids=[]): - """Attaches existing storage to this unit. - - :param [str] storage_ids: existing storage ids to attach to the unit - :return: - """ - if not storage_ids: - raise JujuError(f"Expected a storage ID to be attached to unit {self.name}") - - storage_facade = client.StorageFacade.from_connection(self.connection) - return await storage_facade.Attach( - ids=[ - client.StorageAttachmentId( - storage_tag=tag.storage(s_id), - unit_tag=self.tag, - ) - for s_id in storage_ids - ] - ) - - async def detach_storage(self, *storage_ids, force=False): - """Detaches storage from units. - - :param bool force: Forcefully detach storage - :param [str] storage_ids: - :return: - """ - if not storage_ids: - raise JujuError("Expected at least one storage ID") - - storage_facade = client.StorageFacade.from_connection(self.connection) - ret = await storage_facade.DetachStorage( - force=force, - ids=client.StorageAttachmentIds( - ids=[ - client.StorageAttachmentId( - storage_tag=tag.storage(s), - unit_tag=self.tag, - ) - for s in storage_ids - ] - ), - ) - if ret.results[0].error: - raise JujuError(ret.results[0].error.message) - - async def run(self, command, timeout=None, block=False): - """Run command on this unit. - - :param str command: The command to run - :param int timeout: Time, in seconds, to wait before command is - considered failed - :param bool block: A flag to use this function in synchronized fashion. - Useful with older versions of juju, i.e. getting the result without - having to call ``action.wait()`` separately. - :returns: A :class:`juju.action.Action` instance. - - Note that this is very similarly to unit.run_action only enqueues the action. - You will need to call ``action.wait()`` on the resulting `Action` instance - if you wish to block until the action is complete. - - """ - action = client.ActionFacade.from_connection(self.connection) - - log.debug("Running `%s` on %s", command, self.name) - - if timeout: - # Convert seconds to nanoseconds - timeout = int(timeout * 1000000000) - - # It's not enough to only check for the old_client on the connection here - # The old client's ActionFacade is updated to version 7, so - # 2.9 track client may be using either ActionFacade v6 or v7 too - old_facade = client.ActionFacade.best_facade_version(self.connection) <= 6 - - res = await action.Run( - applications=[], - commands=command, - machines=[], - timeout=timeout, - units=[self.name], - ) - - action_result = res.results[0] if old_facade else res.actions[0] - action = action_result.action - - action_id = action.tag - if action_id.startswith("action-"): - # strip the action- part of "action-" tag - action_id = action_id[7:] - - error = action_result.error - if error: - raise JujuError(f"Action error - {error.code} : {error.message}") - - action = await self.model._wait_for_new("action", action_id) - if block: - return await action.wait() - return action - - async def run_action(self, action_name, **params): - """Run an action on this unit. - - :param str action_name: Name of action to run - :param **params: Action parameters - :returns: A :class:`juju.action.Action` instance. - - Note that this only enqueues the action. You will need to call - ``action.wait()`` on the resulting `Action` instance if you wish - to block until the action is complete. - - """ - action_facade = client.ActionFacade.from_connection(self.connection) - log.debug("Starting action `%s` on %s", action_name, self.name) - - old_client = self.connection.is_using_old_client - - op = action_facade.Enqueue if old_client else action_facade.EnqueueOperation - res = await op( - actions=[ - client.Action( - name=action_name, - parameters=params, - receiver=self.tag, - ) - ] - ) - - _action = res.results[0] if old_client else res.actions[0] - action = _action.action - error = _action.error - - if error and error.code == "not found": - raise ValueError("Action `%s` not found on %s" % (action_name, self.name)) - elif error: - raise Exception("Unknown action error: %s" % error.serialize()) - action_id = action.tag[len("action-") :] - log.debug("Action started as %s", action_id) - # we mustn't use wait_for_action because that blocks until the - # action is complete, rather than just being in the model - return await self.model._wait_for_new("action", action_id) - - async def scp_to( - self, source, destination, user="ubuntu", proxy=False, scp_opts="" - ): - """Transfer files to this unit. - - :param str source: Local path of file(s) to transfer - :param str destination: Remote destination of transferred files - :param str user: Remote username - :param bool proxy: Proxy through the Juju API server - :param scp_opts: Additional options to the `scp` command - :type scp_opts: str or list - """ - await self.machine.scp_to( - source, destination, user=user, proxy=proxy, scp_opts=scp_opts - ) - - async def scp_from( - self, source, destination, user="ubuntu", proxy=False, scp_opts="" - ): - """Transfer files from this unit. - - :param str source: Remote path of file(s) to transfer - :param str destination: Local destination of transferred files - :param str user: Remote username - :param bool proxy: Proxy through the Juju API server - :param scp_opts: Additional options to the `scp` command - :type scp_opts: str or list - """ - await self.machine.scp_from( - source, destination, user=user, proxy=proxy, scp_opts=scp_opts - ) - - async def ssh(self, command, user="ubuntu", proxy=False, ssh_opts=None): - """Execute a command over SSH on this unit. - - :param str command: Command to execute - :param str user: Remote username - :param bool proxy: Proxy through the Juju API server - :param str ssh_opts: Additional options to the `ssh` command - - """ - return await self.machine.ssh(command, user, proxy, ssh_opts) - - async def is_leader_from_status(self): - """Check to see if this unit is the leader. Returns True if so, and - False if it is not, or if leadership does not make sense - (e.g., there is no leader in this application.) - - This method is a kluge that calls FullStatus in the - ClientFacade to get its information. Once - https://bugs.launchpad.net/juju/+bug/1643691 is resolved, we - should add a simple .is_leader property, and deprecate this - method. - - """ - unit_parts = self.name.split("/") - app = unit_parts[0] - - client_facade = client.ClientFacade.from_connection(self.connection) - - status = await client_facade.FullStatus(patterns=None) - # FullStatus may be more up to date than our model, and the - # unit may have gone away, or we may be doing something silly, - # like trying to fetch leadership for a subordinate, which - # will not be filed where we expect in the model. In those - # cases, we may simply return False, as a nonexistent or - # subordinate unit is not a leader. - if not status.applications.get(app): - return False - - # We will attempt to look in two places for a leader property based on - # if the unit is subordinate or not. These variables allow for more - # generic non discriminate checks - target_apps = [app] - is_subordinate = False - - # Is the application a subordinate? If so change our data variables to - # the parent - if status.applications[app].subordinate_to: - is_subordinate = True - target_apps = status.applications[app].subordinate_to - - for target_app in target_apps: - app_data = status.applications[target_app] - - if not app_data.units: - continue - - if app_data.units.get(self.name): - is_leader = app_data.units[self.name].leader - return is_leader if is_leader else False - - if not is_subordinate: - continue - - for unit in app_data.units.values(): - if unit.subordinates and unit.subordinates.get(self.name): - is_leader = unit.subordinates[self.name].leader - return is_leader if is_leader else False - - return False - - async def get_metrics(self): - """Get metrics for the unit. - - :return: Dictionary of metrics for this unit. - - """ - metrics = await self.model.get_metrics(self.tag) - return metrics[self.name] - - async def get_annotations(self): - """Get annotations on this unit. - - :return dict: The annotations for this unit - """ - return await _get_annotations(self.tag, self.connection) - - async def set_annotations(self, annotations): - """Set annotations on this unit. - - :param annotations map[string]string: the annotations as key/value - pairs. - - """ - return await _set_annotations(self.tag, annotations, self.connection) diff --git a/build/lib/juju/url.py b/build/lib/juju/url.py deleted file mode 100644 index 3ad90546..00000000 --- a/build/lib/juju/url.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -from enum import Enum -from urllib.parse import urlparse - -from .errors import JujuError - - -class Schema(Enum): - """Charm URL schema kinds.""" - - LOCAL = "local" - CHARM_STORE = "cs" - CHARM_HUB = "ch" - - def matches(self, potential): - return str(self.value) == str(potential) - - def __str__(self): - return str(self.value) - - -class URL: - """Private URL class for this library internals only. - - Should be instantiated by `URL.parse` constructor. - """ - - name: str - - def __init__( - self, - schema, - user=None, - name: str | None = None, - revision=None, - series=None, - architecture=None, - ): - self.schema = schema - self.user = user - # the parse method will set the correct value later - self.name = name # type: ignore - self.series = series - - # 0 can be a valid revision, hence the more verbose check. - if revision is None: - revision = -1 - self.revision = revision - self.architecture = architecture - - @staticmethod - def parse(s: str, default_store=Schema.CHARM_HUB) -> URL: - """Parse parses the provided charm URL string into its respective - structure. - - A missing schema is assumed to be 'ch'. - - """ - u = urlparse(s) - if u.query != "" or u.fragment != "" or u.username or u.password: - raise JujuError(f"charm or bundle URL {u} has unrecognized parts") - - if Schema.CHARM_STORE.matches(u.scheme) or ( - u.scheme == "" and Schema.CHARM_STORE.matches(default_store) - ): - c = parse_v1_url(Schema.CHARM_STORE, u, s) - else: - c = parse_v2_url(u, s, default_store) - - if not c or not c.schema: - raise JujuError(f"expected schema for charm or bundle URL {u}") - return c - - def with_revision(self, rev): - return URL( - self.schema, self.user, self.name, rev, self.series, self.architecture - ) - - def with_series(self, series): - return URL( - self.schema, self.user, self.name, self.revision, series, self.architecture - ) - - def path(self): - parts = [] - if self.user is not None: - parts.append(f"~{self.user}") - if self.architecture is not None: - parts.append(self.architecture) - if self.series is not None: - parts.append(self.series) - if self.revision is not None and self.revision >= 0: - parts.append(f"{self.name}-{self.revision}") - else: - parts.append(self.name) - return "/".join(parts) - - def __eq__(self, other): - if isinstance(other, self.__class__): - return ( - self.schema == other.schema - and self.user == other.user - and self.name == other.name - and self.revision == other.revision - and self.series == other.series - and self.architecture == other.architecture - ) - return False - - def __str__(self): - return f"{self.schema!s}:{self.path()}" - - -def parse_v1_url(schema, u, s) -> URL: - c = URL(schema) - - parts = u.path.split("/") - if len(parts) < 1 or len(parts) > 4: - raise JujuError(f"charm or bundle URL has invalid form {s}") - - # ~ - if parts[0].startswith("~"): - if schema == Schema.LOCAL: - raise JujuError(f"local charm or bundle URL with username {s}") - c.user = parts[0][1:] - parts = parts[1:] - - if len(parts) > 2: - raise JujuError(f"charm or bundle URL has invalid form {s}") - - # - if len(parts) == 2: - c.series = parts[0] - parts = parts[1:] - # TODO (stickupkid) - validate the series. - - if len(parts) < 1: - raise JujuError(f"URL without charm or bundle name {s}") - - (c.name, c.revision) = extract_revision(parts[0]) - # TODO (stickupkid) - validate the name. - - return c - - -def parse_v2_url(u, s, default_store) -> URL: - if not u.scheme: - c = URL(default_store) - elif Schema.CHARM_HUB.matches(u.scheme): - c = URL(Schema.CHARM_HUB) - elif Schema.LOCAL.matches(u.scheme): - c = URL(Schema.LOCAL) - else: - raise JujuError(f"invalid charm url schema {u.scheme}") - - parts = u.path.split("/") - num = len(parts) - if num == 0 or num > 3: - raise JujuError(f"charm or bundle URL {s} malformed") - - name = "" - if num == 3: - c.architecture, c.series, name = parts[0], parts[1], parts[2] - elif num == 2: - c.architecture, name = parts[0], parts[1] - else: - name = parts[0] - - (c.name, c.revision) = extract_revision(name) - # TODO (stickupkid) - validate the name. - - return c - - -def extract_revision(name): - revision = -1 - for i in range(len(name) - 1, -1, -1): - c = name[i] - if c.isnumeric(): - continue - if c == "-" and i != (len(name) - 1): - revision = int(name[(i + 1) :]) - name = name[:i] - break - return (name, revision) diff --git a/build/lib/juju/user.py b/build/lib/juju/user.py deleted file mode 100644 index 7ff0fcc3..00000000 --- a/build/lib/juju/user.py +++ /dev/null @@ -1,183 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. - -import logging - -import pyrfc3339 - -from . import errors, tag -from .client import client - -log = logging.getLogger(__name__) - - -class User: - def __init__(self, controller, user_info, secret_key=None): - self.controller = controller - self._user_info = user_info - self._secret_key = secret_key - - @property - def tag(self): - return tag.user(self.username) - - @property - def username(self): - return self._user_info.username - - @property - def display_name(self): - return self._user_info.display_name - - @property - def last_connection(self): - return pyrfc3339.parse(self._user_info.last_connection) - - @property - def access(self): - """Identifies the controller access levels of this user""" - return self._user_info.access - - @property - def date_created(self): - return self._user_info.date_created - - @property - def enabled(self): - return not self._user_info.disabled - - @property - def disabled(self): - return self._user_info.disabled - - @property - def created_by(self): - return self._user_info.created_by - - @property - def secret_key(self): - return self._secret_key - - async def set_password(self, password): - """Update this user's password.""" - await self.controller.change_user_password(self.username, password) - self._user_info.password = password - - async def modify_model_access(self, acl, action, model_name): - """Grants or revokes the given access level for this user for a given model - - :param str acl: Model access levels (see access module) - :param str action: grant/revoke - :param str model_name: Name of the model - - :return bool: True if access changed, Error if user already has it - """ - modelmanager_facade = client.ModelManagerFacade.from_connection( - self.controller.connection() - ) - models = await self.controller.model_uuids() - if model_name not in models: - raise errors.JujuError(f"Unable to find model : {model_name}") - changes = client.ModifyModelAccess( - acl, action, tag.model(models[model_name]), self.tag - ) - await modelmanager_facade.ModifyModelAccess(changes=[changes]) - return True - - async def modify_controller_access(self, acl, action): - """Grants or revokes the given access level for this user on the current controller - - :param str acl: Controller access levels (see access module) - :param str action: grant/revoke - - :return bool: True if access changed, Error if user already has it - """ - controller_facade = client.ControllerFacade.from_connection( - self.controller.connection() - ) - changes = client.ModifyControllerAccess(acl, action, self.tag) - await controller_facade.ModifyControllerAccess(changes=[changes]) - - new_access = acl - if action == "revoke": - new_access = "" - self._user_info.access = new_access - return True - - async def modify_offer_access(self, acl, action, offer_url): - """Grants or revokes the given access level for this user on a given offer - - :param str acl: Controller access levels (see access module) - :param str action: grant/revoke - :param str offer_url: url for the offer - - :return bool: True if access changed, Error if user already has it - """ - application_offers_facade = client.ApplicationOffersFacade.from_connection( - self.controller.connection() - ) - changes = client.ModifyOfferAccess(acl, action, offer_url, self.tag) - await application_offers_facade.ModifyOfferAccess(changes=[changes]) - return True - - async def grant_or_revoke(self, acl, action, **kwargs): - """Grants or revokes the given access level of this user on model, offer or controller, - depending on the access level (see the access module) - - :param str acl: Access control level - :param str action: 'grant' or 'revoke' - - Depending on the access level, the available keyword parameters are: - :param str model_name: name of the model if acl is one of model access levels - :param str offer_url: url for the offer if acl is one of offer access levels - - :return: True if access changed, False if user already has it - """ - try: - if "model_name" in kwargs: - return await self.modify_model_access(acl, action, kwargs["model_name"]) - elif "offer_url" in kwargs: - return await self.modify_offer_access(acl, action, kwargs["offer_url"]) - else: - return await self.modify_controller_access(acl, action) - except errors.JujuError as e: - if "user already has" in str(e): - return False - else: - raise - - async def grant(self, acl, **kwargs): - """Grant the given access level of this user on model, offer or controller, depending on - the access level (see the access module) - - :param str acl: Access control level - - Depending on the access level, the available keyword parameters are: - :param str model_name: name of the model if acl is one of model access levels - :param str offer_url: url for the offer if acl is one of offer access levels - - :return: None or Error - """ - return await self.grant_or_revoke(acl, "grant", **kwargs) - - async def revoke(self, acl="login", **kwargs): - """The opposite of user.grant(). Revokes the given access level of this user on model, - offer or controller, depending on the given access level. - - :param str acl: Access control level (see access module) - - Available keyword parameters are: - :param str model_name: name of the model if acl is one of model access levels - :param str offer_url: url for the offer if acl is one of offer access levels - """ - return await self.grant_or_revoke(acl, "revoke", **kwargs) - - async def disable(self): - """Disable this user.""" - await self.controller.disable_user(self.username) - self._user_info.disabled = True - - async def enable(self): - """Re-enable this user.""" - await self.controller.enable_user(self.username) - self._user_info.disabled = False diff --git a/build/lib/juju/utils.py b/build/lib/juju/utils.py deleted file mode 100644 index 710fcc56..00000000 --- a/build/lib/juju/utils.py +++ /dev/null @@ -1,602 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -from __future__ import annotations - -import asyncio -import base64 -import os -import textwrap -import zipfile -from collections import defaultdict -from pathlib import Path -from typing import Any - -import yaml -from pyasn1.codec.der.encoder import encode -from pyasn1.type import char, univ - -from . import errors, jasyncio, origin -from .client import client -from .errors import JujuError - - -async def execute_process(*cmd, log=None): - """Wrapper around asyncio.create_subprocess_exec.""" - p = await asyncio.create_subprocess_exec( - *cmd, - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await p.communicate() - if log: - log.debug("Exec %s -> %d", cmd, p.returncode) - if stdout: - log.debug(stdout.decode("utf-8")) - if stderr: - log.debug(stderr.decode("utf-8")) - return p.returncode == 0 - - -def juju_config_dir(): - """Resolves and returns the path string to the juju configuration folder - for the juju CLI tool. Of the following items, returns the first option - that works (top to bottom): - - * $JUJU_DATA - * $XDG_DATA_HOME/juju - * ~/.local/share/juju - """ - # Set it to ~/.local/share/juju as default - config_dir = Path("~/.local/share/juju") - - # Check $JUJU_DATA - if juju_data := os.environ.get("JUJU_DATA"): - config_dir = Path(juju_data) - # Secondly check: $XDG_DATA_HOME for ~/.local/share - elif xdg_data_home := os.environ.get("XDG_DATA_HOME"): - config_dir = Path(xdg_data_home) / "juju" - - return str(config_dir.expanduser().resolve()) - - -def juju_ssh_key_paths(): - """Resolves and returns the path strings for public and private ssh keys - for juju CLI. - """ - config_dir = juju_config_dir() - public_key_path = os.path.join(config_dir, "ssh", "juju_id_rsa.pub") - private_key_path = os.path.join(config_dir, "ssh", "juju_id_rsa") - - return public_key_path, private_key_path - - -def _read_ssh_key(): - """Inner function for read_ssh_key, suitable for passing to our - Executor. - """ - public_key_path_str, _ = juju_ssh_key_paths() - ssh_key_path = Path(public_key_path_str) - with ssh_key_path.open("r") as ssh_key_file: - ssh_key = ssh_key_file.readlines()[0].strip() - return ssh_key - - -async def read_ssh_key(): - """Attempt to read the local juju admin's public ssh key, so that it can be - passed on to a model. - """ - loop = asyncio.get_running_loop() - return await loop.run_in_executor(None, _read_ssh_key) - - -class IdQueue: - """Wrapper around asyncio.Queue that maintains a separate queue for each - ID. - """ - - _queues: dict[int, asyncio.Queue[dict[str, Any] | Exception]] - - def __init__(self): - self._queues = defaultdict(asyncio.Queue) - # FIXME cleanup needed. - # in some cases an Exception is put into the queue. - # if the main coro exits, this exception will be logged as "never awaited" - # we gotta do something about that to keep the output clean. - # - # Additionally, it's conceivable that a response is put in the queue - # and then an exception is put via put_all() - # the reader only ever fetches one item, and exception is "never awaited" - # rewrite put_all to replace the pending response instead. - - async def get(self, id_: int) -> dict[str, Any]: - value = await self._queues[id_].get() - del self._queues[id_] - if isinstance(value, Exception): - raise value - return value - - async def put(self, id_: int, value: dict[str, Any]): - await self._queues[id_].put(value) - - async def put_all(self, value: Exception): - for queue in self._queues.values(): - await queue.put(value) - - -async def block_until(*conditions, timeout=None, wait_period=0.5): - """Return only after all conditions are true. - - If a timeout occurs, it cancels the task and raises - asyncio.TimeoutError. - """ - - async def _block(): - while not all(c() for c in conditions): - await asyncio.sleep(wait_period) - - await asyncio.shield(asyncio.wait_for(_block(), timeout)) - - -async def block_until_with_coroutine( - condition_coroutine, timeout=None, wait_period=0.5 -): - """Return only after the given coroutine returns True. - - If a timeout occurs, it cancels the task and raises - asyncio.TimeoutError. - """ - - async def _block(): - while not await condition_coroutine(): - await asyncio.sleep(wait_period) - - await asyncio.shield(asyncio.wait_for(_block(), timeout=timeout)) - - -async def wait_for_bundle(model, bundle: str | Path, **kwargs) -> None: - """Helper to wait for just the apps in a specific bundle. - - Equivalent to loading the bundle, pulling out the app names, and calling:: - - await model.wait_for_idle(app_names, **kwargs) - """ - try: - bundle_path = Path(bundle) - if bundle_path.is_file(): - bundle = bundle_path.read_text() - elif (bundle_path / "bundle.yaml").is_file(): - bundle = bundle_path / "bundle.yaml" - except OSError: - pass - content: dict[str, Any] = yaml.safe_load(textwrap.dedent(bundle).strip()) - apps = list(content.get("applications", content.get("services")).keys()) - await model.wait_for_idle(apps, **kwargs) - - -async def run_with_interrupt(task, *events, log=None): - """Awaits a task while allowing it to be interrupted by one or more - `asyncio.Event`s. - - If the task finishes without the events becoming set, the results of the - task will be returned. If the event become set, the task will be cancelled - ``None`` will be returned. - - :param task: Task to run - :param events: One or more `asyncio.Event`s which, if set, will interrupt - `task` and cause it to be cancelled. - """ - task = jasyncio.create_task_with_handler(task, "tmp", log) - event_tasks = [jasyncio.ensure_future(event.wait()) for event in events] - done, pending = await jasyncio.wait( - [task, *event_tasks], return_when=jasyncio.FIRST_COMPLETED - ) - for f in pending: - f.cancel() # cancel unfinished tasks - for f in pending: - try: - await f - except jasyncio.CancelledError: - pass - for f in done: - f.exception() # prevent "exception was not retrieved" errors - if task in done: - return task.result() # may raise exception - else: - return None - - -class Addrs(univ.SequenceOf): - """Internal.""" - - componentType = char.PrintableString() - - -class RegistrationInfo(univ.Sequence): - """ASN.1 representation of: - - type RegistrationInfo struct { User string - - Addrs []string - - SecretKey []byte - - ControllerName string } - """ - - pass - - -def generate_user_controller_access_token( - username, controller_endpoints, secret_key, controller_name -): - """Implement in python what is currently done in GO. - - https://github.com/juju/juju/blob/a5ab92e/cmd/juju/user/utils.go#L16 - - :param username: name of the user to register - :param controller_endpoints: juju controller endpoints list in the format : - :param secret_key: base64 encoded string of the secret-key generated by juju - :param controller_name: name of the controller to register to. - """ - # Secret key is returned as base64 encoded string in: - # https://websockets.readthedocs.io/en/stable/_modules/websockets/protocol.html#WebSocketCommonProtocol.recv - # Decoding it before marshalling into the ASN.1 message - secret_key = base64.b64decode(secret_key) - addr = Addrs() - for endpoint in controller_endpoints: - addr.append(endpoint) - - registration_string = RegistrationInfo() - registration_string.setComponentByPosition(0, char.PrintableString(username)) - registration_string.setComponentByPosition(1, addr) - registration_string.setComponentByPosition(2, univ.OctetString(secret_key)) - registration_string.setComponentByPosition(3, char.PrintableString(controller_name)) - registration_string = encode(registration_string) - remainder = len(registration_string) % 3 - registration_string += b"\0" * (3 - remainder) - return base64.urlsafe_b64encode(registration_string) - - -def get_local_charm_data(path, yaml_file): - """Retrieve Metadata of a Charm from its path. - - :patam str path: Path of charm directory or .charm file :patam str - yaml_ - file: - name of the yaml file, can be either "metadata.yaml", or - "manifest.yaml", or "charmcraft.yaml" - - :return: Object of charm metadata - """ - if str(path).endswith(".charm"): - with zipfile.ZipFile(str(path), "r") as charm_file: - metadata = yaml.load(charm_file.read(yaml_file), Loader=yaml.FullLoader) - else: - entity_path = Path(path) - metadata_path = entity_path / yaml_file - if not metadata_path.exists(): - return {} - metadata = yaml.load(metadata_path.read_text(), Loader=yaml.FullLoader) - - return metadata - - -def get_local_charm_metadata(path): - return get_local_charm_data(path, "metadata.yaml") - - -def get_local_charm_manifest(path): - return get_local_charm_data(path, "manifest.yaml") - - -def get_local_charm_charmcraft_yaml(path): - return get_local_charm_data(path, "charmcraft.yaml") - - -PRECISE = "precise" -QUANTAL = "quantal" -RARING = "raring" -SAUCY = "saucy" -TRUSTY = "trusty" -UTOPIC = "utopic" -VIVID = "vivid" -WILY = "wily" -XENIAL = "xenial" -YAKKETY = "yakkety" -ZESTY = "zesty" -ARTFUL = "artful" -BIONIC = "bionic" -COSMIC = "cosmic" -DISCO = "disco" -EOAN = "eoan" -FOCAL = "focal" -GROOVY = "groovy" -HIRSUTE = "hirsute" -IMPISH = "impish" -JAMMY = "jammy" -KINETIC = "kinetic" -LUNAR = "lunar" -MANTIC = "mantic" -NOBLE = "noble" - -UBUNTU_SERIES = { - PRECISE: "12.04", - QUANTAL: "12.10", - RARING: "13.04", - SAUCY: "13.10", - TRUSTY: "14.04", - UTOPIC: "14.10", - VIVID: "15.04", - WILY: "15.10", - XENIAL: "16.04", - YAKKETY: "16.10", - ZESTY: "17.04", - ARTFUL: "17.10", - BIONIC: "18.04", - COSMIC: "18.10", - DISCO: "19.04", - EOAN: "19.10", - FOCAL: "20.04", - GROOVY: "20.10", - HIRSUTE: "21.04", - IMPISH: "21.10", - JAMMY: "22.04", - KINETIC: "22.10", - LUNAR: "23.04", - MANTIC: "23.10", - NOBLE: "24.04", -} - -KUBERNETES = "kubernetes" -KUBERNETES_SERIES = {KUBERNETES: "kubernetes"} - -ALL_SERIES_VERSIONS = {**UBUNTU_SERIES, **KUBERNETES_SERIES} - - -def get_series_version(series_name): - """get_series_version outputs the version of the OS based on the given - series e.g. jammy -> 22.04, kubernetes -> kubernetes. - - :param str series_name: name of the series - :return str: os version - """ - if series_name not in ALL_SERIES_VERSIONS: - raise errors.JujuError("Unknown series : %s", series_name) - return ALL_SERIES_VERSIONS[series_name] - - -def get_version_series(version): - """get_version_series is the opposite of the get_series_version. It outputs - the series based on given OS version. - - :param str version: version of the OS - return str: name of the series corresponding to the given version - """ - if version not in UBUNTU_SERIES.values(): - raise errors.JujuError("Unknown version : %s", version) - return list(UBUNTU_SERIES.keys())[list(UBUNTU_SERIES.values()).index(version)] - - -def get_local_charm_base(series, charm_path, base_class): - """Deduce the base [channel/osname] of a local charm based on what we know - already. - - :param str series: This may come from the argument or the - metadata.yaml - :param str charm_path: Path of charm directory/.charm file - :param class base_class: - :return: Instance of the baseCls with channel/osname information - """ - channel_for_base = "" - os_name_for_base = "" - - # We should know the series, so use it to get a channel - if series: - channel_for_base = get_series_version(series) if series else "" - if channel_for_base: - # we currently only support ubuntu series (statically) - # TODO (cderici) : go juju/core/series/supported.go and get the - # others here too - if series in KUBERNETES_SERIES: - os_name_for_base = "kubernetes" - else: - os_name_for_base = "ubuntu" - - # Check the charm manifest - if channel_for_base == "": - charm_manifest = get_local_charm_manifest(charm_path) - if "bases" in charm_manifest: - channel_for_base = charm_manifest["bases"][0]["channel"] - os_name_for_base = charm_manifest["bases"][0]["name"] - - # Also check the charmcraft.yaml - if channel_for_base == "": - charmcraft_yaml = get_local_charm_charmcraft_yaml(charm_path) - if "bases" in charmcraft_yaml: - channel_for_base = charmcraft_yaml["bases"][0]["run-on"][0]["channel"] - os_name_for_base = charmcraft_yaml["bases"][0]["run-on"][0]["name"] - - if channel_for_base == "": - raise errors.JujuError("Unable to determine base for charm : %s" % charm_path) - - # Legacy k8s charms - assume ubuntu focal - # as per juju/cmd/juju/application/utils.DeduceOrigin() - if channel_for_base == "kubernetes" or os_name_for_base == "kubernetes": - channel_for_base = "20.04/stable" - os_name_for_base = "ubuntu" - return base_class(channel_for_base, os_name_for_base) - - -def base_channel_to_series(channel): - """Returns the series string using the track inside the base channel. - - :param str channel: is track/risk (e.g. 20.04/stable) - :return: str series (e.g. focal) - """ - return get_version_series(origin.Channel.parse(channel).track) - - -def parse_base_arg(base): - """Parses a given base into a Client.Base object :param base str : The base - to deploy a charm (e.g. ubuntu@22.04) - """ - client.CharmBase() - if not (isinstance(base, str) and "@" in base): - raise errors.JujuError( - f"expected base string to contain os and channel separated by '@', got : {base}" - ) - - name, channel = base.split("@") - return client.Base(name=name, channel=channel) - - -DEFAULT_SUPPORTED_LTS = "jammy" -DEFAULT_SUPPORTED_LTS_BASE = client.Base(channel="22.04", name="ubuntu") - - -def base_channel_from_series(track, risk, series): - return ( - origin.Channel(track=track, risk=risk) - .normalize() - .compute_base_channel(series=series) - ) - - -def get_os_from_series(series): - if series in UBUNTU_SERIES: - return "ubuntu" - raise JujuError(f"os for the series {series} needs to be added") - - -def get_base_from_origin_or_channel(origin_or_channel, series=None): - channel, os_name = None, None - if series: - channel = base_channel_from_series( - origin_or_channel.track, origin_or_channel.risk, series - ) - os_name = get_os_from_series(series) - return client.Base(channel=channel, name=os_name) - - -def series_for_charm(requested_series, supported_series): - """series_for_charm takes a requested series and a list of series supported - by a charm and returns the series which is relevant. - - If the requested series is empty, then the first supported series is - used, otherwise the requested series is validated against the - supported series. - """ - if len(supported_series) == 1 and supported_series[0] == "": - raise JujuError("invalid supported series reported by charm : ['']") - if len(supported_series) == 0: - if requested_series == "": - raise JujuError("missing series") - return requested_series - - # use the charm default - if requested_series == "": - return supported_series[-1] - - for s in supported_series: - if requested_series == s: - return requested_series - raise JujuError( - f"requested series {requested_series} is not among the supported series {supported_series}" - ) - - -def user_requested(series_arg, supported_series, force): - series = series_for_charm(series_arg, supported_series) - if force: - series = series_arg - # Todo (cderici): validate the series with workload_series to see if juju is - # supporting that - return series - - -def series_selector( - series_arg="", charm_url=None, model_config=None, supported_series=[], force=False -): - """Select series to deploy on. - - series_selector corresponds to the CharmSeries() in - https://github.com/juju/juju/blob/develop/core/charm/series_selector.go - - determines what series to use with a charm. - Order of preference is: - - user requested with --series or defined by bundle when deploying - - user requested in charm's url (e.g. juju deploy jammy/ubuntu) - - model default, if set, acts like --series - - default from charm metadata supported series / series in url - - default LTS - """ - # User has requested a series with --series. - if series_arg: - return user_requested(series_arg, supported_series, force) - - # User specified a series in the charm URL, e.g. - # juju deploy precise/ubuntu. - if charm_url and charm_url.series: - return user_requested(charm_url.series, supported_series, force) - - # No series explicitly requested by the user. - # Use model default series, if explicitly set and supported by the charm. - if model_config and model_config["default-base"].value: - default_base = model_config["default-base"].value - base = parse_base_arg(default_base) - series = base_channel_to_series(base.channel) - return user_requested(series, supported_series, force) - - # Next fall back to the charm's list of series, filtered to what's supported - # by Juju. Preserve the order of the supported series from the charm - # metadata, as the order could be out of order compared to Ubuntu series - # order (precise, xenial, bionic, trusty, etc). - try: - # TODO (cderici): restrict the supported_series with JujuSupportedSeries - return user_requested("", supported_series, force) - except JujuError: - pass - - # Charm hasn't specified a default (likely due to being a local charm - # deployed by path). Last chance, best we can do is default to LTS. - return DEFAULT_SUPPORTED_LTS - - -def should_upgrade_resource(available_resource, existing_resources, arg_resources): - """Determine if the given resource should be upgraded. - - Called in the context of upgrade_charm. Given a resource R, takes a look - at the resources we already have and decides if we need to refresh R. - - :param dict[str] available_resource: The dict representing the - client.Resource coming from the charmhub api. We're considering if - we need to refresh this during upgrade_charm. :param dict[str] - existing_resources: The dict coming from - resources_facade.ListResources representing the resources of the - currently deployed charm. :param dict[str] arg_resources: user - provided resources to be refreshed - - :result bool: The decision to refresh the given resource - """ - # should upgrade resource? - res_name = available_resource.get("Name", available_resource.get("name")) - - if res_name in arg_resources: - return True - - # do we have it already? - if res_name in existing_resources: - # no upgrade, if it's upload - if existing_resources[res_name].origin == "upload": - return False - # no upgrade, if upstream doesn't have a newer revision of the resource - # available - available_rev = available_resource.get( - "Revision", available_resource.get("revision", -1) - ) - u_fields = existing_resources[res_name].unknown_fields - existing_rev = u_fields.get("Revision", u_fields.get("revision", -1)) - if existing_rev >= available_rev: - return False - return True diff --git a/build/lib/juju/version.py b/build/lib/juju/version.py deleted file mode 100644 index b3e36f02..00000000 --- a/build/lib/juju/version.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright 2023 Canonical Ltd. -# Licensed under the Apache V2, see LICENCE file for details. -"""Client version definitions.""" - -LTS_RELEASES = ["jammy", "focal", "bionic", "xenial", "trusty", "precise"] - -DEFAULT_ARCHITECTURE = "amd64" - -CLIENT_VERSION = "3.6.0.0" diff --git a/docs/conf.py b/docs/conf.py index 41c1b4c0..55c9e89e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,7 +48,7 @@ "sphinx.ext.viewcode", "sphinxcontrib.asyncio", "automembersummary", -# "sphinx_tabs.tabs" + # 'sphinx_tabs.tabs', ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/howto/#manage-machines.rst# b/docs/howto/#manage-machines.rst# deleted file mode 100644 index bcb3f92c..00000000 --- a/docs/howto/#manage-machines.rst# +++ /dev/null @@ -1,161 +0,0 @@ -.. _manage-machines: - -How to manage machines -====================== - -> See also: :ref:`juju:machine` - - -Add a machine -------------- - - -To add a machine to a model, on a connected Model object, use the `add_machine()` method. For example: - -.. code:: python - - await my_model.add_machine() - - -> See more: `add_machine() `_, `Model (module) `_ - - - -List all machines ------------------ - -To see a list the names of all the available machines on a model, on a connected Model object, use the `get_machines()` method. For example: - -.. code:: python - - await my_model.get_machines() - - -To get a list of the machines as Machine objects on a model, use the `machines` property on the Model object. This allows direct interaction with any of the machines on the model. For example: - -.. code:: python - - machines = my_model.machines - my_machine = machines[0] # Machine object - print(my_machine.status) - - -> See more: `get_machines() `_, `Model (module) `_, `Model.machines (property) `_, `Machine (object) `_ - - -View details about a machine ----------------------------- - -To see details about a machine, on a connected Model object, get a hold of the Machine object within the model using the `machines` property. This allows direct interaction with the machine, such as accessing all the details (via the object properties) for that machine. For example: - -.. code:: python - - my_machine = await my_model.machines[0] - # Then we can access all the properties to view details - print(my_machine.addresses) - print(my_machine.agent_version) - print(my_machine.hostname) - print(my_machine.status) - - -> See more: `Model (module) `_, `Model.machines (property) `_, `Machine (object) `_ - -Show the status of a machine ----------------------------- - -To see the status of a machine, on a connected Model object, get a hold of the Machine object within the model using the `machines` property. The status is then retrieved directly via the Machine object properties, in this case the `status` property. For example: - -.. code:: python - - my_machine = await my_model.machines[0] - print(my_machine.status) - - -> See more: `Model (module) `_, `Model.machines (property) `_, `Machine (object) `_, `Machine.status (property) `_ - - -Manage constraints for a machine --------------------------------- -> See also: :ref:`juju:constraint` - -**Set values.** To set constraint values for an individual machine when you create it manually, on a connected Model, use the `add_machine()` method, passing constraints as a parameter. For example: - -.. code:: python - - machine = await model.add_machine( - constraints={ - 'arch': 'amd64', - 'mem': 256 * MB, - }) - - -**Get values.** The `python-libjuju` client does not currently support getting constraint values for for an individual machine. However, to retrieve machine constraints on a model, on a connected Model, use the `get_constraints()` method. For example: - -.. code:: python - - await my_model.get_constraints() - - -Note that this will return `None` if no constraints have been set on the model. - -> See more: `add_machine() `_, `get_constraints() `_, `Model (module) `_ - - -Execute a command inside a machine ----------------------------------- - -To run a command in a machine, on a Machine object, use the `ssh()` method, passing a command as a parameter. For example: - -.. code:: python - - output = await my_machine.ssh("echo test") - assert 'test' in output - - -To run a command in all the machines corresponding to an application, on an Application object, use the `run()` method, passing the command as a parameter. For example: - -.. code:: python - - output = await my_application.run("echo test") - assert 'test' in output - - - -> See more: `ssh() `_, `Machine (object) `_, `run() `_, `Application (object) `_ - - -Copy files securely between machines ------------------------------------- - -To copy files securely between machines, on a Machine object, use the `scp_to()` and `scp_from()` methods, passing source and destination parameters for the transferred files or directories. For example: - -.. code:: python - - # Transfer from local machine to Juju machine represented by my_machine object - with open(file_name, 'r') as f: - await my_machine.scp_to(f.name, 'testfile') - - # Transfer from my_machine to local machine - with open(file_name, 'w') as f: - await my_machine.scp_from('testfile', f.name) - assert f.read() == b'contents_of_file' - - # Pass -r for recursively copy a directory via the `scp_opts` parameter. - await my_machine.scp_to('my_directory', 'testdirectory', scp_opts=['-r']) - - -> See more: `scp_to() `_, `scp_from() `_, `Machine (object) `_ - - - -Remove a machine ----------------- -> See also: :ref:`juju:removing-things` - -To remove a machine, on a Machine object, use the `destroy()` method. For example: - -.. code:: python - - await my_machine.destroy() - -> See more: `destroy() `_, `Machine (object) `_re diff --git a/docs/howto/index.rst b/docs/howto/index.rst index 88e1fb39..97348e4c 100644 --- a/docs/howto/index.rst +++ b/docs/howto/index.rst @@ -24,4 +24,3 @@ Manage storage Manage storage pools Manage spaces - diff --git a/docs/howto/manage-actions.rst b/docs/howto/manage-actions.rst index 05c8a9f3..c7429609 100644 --- a/docs/howto/manage-actions.rst +++ b/docs/howto/manage-actions.rst @@ -6,7 +6,7 @@ How to manage actions > See also: :ref:`juju:action` - + List all actions ---------------- @@ -15,7 +15,7 @@ List all actions To list the actions defined for a deployed application, use the `get_actions()` method on the `Application` object to get all the actions defined for this application. .. code:: python - + await my_app.get_actions() @@ -29,15 +29,12 @@ To run an action on a unit, use the `run_action()` method on a Unit object of a Note that "running" an action on a unit, enqueues an action to be performed. The result will be an Action object to interact with. You will need to call `action.wait()` on that object to wait for the action to complete and retrieve the results. .. code:: python - + # Assume we deployed a git application my_app = await model.deploy('git', application_name='git', channel='stable') my_unit = my_app.units[0] - + action = await my_unit.run_action('add-repo', repo='myrepo') await action.wait() # will return the result for the action - -> See more: `Unit (object) `_, `Action (object) `_, `Unit.run_action (method) `_, `Action.wait() (method) `_ - - +> See more: `Unit (object) `_, `Action (object) `_, `Unit.run_action (method) `_, `Action.wait() (method) `_ diff --git a/docs/howto/manage-applications.rst b/docs/howto/manage-applications.rst index faf5a371..11aab74d 100644 --- a/docs/howto/manage-applications.rst +++ b/docs/howto/manage-applications.rst @@ -15,7 +15,7 @@ To deploy an application, find and deploy a charm / bundle that delivers it. View details about an application --------------------------------- -To view details about an application on python-libjuju, you may use various `get_*` methods that are defined for applications. +To view details about an application on python-libjuju, you may use various `get_*` methods that are defined for applications. For example, to get the config for an application, call `get_config()` method on an `Application` object: @@ -30,17 +30,17 @@ For example, to get the config for an application, call `get_config()` method on Trust an application with a credential -------------------------------------- -Some applications may require access to the backing cloud in order to fulfil their purpose (e.g., storage-related tasks). In such cases, the remote credential associated with the current model would need to be shared with the application. When the Juju administrator allows this to occur the application is said to be *trusted*. +Some applications may require access to the backing cloud in order to fulfil their purpose (e.g., storage-related tasks). In such cases, the remote credential associated with the current model would need to be shared with the application. When the Juju administrator allows this to occur the application is said to be *trusted*. To trust an application during deployment in python-libjuju, you may call the `Model.deploy()` with the `trust` parameter: -.. code:: python +.. code:: python await my_model.deploy(..., trust=True, ...) To trust an application after deployment, you may use the `Application.set_trusted()` method: -.. code:: python +.. code:: python await my_app.set_trusted(True) @@ -58,7 +58,7 @@ Configure an application **Get values.** To view the existing configuration for an application on python-libjuju, you may use the `Application.get_config()` method: -.. code:: python +.. code:: python config = await my_app.get_config() @@ -67,14 +67,14 @@ Configure an application * To configure an application at deployment, simply provide a `config` map during the `Model.deploy()` call: -.. code:: python +.. code:: python await my_model.deploy(..., config={'redirect-map':'https://demo'}, ...) * To configure an application post deployment, you may use the `Application.set_config()` method, similar to passing config in the deploy call above: -.. code:: python +.. code:: python await my_app.set_config(config={'redirect-map':'https://demo'}) @@ -92,7 +92,7 @@ Scale an application vertically ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To scale an application vertically, set constraints for the resources that the application's units will be deployed on. - + > See more: :ref:`manage-constraints-for-an-application` Scale an application horizontally @@ -107,7 +107,7 @@ Make an application highly available ------------------------------------ > See also: :ref:`juju:high-availability` -1. Find out if the charm delivering the application supports high availability natively or not. If the latter, find out what you need to do. This could mean integrating with a load balancing reverse proxy, configuring storage etc. +1. Find out if the charm delivering the application supports high availability natively or not. If the latter, find out what you need to do. This could mean integrating with a load balancing reverse proxy, configuring storage etc. > See more: `Charmhub `_ @@ -115,7 +115,7 @@ Make an application highly available > See more: {ref}`How to scale an application horizontally <5476md>` -Every time a unit is added to an application, Juju will spread out that application's units, distributing them evenly as supported by the provider (e.g., across multiple availability zones) to best ensure high availability. So long as a cloud's availability zones don't all fail at once, and the charm and the charm's application are well written (changing leaders, coordinating across units, etc.), you can rest assured that cloud downtime will not affect your application. +Every time a unit is added to an application, Juju will spread out that application's units, distributing them evenly as supported by the provider (e.g., across multiple availability zones) to best ensure high availability. So long as a cloud's availability zones don't all fail at once, and the charm and the charm's application are well written (changing leaders, coordinating across units, etc.), you can rest assured that cloud downtime will not affect your application. > See more: `Charmhub | wordpress `_, `Charmhub | mediawiki `_, `Charmhub | haproxy `_ @@ -130,31 +130,31 @@ Manage an application’s public availability over the network To expose some or all endpoints of an application over a network, you may use the `Application.expose()` method, as follows: -.. code:: python +.. code:: python await my_app.expose(exposed_endpoints=None) # everything's reachable from 0.0.0.0/0. To expose to specific CIDRs or spaces, you may use an `ExposedEndpoint` object to describe that, as follows: -.. code:: python +.. code:: python # For spaces await my_app.expose(exposed_endpoints={"": ExposedEndpoint(to_spaces=["alpha"]) }) - + # For cidrs await my_app.expose(exposed_endpoints={"": ExposedEndpoint(to_cidrs=["10.0.0.0/24"])}) - + # You may use both at the same time too await my_app.expose(exposed_endpoints={ "ubuntu": ExposedEndpoint(to_spaces=["alpha"], to_cidrs=["10.0.0.0/24"]) }) - + To unexpose an application, use the `Application.unexpose()` method: -.. code:: python +.. code:: python await my_app.unexpose() # unexposes the entire application @@ -174,7 +174,7 @@ Manage constraints for an application * To set at deployment, simply provide a `constraints` map during the `Model.deploy()` call: -.. code:: python +.. code:: python await my_model.deploy(..., constraints={, 'arch': 'amd64', 'mem': 256}, ...) @@ -182,13 +182,13 @@ Manage constraints for an application * To set constraints post deployment, you may use the `Application.set_contraints()` method, similar to passing constraints in the deploy call above: .. code:: python - + await my_app.set_constraints(constraints={, 'arch': 'amd64', 'mem': 256}) **Get values.** To see what constraints are set on an application, use the `Application.get_constraints()` method: -.. code:: python +.. code:: python await my_app.get_constraints() @@ -201,7 +201,7 @@ Change space bindings for an application To set bindings for an application on python-libjuju, simply pass the `bind` parameter at the `Model.deploy()` call: -.. code:: python +.. code:: python await my_model.deploy(..., bind="db=db db-client=db public admin-api=public", ...) @@ -212,7 +212,7 @@ Python-libjuju currently doesn't support resetting space bindings post deploymen Upgrade an application ---------------------- -To upgrade an application, update its charm. +To upgrade an application, update its charm. > See more: :ref:`update-a-charm` @@ -228,13 +228,13 @@ To remove an application from a model in python-libjuju, you have two choices: (1) If you have a reference to a connected model object (connected to the model you're working on), then you may use the `Model.remove_application()` method: .. code:: python - + await my_model.remove_application(my_app.name) (2) If you have a reference to the application you want to remove, then you may use the `Application.destroy()` directly on the application object you want to remove: -.. code:: python +.. code:: python await my_app.destroy() diff --git a/docs/howto/manage-charm-resources.rst b/docs/howto/manage-charm-resources.rst index 9f7c5ef7..ccd2cf91 100644 --- a/docs/howto/manage-charm-resources.rst +++ b/docs/howto/manage-charm-resources.rst @@ -25,17 +25,17 @@ Specify the resources to be deployed with a charm To specify a resource during deployment, on a connected Model object, use the `deploy` method, passing the resources as a parameter. For example: .. code:: python - + resources = {"file-res": "test.file"} app = await model.deploy(charm_path, resources=resources) To update a resource after deployment by uploading file from local disk, on an Application object, use the `attach_resource()` method, passing resource name, file name and the file object as parameters. .. code:: python - + with open(str(charm_path / 'test.file')) as f: app.attach_resource('file-res', 'test.file', f) - + > See more: `deploy() `_, `attach_resource() `_, `Model (module) `_ diff --git a/docs/howto/manage-charms.rst b/docs/howto/manage-charms.rst index cd34a674..070534be 100644 --- a/docs/howto/manage-charms.rst +++ b/docs/howto/manage-charms.rst @@ -45,27 +45,27 @@ To deploy a Charmhub charm / bundle using python-libjuju, you can use the `deplo m = model.Model() await m.connect() - + # deploy a charm - await m.deploy('mysql') - + await m.deploy('mysql') + # deploy a bundle - await m.deploy('kubeflow') - + await m.deploy('kubeflow') + # deploy a local charm - await m.deploy('./mini_ubuntu-20.04-amd64.charm') - + await m.deploy('./mini_ubuntu-20.04-amd64.charm') + # deploy a local charm with a resource - await m.deploy('./demo-api-charm_ubuntu-22.04-amd64.charm', resources={'demo-server-image=ghcr.io/beliaev-maksim/api_demo_server':'0.0.9'}) - + await m.deploy('./demo-api-charm_ubuntu-22.04-amd64.charm', resources={'demo-server-image=ghcr.io/beliaev-maksim/api_demo_server':'0.0.9'}) + # deploy a local bundle - await m.deploy('./mediawiki-model-bundle.yaml') - + await m.deploy('./mediawiki-model-bundle.yaml') + # deploy a bundle with an overlay await m.deploy('mediawiki', overlays=['./custom-mediawiki.yaml']) - + # generic openstack example - await m.deploy('./bundle-focal-yoga.yaml', overlays=['./overlay-focal-yoga-mymaas.yaml', './overlay-focal-yoga-mymaas-shared-filesystem.yaml']) + await m.deploy('./bundle-focal-yoga.yaml', overlays=['./overlay-focal-yoga-mymaas.yaml', './overlay-focal-yoga-mymaas-shared-filesystem.yaml']) > See more: `Model.deploy() `_ @@ -81,19 +81,19 @@ To update a charm on python-libjuju, you can use the `upgrade_charm` (aliased as # upgrade to latest revision on the channel await my_app.upgrade_charm() - + # upgrade to the latest revision on a given channel await my_app.upgrade_charm(channel='latest/edge') - + # upgrade to a particular revision await my_app.upgrade_charm(revision=3) - + # upgrade with a local charm await my_app.upgrade_charm(path='./path/to/juju-test') - + # replace a charm completely with another charm await my_app.upgrade_charm(switch='./path/to/juju-test') - + # Note that the path and switch parameters are mutually exclusive. > See more: `Application.upgrade_charm() `_ @@ -105,4 +105,3 @@ Remove a charm / bundle As a charm / bundle is just the *means* by which (an) application(s) are deployed, there is no way to remove the *charm* / *bundle*. What you *can* do, however, is remove the *application* / *model*. > See more: :ref:`remove-an-application` - diff --git a/docs/howto/manage-clouds.rst b/docs/howto/manage-clouds.rst index 40fc6bde..b9a2d63e 100644 --- a/docs/howto/manage-clouds.rst +++ b/docs/howto/manage-clouds.rst @@ -19,8 +19,8 @@ To add a cloud, use the `Controller.add_cloud()` method on a connected `Controll .. code:: python from juju.client import client as jujuclient - - await my_controller.add_cloud("my-cloud", + + await my_controller.add_cloud("my-cloud", jujuclient.Cloud( auth_types=["userpass"], endpoint="http://localhost:1234", diff --git a/docs/howto/manage-controllers.rst b/docs/howto/manage-controllers.rst index 9be6f5aa..69c04ca6 100644 --- a/docs/howto/manage-controllers.rst +++ b/docs/howto/manage-controllers.rst @@ -26,7 +26,7 @@ To view details about a controller in `python-libjuju`, with a connected control .. code:: python await controller.info() - + > See more: `Controller.info() `_ @@ -36,17 +36,15 @@ Switch to a different controller To switch to a different controller with `python-libjuju`, simply connect to the controller you want to work with, which is done by calling `connect` on the `Controller `_ object (below, `controller`): .. code:: python - + from juju.model import Controller - + controller = Controller() await controller.connect() # will connect to the "current" controller - + await controller.connect('mycontroller') # will connect to the controller named "mycontroller" Note that if the `controller` object is already connected to a controller, then that connection will be closed before making the new connection. > See more: `Controller.connect() `_, `Connect with Authentication `_, `Connect with explicit endpoints `_ - - diff --git a/docs/howto/manage-credentials.rst b/docs/howto/manage-credentials.rst index 79e19bfd..1ebe2493 100644 --- a/docs/howto/manage-credentials.rst +++ b/docs/howto/manage-credentials.rst @@ -12,11 +12,10 @@ Update a credential To update a credential, on a connected `Controller` object, use the `Controller.add_credential()` method. `add_credential` is an upsert method (where it inserts if the given credential is new, and updates if the given credential name already exists). .. code:: python - + from juju.client import client as jujuclient - - my_controller.add_credential("my-credential", + + my_controller.add_credential("my-credential", jujuclient.CloudCredential(auth_type="jsonfile", attrs={'file':'path_to_cred_file'}) - -> See more: `add_credential (method) `_ +> See more: `add_credential (method) `_ diff --git a/docs/howto/manage-machines.rst b/docs/howto/manage-machines.rst index e5940db3..21c7aa23 100644 --- a/docs/howto/manage-machines.rst +++ b/docs/howto/manage-machines.rst @@ -34,7 +34,7 @@ To see a list the names of all the available machines on a model, on a connected To get a list of the machines as Machine objects on a model, use the `machines` property on the Model object. This allows direct interaction with any of the machines on the model. For example: .. code:: python - + machines = my_model.machines my_machine = machines[0] # Machine object print(my_machine.status) @@ -49,7 +49,7 @@ View details about a machine To see details about a machine, on a connected Model object, get a hold of the Machine object within the model using the `machines` property. This allows direct interaction with the machine, such as accessing all the details (via the object properties) for that machine. For example: .. code:: python - + my_machine = await my_model.machines[0] # Then we can access all the properties to view details print(my_machine.addresses) @@ -66,7 +66,7 @@ Show the status of a machine To see the status of a machine, on a connected Model object, get a hold of the Machine object within the model using the `machines` property. The status is then retrieved directly via the Machine object properties, in this case the `status` property. For example: .. code:: python - + my_machine = await my_model.machines[0] print(my_machine.status) @@ -81,7 +81,7 @@ Manage constraints for a machine **Set values.** To set constraint values for an individual machine when you create it manually, on a connected Model, use the `add_machine()` method, passing constraints as a parameter. For example: .. code:: python - + machine = await model.add_machine( constraints={ 'arch': 'amd64', @@ -92,7 +92,7 @@ Manage constraints for a machine **Get values.** The `python-libjuju` client does not currently support getting constraint values for for an individual machine. However, to retrieve machine constraints on a model, on a connected Model, use the `get_constraints()` method. For example: .. code:: python - + await my_model.get_constraints() @@ -107,7 +107,7 @@ Execute a command inside a machine To run a command in a machine, on a Machine object, use the `ssh()` method, passing a command as a parameter. For example: .. code:: python - + output = await my_machine.ssh("echo test") assert 'test' in output @@ -115,7 +115,7 @@ To run a command in a machine, on a Machine object, use the `ssh()` method, pass To run a command in all the machines corresponding to an application, on an Application object, use the `run()` method, passing the command as a parameter. For example: .. code:: python - + output = await my_application.run("echo test") assert 'test' in output @@ -130,19 +130,19 @@ Copy files securely between machines To copy files securely between machines, on a Machine object, use the `scp_to()` and `scp_from()` methods, passing source and destination parameters for the transferred files or directories. For example: .. code:: python - + # Transfer from local machine to Juju machine represented by my_machine object with open(file_name, 'r') as f: await my_machine.scp_to(f.name, 'testfile') - + # Transfer from my_machine to local machine with open(file_name, 'w') as f: await my_machine.scp_from('testfile', f.name) assert f.read() == b'contents_of_file' - + # Pass -r for recursively copy a directory via the `scp_opts` parameter. await my_machine.scp_to('my_directory', 'testdirectory', scp_opts=['-r']) - + > See more: `scp_to() `_, `scp_from() `_, `Machine (object) `_ @@ -155,7 +155,7 @@ Remove a machine To remove a machine, on a Machine object, use the `destroy()` method. For example: .. code:: python - + await my_machine.destroy() > See more: `destroy() `_, `Machine (object) `_ diff --git a/docs/howto/manage-models.rst b/docs/howto/manage-models.rst index b1f1d4d8..d79eb937 100644 --- a/docs/howto/manage-models.rst +++ b/docs/howto/manage-models.rst @@ -11,7 +11,7 @@ Add a model To add a model, on a connected controller, call the `add_model` function. For example, below we're adding a model called `test-model` on the `controller`: .. code:: python - + await controller.add_model("test-model") > See more: `Controller.add_model() `_, `juju_model (module) `_, `juju_controller (module) `_ @@ -23,7 +23,7 @@ View all the models available on a controller To view all the models available on a controller, call the `Controller.list_models()` function: .. code:: python - + await controller.list_models() > See more: `Controller.list_models() `_ @@ -35,12 +35,12 @@ Switch to a different model In `python-libjuju`, switching to a different model means simply connecting to the model you want to work with, which is done by calling `connect` on the `Model `_ object: .. code:: python - + from juju.model import Model - + model = Model() await model.connect() # will connect to the "current" model - + await model.connect(model_name="test-model") # will connect to the model named "test-model" Note that if the `model` object is already connected to a model, then that connection will be closed before making the new connection. @@ -81,9 +81,8 @@ Destroy a model To destroy a model, with a connected controller object, call the `Controller.destroy_model()` function. For example: .. code:: python - + await controller.destroy_model("test-model") > See more: `Controller.destroy_model() `_ - diff --git a/docs/howto/manage-offers.rst b/docs/howto/manage-offers.rst index d6f112d1..fef2e23a 100644 --- a/docs/howto/manage-offers.rst +++ b/docs/howto/manage-offers.rst @@ -15,7 +15,7 @@ Create an offer To create an offer, use the `create_offer()` method on a connected Model object. .. code:: python - + # Assume a deployed mysql application await my_model.deploy('mysql') # Expose the database endpoint of the mysql application @@ -31,10 +31,10 @@ Control access to an offer The access levels for offers can be applied in the same way the model or controller access for a given user. Use the `grant()` and `revoke()` methods on a User object to grant or revoke access to an offer. .. code:: python - + # Grant Bob consume access to an offer await user_bob.grant('consume', offer_name='admin/default.hosted-mysql') - + # Revoke Bob's consume access (he will be left with read access) await user_bob.revoke('consume', offer_name='admin/default.hosted-mysql') @@ -49,14 +49,14 @@ Integrate with an offer To integrate with an offer, on a connected model, use the `Model.integrate()` method with a consumed offer url. For example: .. code:: python - + # Integrate via offer url await my_model.integrate('mediawiki:db', 'admin/default.hosted-mysql') - + # Integrate via an offer alias created when consumed await my_model.consume('admin/prod.hosted_mysql', application_alias="mysql-alias") await my_model.integrate('mediawiki:db', 'mysql-alias') - + # Remove a consumed offer: await my_model.remove_saas('mysql-alias') @@ -70,7 +70,7 @@ Inspect integrations with an offer To see all connections to one or more offers, use the `list_offers()` method on a connected Model object. .. code:: python - + await my_model.list_offers() > See more: `list_offers() `_ @@ -83,8 +83,7 @@ Remove an offer To remove an offer, use the `remove_offer()` method on a connected Model. If the offer is used in an integration, then the `force=True` parameter is required to remove the offer, in which case the integration is also removed. .. code:: python - + await my_model.remove_offer('admin/mymodel.ubuntu', force=True) > See more: `remove_offer() `_ - diff --git a/docs/howto/manage-python-libjuju.rst b/docs/howto/manage-python-libjuju.rst index c65597dd..240a3063 100644 --- a/docs/howto/manage-python-libjuju.rst +++ b/docs/howto/manage-python-libjuju.rst @@ -12,7 +12,7 @@ Install `python-libjuju` In PyPI, which is the Python repository that `pip` is drawing modules from, `python-libjuju` is simply referred to as `juju`. You can install it directly via `pip`: .. code:: bash - + pip3 install juju @@ -22,19 +22,19 @@ Use `python-libjuju` 1. After installing `python-libjuju`, import it into your Python script as follows: .. code:: - + import juju You can also import specific modules to use, depending on your use case: .. code:: - + from juju import model -or +or .. code:: - + from juju import controller @@ -43,31 +43,30 @@ directory in the repository >> from juju.model import Model >>> model = Model() >>> await model.connect_current() >>> status = await model.get_status() Whichever your chosen method, use the `python-libjuju` how-to guides and the reference to build up your deployment. - diff --git a/docs/howto/manage-relations.rst b/docs/howto/manage-relations.rst index 9f83ca33..fdf7d7fe 100644 --- a/docs/howto/manage-relations.rst +++ b/docs/howto/manage-relations.rst @@ -17,7 +17,7 @@ Add a same-model relation To add a same-model relation between two applications, on a connected Model, use the `integrate()` method. .. code:: python - + await my_model.integrate('mysql', 'mediawiki') # Integrate with particular endpoints @@ -41,7 +41,7 @@ View all the current relations To view the current relations in a model, directly access the Model's `relations` property. .. code:: python - + my_model.relations > See more: `Model.relations (property) `_ @@ -53,7 +53,7 @@ Remove a relation To remove a relation, use the `remove_relation()` method on an Application object. .. code:: python - + await my_app.remove_relation('mediawiki', 'mysql:db') > See more: `remove_relation() `_ diff --git a/docs/howto/manage-secret-backends.rst b/docs/howto/manage-secret-backends.rst index 7f2eddea..bb4e28d8 100644 --- a/docs/howto/manage-secret-backends.rst +++ b/docs/howto/manage-secret-backends.rst @@ -15,7 +15,7 @@ Add a secret backend to a model To add a secret backend to a controller, on a connected Controller, use the `add_secret_backends()` method, passing the `id`, `name`, `backend_type`, and `config` as arguments. For example: .. code:: python - + await my_controller.add_secret_backends("1001", "myvault", "vault", {"endpoint": vault_url, "token": keys["root_token"]}) > See more: `add_secret_backend() `_, `Controller (module) `_ @@ -27,7 +27,7 @@ View all the secret backends available on a controller To view all the secret backends available in the controller, on a connected Controller, use the `list_secret_backends()` method. .. code:: python - + list = await my_controller.list_secret_backends() > See more: `list_secret_backends() `_, `Controller (module) `_ @@ -39,7 +39,7 @@ Update a secret backend To update a secret backend on the controller, on a connected Controller, use the `update_secret_backends()` method, passing the backend name as argument, along with the updated information, such as `name_change` for a new name. For example: .. code:: python - + await my_controller.update_secret_backends( "myvault", name_change="changed_name") @@ -54,7 +54,7 @@ Remove a secret backend To remove a secret backend on the controller, on a connected Controller, use the `remove_secret_backends()` method, passing the backend name as argument. For example: .. code:: python - + await my_controller.remove_secret_backends("myvault") Check out the documentation for the full list of arguments. diff --git a/docs/howto/manage-secrets.rst b/docs/howto/manage-secrets.rst index bc497cf4..2416aafc 100644 --- a/docs/howto/manage-secrets.rst +++ b/docs/howto/manage-secrets.rst @@ -5,7 +5,7 @@ How to manage secrets > See also: :ref:`juju:secret` -Charms can use relations to share secrets, such as API keys, a database’s address, credentials and so on. This document demonstrates how to interact with them as a Juju user. +Charms can use relations to share secrets, such as API keys, a database’s address, credentials and so on. This document demonstrates how to interact with them as a Juju user. Add a secret @@ -14,7 +14,7 @@ Add a secret To add a (user) secret, on a connected Model, use the `add_secret()` method, passing the name of the secret and the data as arguments. For example: .. code:: python - + await model.add_secret(name='my-apitoken', data_args=['token=34ae35facd4']) > See more: `add_secret() `_, `Model (module) `_ @@ -26,7 +26,7 @@ View all the available secrets To view all the (user and charm) secrets available in a model, on a connected Model, use the `list_secrets()` method. .. code:: python - + await model.list_secrets() > See more: `list_secrets() `_, `Model (module) `_ @@ -39,13 +39,13 @@ Grant access to a secret Given a model that contains both your (user) secret and the application(s) that you want to grant access to, to grant the application(s) access to the secret, on a connected Model, use the `grant_secret()` method, passing the name of the secret and the application name as arguments. For example: .. code:: python - + await model.grant_secret('my-apitoken', 'ubuntu') Similarly, you can use the `revoke_secret()` method to revoke access to a secret for an application. .. code:: python - + await model.revoke_secret('my-apitoken', 'ubuntu') > See more: `grant_secret() `_, `revoke_secret() `_, `Model (module) `_ @@ -58,7 +58,7 @@ Update a secret To update a (user) secret, on a connected Model, use the `update_secret()` method, passing the name of the secret and the updated info arguments. You may pass in `data_args`, `new_name`, `file` and `info` to update the secret (check out the documentation for details). For example: .. code:: python - + await model.update_secret(name='my-apitoken', new_name='new-token') > See more: `update_secret() `_, `Model (module) `_ @@ -70,11 +70,11 @@ Remove a secret To remove a secret from a model, on a connected Model, use the `remove_secret()` method, passing the name of the secret as an argument. For example: .. code:: python - - # Remove all the revisions of a secret + + # Remove all the revisions of a secret await model.remove_secret('my-apitoken') - - # Remove the revision 2 of a secret + + # Remove the revision 2 of a secret await model.remove_secret('my-apitoken', revision=2) > See more: `remove_secret() `_, `Model (module) `_ diff --git a/docs/howto/manage-spaces.rst b/docs/howto/manage-spaces.rst index d429e817..68273803 100644 --- a/docs/howto/manage-spaces.rst +++ b/docs/howto/manage-spaces.rst @@ -12,7 +12,7 @@ Add a space To create and add a new space, on a connected Model object, use the `add_space()` method, passing a name for the space and associated subnets. For example: .. code:: python - + await my_model.add_space("db-space", ["172.31.0.0/20"]) > See more: `add_space() `_, `Model (module) `_ @@ -25,12 +25,7 @@ View available spaces To view available spaces, on a connected Model, use the `get_spaces()` method. .. code:: python - + await my_model.get_spaces() > See more: `get_spaces() `_, `Model (module) `_ - - - - - diff --git a/docs/howto/manage-ssh-keys.rst b/docs/howto/manage-ssh-keys.rst index 0f0917b9..df42df5a 100644 --- a/docs/howto/manage-ssh-keys.rst +++ b/docs/howto/manage-ssh-keys.rst @@ -12,7 +12,7 @@ Add an SSH key To add a public `ssh` key to a model, on a connected Model object, use the `add_ssh_key()` method, passing a name for the key and the actual key payload. For example: .. code:: python - + SSH_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1I8QDP79MaHEIAlfh933zqcE8LyUt9doytF3YySBUDWippk8MAaKAJJtNb+Qsi+Kx/RsSY02VxMy9xRTp9d/Vr+U5BctKqhqf3ZkJdTIcy+z4hYpFS8A4bECJFHOnKIekIHD9glHkqzS5Vm6E4g/KMNkKylHKlDXOafhNZAiJ1ynxaZIuedrceFJNC47HnocQEtusPKpR09HGXXYhKMEubgF5tsTO4ks6pplMPvbdjxYcVOg4Wv0N/LJ4ffAucG9edMcKOTnKqZycqqZPE6KsTpSZMJi2Kl3mBrJE7JbR1YMlNwG6NlUIdIqVoTLZgLsTEkHqWi6OExykbVTqFuoWJJY3BmRAcP9H3FdLYbqcajfWshwvPM2AmYb8V3zBvzEKL1rpvG26fd3kGhk3Vu07qAUhHLMi3P0McEky4cLiEWgI7UyHFLI2yMRZgz23UUtxhRSkvCJagRlVG/s4yoylzBQJir8G3qmb36WjBXxpqAGHfLxw05EQI1JGV3ReYOs= user@somewhere" await my_model.add_ssh_key('admin', SSH_KEY) @@ -39,7 +39,7 @@ Remove an SSH key To remove an SSH key, on a connected Model object, use the `remove_ssh_key()` method, passing the user name and the key as parameters. For example: .. code:: python - + SSH_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1I8QDP79MaHEIAlfh933zqcE8LyUt9doytF3YySBUDWippk8MAaKAJJtNb+Qsi+Kx/RsSY02VxMy9xRTp9d/Vr+U5BctKqhqf3ZkJdTIcy+z4hYpFS8A4bECJFHOnKIekIHD9glHkqzS5Vm6E4g/KMNkKylHKlDXOafhNZAiJ1ynxaZIuedrceFJNC47HnocQEtusPKpR09HGXXYhKMEubgF5tsTO4ks6pplMPvbdjxYcVOg4Wv0N/LJ4ffAucG9edMcKOTnKqZycqqZPE6KsTpSZMJi2Kl3mBrJE7JbR1YMlNwG6NlUIdIqVoTLZgLsTEkHqWi6OExykbVTqFuoWJJY3BmRAcP9H3FdLYbqcajfWshwvPM2AmYb8V3zBvzEKL1rpvG26fd3kGhk3Vu07qAUhHLMi3P0McEky4cLiEWgI7UyHFLI2yMRZgz23UUtxhRSkvCJagRlVG/s4yoylzBQJir8G3qmb36WjBXxpqAGHfLxw05EQI1JGV3ReYOs= user@somewhere" await my_model.remove_ssh_key('admin', SSH_KEY) diff --git a/docs/howto/manage-storage-pools.rst b/docs/howto/manage-storage-pools.rst index b63a8a60..ce5db748 100644 --- a/docs/howto/manage-storage-pools.rst +++ b/docs/howto/manage-storage-pools.rst @@ -11,7 +11,7 @@ Create a storage pool To create a storage pool, on a connected Model object, use the `create_storage_pool()` method, passing the name of the pool and the provider type. For example: .. code:: python - + await my_model.create_storage_pool("test-pool", "lxd") > See more: `create_storage_pool() `_, `Model (module) `_ @@ -23,9 +23,9 @@ View the available storage pools To view the available storage pools, on a connected Model object, use the `list_storage_pools()` method. For example: .. code:: python - + await my_model.list_storage_pools() - + > See more: `list_storage_pools() `_, `Model (module) `_ @@ -35,9 +35,9 @@ Update a storage pool To update an existing storage pool attributes, on a connected Model object, use the `update_storage_pool()` method, passing the name of the storage and the attribute values to update. For example: .. code:: python - + await my_model.update_storage_pool( - "operator-storage", + "operator-storage", attributes={"volume-type":"provisioned-iops", "iops"="40"}) > See more: `update_storage_pool() `_, `Model (module) `_ @@ -49,8 +49,7 @@ Remove a storage pool To remove a storage pool, on a connected Model object, use the `remove_storage_pool()` method, passing the name of the storage. For example: .. code:: python - + await my_model.remove_storage_pool("test-pool") > See more: `remove_storage_pool() `_, `Model (module) `_ - diff --git a/docs/howto/manage-storage.rst b/docs/howto/manage-storage.rst index 05460c4c..77495582 100644 --- a/docs/howto/manage-storage.rst +++ b/docs/howto/manage-storage.rst @@ -6,7 +6,7 @@ How to manage storage > See also: :ref:`juju:storage` -This document shows how to manage storage. This will enable you to allocate resources at a granular level and can be useful in optimizing the deployment of an application. The level of sophistication is limited by your cloud (whether it supports dynamic storage or storage configuration attributes), and by the charm in charge of that application (whether it supports storage persistence, additional cache, etc.). +This document shows how to manage storage. This will enable you to allocate resources at a granular level and can be useful in optimizing the deployment of an application. The level of sophistication is limited by your cloud (whether it supports dynamic storage or storage configuration attributes), and by the charm in charge of that application (whether it supports storage persistence, additional cache, etc.). Add storage @@ -15,14 +15,14 @@ Add storage To create and attach a storage instance to a unit, on a Unit object, use the `add_storage()` method, passing the storage name as an argument. For example: .. code:: python - + await my_unit.add_storage("pgdata", size=512) To attach an existing storage to an application during deployment, on a connected Model object, use the `attach_storage` parameter of the `deploy()` method. .. code:: python - + await model.deploy('postgresql', attach_storage=[tag.storage("my-storage")]) @@ -34,7 +34,7 @@ List available storage To list available storage instances, on a connected Model object, use the `list_storage()` method. For example: .. code:: python - + await model.list_storage() > See more: `list_storage() `_, `Model (module) `_ @@ -47,7 +47,7 @@ Detach storage To detach a storage instance from a unit, on a Unit object, use the `detach_storage()` method, passing the storage id as an argument. For example: .. code:: python - + await my_unit.detach_storage("osd-devices/2") @@ -60,7 +60,7 @@ Attach storage To attach an existing storage instance to a unit, on a Unit object, use the `attach_storage()` method, passing the storage id as an argument. For example: .. code:: python - + await my_unit.attach_storage(["osd-devices/2"]) > See more: `attach_storage() `_, `Unit (module) `_ @@ -73,11 +73,9 @@ Remove storage To remove a storage instance, on a connected Model object, use the `remove_storage()` method, passing the storage id as an argument. For example: .. code:: python - + # use force=True to remove storage even if it is currently attached await my_model.remove_storage(["osd-devices/2"], force=True) > See more: `remove_storage() `_, `Model (module) `_ - - diff --git a/docs/howto/manage-units.rst b/docs/howto/manage-units.rst index 5e041458..c37bb492 100644 --- a/docs/howto/manage-units.rst +++ b/docs/howto/manage-units.rst @@ -11,7 +11,7 @@ Add a unit To add a unit in `python-libjuju` client, you simply call `add_unit()` on your `Application` object, as follows: .. code:: python - + my_app.add_unit(count=3) > See more: `Application (module) `_, `Application.add_unit() `_, `Application.scale() `_ @@ -25,18 +25,18 @@ To control the number of units of an application in `python-libjuju` client, you If you're on an IAAS system (machine applications): .. code:: python - + u = my_app.add_unit() my_app.destroy_units(u.name) # Note that the argument is the name of the unit - + # You may give multiple unit names to destroy at once my_app.destroy_units(u1.name, u2.name) - - If you're on a CAAS sytem (k8s applications): - + + If you're on a CAAS system (k8s applications): + .. code:: python - + my_app.scale(4) @@ -49,13 +49,13 @@ Show details about a unit Too see details about a unit in `python-libjuju` client, you can use various fields and methods of a `Unit` object. For example, to get the `public_address` of a unit: .. code:: python - + my_unit.get_public_address() Or, to see if the unit is a leader: .. code:: python - + my_unit.is_leader_from_status() > See more: `Unit (methods) `_, `Unit.get_public_address() `_, `Unit.is_leader_from_status() `_ @@ -67,7 +67,7 @@ Show the status of a unit To get the status of a unit on `pylibjuju-client`, you can use various (dynamically updated) status fields defined on a Unit object, such as: .. code:: python - + workload_st = my_unit.workload_status agent_st = my_unit.agent_status @@ -80,9 +80,9 @@ Mark unit errors as resolved To mark unit errors as resolved in the `python-libjuju` client, you can call the `resolved()` method on a `Unit` object: .. code:: python - + my_unit.resolved() - + > See more: `Unit.resolved()` @@ -93,11 +93,10 @@ To remove individual units on `python-libjuju` client, simply use the `Applicati .. code:: python - + my_app.destroy_units(u.name) # Note that the argument is the name of the unit # You may give multiple unit names to destroy at once my_app.destroy_units(u1.name, u2.name) > See more: `Application (module) `_, `Application.destroy_units() `_ - diff --git a/docs/howto/manage-users.rst b/docs/howto/manage-users.rst index 540bec54..1d4a1316 100644 --- a/docs/howto/manage-users.rst +++ b/docs/howto/manage-users.rst @@ -9,12 +9,12 @@ How to manage users Add a user ---------- - + To add a user to a controller, on a connected Controller object, use the `add_user()` method. .. code:: python - + await my_controller.add_user("alex") > See more: `add_user() `_ @@ -26,7 +26,7 @@ List all the known users To view a list of all the users known (i.e., allowed to log in) to a controller, on a connected Controller object, use the `get_users()` method. .. code:: python - + await my_controller.get_users() > See more: `get_users() `_ @@ -38,7 +38,7 @@ View details about a user To view details about a specific user, on a connected Controller, use the `get_user()` method to retrieve a User object that encapsulates everything about that user. Using that object, you can access all the details (via the object properties) for that user. .. code:: python - + user_object = await my_controller.get_user("alice") # Then we can access all the properties to view details print(user_object.display_name) @@ -55,7 +55,7 @@ View details about the current user To see details about the current user, on a connected Controller, use the `get_current_user()` method to retrieve a User object that encapsulates everything about the current user. Using that object, you can access all the details (via the object properties) for that user. .. code:: python - + user_object = await my_controller.get_current_user() # Then we can access all the properties to view details print(user_object.display_name) @@ -70,16 +70,16 @@ Manage a user's access level ---------------------------- > See also: :ref:`juju:user-access-levels` -To manage a user's access to a controller, a model, or an offer, on a User object, use the `grant()` and `revoke()` methods to grant or revoke a certain access level to a user. +To manage a user's access to a controller, a model, or an offer, on a User object, use the `grant()` and `revoke()` methods to grant or revoke a certain access level to a user. .. code:: python - + # grant a superuser access to the controller (that the user is on) await user_object.grant('superuser') - + # grant user the access to see a model await user_object.grant("read", model_name="test-model") - + # revoke ‘read’ (and ‘write’) access from user for application offer ‘fred/prod.hosted-mysql’: await user_object.revoke("read", offer_url="fred/prod.hosted-mysql") @@ -92,7 +92,7 @@ Manager a user's login details To set or change a user's password, on a User object, use the `set_password()` method. .. code:: python - + await user_object.set_password('123') @@ -104,15 +104,15 @@ Manage a user's enabled status To enable or disable a user, on a User object, use the `enable()` and `disable()` methods. .. code:: python - + await user_object.enable() - + await user_object.disable() You can also check if a user is enabled or disabled using the `enabled` and `disabled` properties on the Unit object. .. code:: python - + # re-enable a disabled user if user_object.disabled: await user_object.enable() @@ -127,9 +127,8 @@ Remove a user To remove a user, on a connected Controller object, use the `remove_user()` method. .. code:: python - + await my_controller.remove_user("bob") > See more: `remove_user() `_, `User (module) `_ - diff --git a/docs/index.rst b/docs/index.rst index 789685bb..9f04f0ab 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,11 +12,11 @@ Table of Contents .. toctree:: :caption: How-to guides - :glob: + :glob: :maxdepth: 3 - + howto/index - + .. toctree:: :caption: Overview From 90fabd94f78e918903ee03cb0b70511e5492feb3 Mon Sep 17 00:00:00 2001 From: James Garner Date: Fri, 20 Dec 2024 10:26:24 +1300 Subject: [PATCH 3/4] fix: many small corrections in docs Thanks @dimaqq Co-authored-by: Dima Tisnek --- docs/howto/manage-applications.rst | 4 ++-- docs/howto/manage-credentials.rst | 2 +- docs/howto/manage-secret-backends.rst | 4 ++-- docs/howto/manage-ssh-keys.rst | 2 +- docs/howto/manage-storage-pools.rst | 2 +- docs/howto/manage-storage.rst | 3 +-- docs/howto/manage-units.rst | 6 +++--- docs/howto/manage-users.rst | 2 +- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/howto/manage-applications.rst b/docs/howto/manage-applications.rst index 11aab74d..91e0b58c 100644 --- a/docs/howto/manage-applications.rst +++ b/docs/howto/manage-applications.rst @@ -176,14 +176,14 @@ Manage constraints for an application .. code:: python - await my_model.deploy(..., constraints={, 'arch': 'amd64', 'mem': 256}, ...) + await my_model.deploy(..., constraints={'arch': 'amd64', 'mem': 256}, ...) * To set constraints post deployment, you may use the `Application.set_contraints()` method, similar to passing constraints in the deploy call above: .. code:: python - await my_app.set_constraints(constraints={, 'arch': 'amd64', 'mem': 256}) + await my_app.set_constraints(constraints={'arch': 'amd64', 'mem': 256}) **Get values.** To see what constraints are set on an application, use the `Application.get_constraints()` method: diff --git a/docs/howto/manage-credentials.rst b/docs/howto/manage-credentials.rst index 1ebe2493..2e908bbf 100644 --- a/docs/howto/manage-credentials.rst +++ b/docs/howto/manage-credentials.rst @@ -16,6 +16,6 @@ To update a credential, on a connected `Controller` object, use the `Controller. from juju.client import client as jujuclient my_controller.add_credential("my-credential", - jujuclient.CloudCredential(auth_type="jsonfile", attrs={'file':'path_to_cred_file'}) + jujuclient.CloudCredential(auth_type="jsonfile", attrs={'file':'path_to_cred_file'})) > See more: `add_credential (method) `_ diff --git a/docs/howto/manage-secret-backends.rst b/docs/howto/manage-secret-backends.rst index bb4e28d8..5eeba1ce 100644 --- a/docs/howto/manage-secret-backends.rst +++ b/docs/howto/manage-secret-backends.rst @@ -18,7 +18,7 @@ To add a secret backend to a controller, on a connected Controller, use the `add await my_controller.add_secret_backends("1001", "myvault", "vault", {"endpoint": vault_url, "token": keys["root_token"]}) -> See more: `add_secret_backend() `_, `Controller (module) `_ +> See more: `add_secret_backends() `_, `Controller (module) `_ View all the secret backends available on a controller @@ -46,7 +46,7 @@ To update a secret backend on the controller, on a connected Controller, use the Check out the documentation for the full list of arguments. -> See more: `update_secret_backend() `_, `Controller (module) `_ +> See more: `update_secret_backends() `_, `Controller (module) `_ Remove a secret backend ----------------------- diff --git a/docs/howto/manage-ssh-keys.rst b/docs/howto/manage-ssh-keys.rst index df42df5a..d4cc2352 100644 --- a/docs/howto/manage-ssh-keys.rst +++ b/docs/howto/manage-ssh-keys.rst @@ -17,7 +17,7 @@ To add a public `ssh` key to a model, on a connected Model object, use the `add_ await my_model.add_ssh_key('admin', SSH_KEY) -> See more: `add_ssh_key() `https://pythonlibjuju.readthedocs.io/en/latest/api/juju.model.html#juju.model.Model.add_ssh_key>`_, `Model (module) `_ +> See more: `add_ssh_key() https://pythonlibjuju.readthedocs.io/en/latest/api/juju.model.html#juju.model.Model.add_ssh_key>`_, `Model (module) `_ View the available SSH keys diff --git a/docs/howto/manage-storage-pools.rst b/docs/howto/manage-storage-pools.rst index ce5db748..826e3c61 100644 --- a/docs/howto/manage-storage-pools.rst +++ b/docs/howto/manage-storage-pools.rst @@ -38,7 +38,7 @@ To update an existing storage pool attributes, on a connected Model object, use await my_model.update_storage_pool( "operator-storage", - attributes={"volume-type":"provisioned-iops", "iops"="40"}) + attributes={"volume-type": "provisioned-iops", "iops": "40"}) > See more: `update_storage_pool() `_, `Model (module) `_ diff --git a/docs/howto/manage-storage.rst b/docs/howto/manage-storage.rst index 77495582..f5385b76 100644 --- a/docs/howto/manage-storage.rst +++ b/docs/howto/manage-storage.rst @@ -50,8 +50,7 @@ To detach a storage instance from a unit, on a Unit object, use the `detach_stor await my_unit.detach_storage("osd-devices/2") - -> See more: `detach_storage() `_, `Unit (module) `_ +> See more: `detach_storage() `_, `Unit (module) `_ Attach storage diff --git a/docs/howto/manage-units.rst b/docs/howto/manage-units.rst index c37bb492..c1db85df 100644 --- a/docs/howto/manage-units.rst +++ b/docs/howto/manage-units.rst @@ -46,7 +46,7 @@ If you're on an IAAS system (machine applications): Show details about a unit ------------------------- -Too see details about a unit in `python-libjuju` client, you can use various fields and methods of a `Unit` object. For example, to get the `public_address` of a unit: +To see details about a unit in `python-libjuju` client, you can use various fields and methods of a `Unit` object. For example, to get the `public_address` of a unit: .. code:: python @@ -81,9 +81,9 @@ To mark unit errors as resolved in the `python-libjuju` client, you can call the .. code:: python - my_unit.resolved() + await my_unit.resolved() -> See more: `Unit.resolved()` +> See more: `Unit.resolved() https://pythonlibjuju.readthedocs.io/en/latest/api/juju.unit.html#juju.unit.Unit.resolved`_ Remove a unit diff --git a/docs/howto/manage-users.rst b/docs/howto/manage-users.rst index 1d4a1316..d7dfc360 100644 --- a/docs/howto/manage-users.rst +++ b/docs/howto/manage-users.rst @@ -109,7 +109,7 @@ To enable or disable a user, on a User object, use the `enable()` and `disable() await user_object.disable() -You can also check if a user is enabled or disabled using the `enabled` and `disabled` properties on the Unit object. +You can also check if a user is enabled or disabled using the `enabled` and `disabled` properties on the User object. .. code:: python From ccfff3afcf105f30de91f47ac0bcd702ad27f850 Mon Sep 17 00:00:00 2001 From: James Garner Date: Fri, 20 Dec 2024 13:44:31 +1300 Subject: [PATCH 4/4] fix: correct heading syntax in docs/howto/index.rst --- docs/howto/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/howto/index.rst b/docs/howto/index.rst index 97348e4c..83992d88 100644 --- a/docs/howto/index.rst +++ b/docs/howto/index.rst @@ -1,4 +1,5 @@ -**How-to guides** +How-to guides +############# .. toctree:: :glob: