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/docs/conf.py b/docs/conf.py index a21bbfed..55c9e89e 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/index.rst b/docs/howto/index.rst new file mode 100644 index 00000000..83992d88 --- /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..c7429609 --- /dev/null +++ b/docs/howto/manage-actions.rst @@ -0,0 +1,40 @@ +.. _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..91e0b58c --- /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..ccd2cf91 --- /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..070534be --- /dev/null +++ b/docs/howto/manage-charms.rst @@ -0,0 +1,107 @@ +.. _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..b9a2d63e --- /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..69c04ca6 --- /dev/null +++ b/docs/howto/manage-controllers.rst @@ -0,0 +1,50 @@ +.. _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..2e908bbf --- /dev/null +++ b/docs/howto/manage-credentials.rst @@ -0,0 +1,21 @@ +.. _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..21c7aa23 --- /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..d79eb937 --- /dev/null +++ b/docs/howto/manage-models.rst @@ -0,0 +1,88 @@ +.. _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..fef2e23a --- /dev/null +++ b/docs/howto/manage-offers.rst @@ -0,0 +1,89 @@ +.. _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..240a3063 --- /dev/null +++ b/docs/howto/manage-python-libjuju.rst @@ -0,0 +1,72 @@ +.. _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..fdf7d7fe --- /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..5eeba1ce --- /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_backends() `_, `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_backends() `_, `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..2416aafc --- /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..68273803 --- /dev/null +++ b/docs/howto/manage-spaces.rst @@ -0,0 +1,31 @@ +.. _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..d4cc2352 --- /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..826e3c61 --- /dev/null +++ b/docs/howto/manage-storage-pools.rst @@ -0,0 +1,55 @@ +.. _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..f5385b76 --- /dev/null +++ b/docs/howto/manage-storage.rst @@ -0,0 +1,80 @@ +.. _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..c1db85df --- /dev/null +++ b/docs/howto/manage-units.rst @@ -0,0 +1,102 @@ +.. _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 system (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 +------------------------- + +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 + + 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 + + await my_unit.resolved() + +> See more: `Unit.resolved() https://pythonlibjuju.readthedocs.io/en/latest/api/juju.unit.html#juju.unit.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..d7dfc360 --- /dev/null +++ b/docs/howto/manage-users.rst @@ -0,0 +1,134 @@ +.. _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 User 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..9f04f0ab 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: