From 6f9041acecc03eaf12a6c8e5d50d20d17a28e87f Mon Sep 17 00:00:00 2001 From: Tim Van Steenburgh Date: Thu, 17 Nov 2016 16:26:45 -0500 Subject: [PATCH 1/1] Add Controller.add_model() with example --- examples/controller.py | 46 +++++++++++++++++++++++++++++++ juju/client/connection.py | 36 ++++++++++++++++++++++--- juju/controller.py | 57 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 examples/controller.py diff --git a/examples/controller.py b/examples/controller.py new file mode 100644 index 0000000..de0460c --- /dev/null +++ b/examples/controller.py @@ -0,0 +1,46 @@ +""" +This example: + +1. Connects to current controller. +2. Creates a new model. +3. Deploys an application on the new model. + +""" +import asyncio +import logging + +from juju.model import Model, ModelObserver +from juju.controller import Controller + + +class MyModelObserver(ModelObserver): + async def on_change(self, delta, old, new, model): + pass + + +async def run(): + controller = Controller() + await controller.connect_current() + model = await controller.add_model( + 'libjuju-test', + 'cloud-aws', + 'cloudcred-aws_tvansteenburgh@external_aws-tim', + ) + await model.deploy( + 'ubuntu-0', + service_name='ubuntu', + series='trusty', + channel='stable', + ) + await model.disconnect() + await controller.disconnect() + model.loop.stop() + + +logging.basicConfig(level=logging.DEBUG) +ws_logger = logging.getLogger('websockets.protocol') +ws_logger.setLevel(logging.INFO) +loop = asyncio.get_event_loop() +loop.set_debug(False) +loop.create_task(run()) +loop.run_forever() diff --git a/juju/client/connection.py b/juju/client/connection.py index 18111ce..f0737ca 100644 --- a/juju/client/connection.py +++ b/juju/client/connection.py @@ -135,8 +135,7 @@ class Connection: redirect_info = await client.redirect_info() if not redirect_info: - server_info = await client.login(username, password, macaroons) - client.build_facades(server_info['facades']) + await client.login(username, password, macaroons) return client await client.close() @@ -153,7 +152,6 @@ class Connection: result = await client.login(username, password, macaroons) if 'discharge-required-error' in result: continue - client.build_facades(result['facades']) return client except Exception as e: await client.close() @@ -175,6 +173,33 @@ class Connection: return await cls.connect_model( '{}:{}'.format(controller_name, model_name)) + @classmethod + async def connect_current_controller(cls): + """Connect to the currently active controller. + + """ + jujudata = JujuData() + controller_name = jujudata.current_controller() + + return await cls.connect_controller(controller_name) + + @classmethod + async def connect_controller(cls, controller_name): + """Connect to a controller by name. + + """ + jujudata = JujuData() + controller = jujudata.controllers()[controller_name] + endpoint = controller['api-endpoints'][0] + cacert = controller.get('ca-cert') + accounts = jujudata.accounts()[controller_name] + username = accounts['user'] + password = accounts.get('password') + macaroons = get_macaroons() if not password else None + + return await cls.connect( + endpoint, None, username, password, cacert, macaroons) + @classmethod async def connect_model(cls, model): """Connect to a model by name. @@ -221,7 +246,10 @@ class Connection: "nonce": "".join(random.sample(string.printable, 12)), "macaroons": macaroons or [] }}) - return result['response'] + response = result['response'] + self.build_facades(response['facades']) + self.info = response.copy() + return response async def redirect_info(self): try: diff --git a/juju/controller.py b/juju/controller.py index e0f8066..ca871a1 100644 --- a/juju/controller.py +++ b/juju/controller.py @@ -1,5 +1,34 @@ +import asyncio +import logging + +from .client import client +from .client import connection +from .client import watcher +from .model import Model + +log = logging.getLogger(__name__) + + class Controller(object): - def add_model(self, name, config=None, credential=None, owner=None): + async def connect_current(self): + """Connect to the current Juju controller. + + """ + self.connection = ( + await connection.Connection.connect_current_controller()) + + async def disconnect(self): + """Shut down the watcher task and close websockets. + + """ + if self.connection and self.connection.is_open: + log.debug('Closing controller connection') + await self.connection.close() + self.connection = None + + async def add_model( + self, name, cloud, credential, owner=None, + config=None, region=None): """Add a model to this controller. :param str name: Name of the model @@ -8,7 +37,31 @@ class Controller(object): :param str owner: Owner username """ - pass + model_facade = client.ModelManagerFacade() + model_facade.connect(self.connection) + + log.debug('Creating model %s', name) + + model_info = await model_facade.CreateModel( + cloud, + config, + credential, + name, + owner or self.connection.info['user-info']['identity'], + region, + ) + + model = Model() + await model.connect( + self.connection.endpoint, + model_info.uuid, + self.connection.username, + self.connection.password, + self.connection.cacert, + self.connection.macaroons, + ) + + return model def add_user(self, username, display_name=None, acl=None, models=None): """Add a user to this controller. -- 2.25.1