X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=juju%2Fclient%2Fconnection.py;h=9e8cb8fa01f362683a1b0b09eb65a212ccba33db;hb=b27c2c18d73186c95cb2995b665cfdccb308162f;hp=18111ce11afed98b640d95e09596ed1aca43d3ef;hpb=77cdb181bdeefd6f33b769dbed0d0c9ca2277006;p=osm%2FN2VC.git diff --git a/juju/client/connection.py b/juju/client/connection.py index 18111ce..9e8cb8f 100644 --- a/juju/client/connection.py +++ b/juju/client/connection.py @@ -9,10 +9,12 @@ import ssl import string import subprocess import websockets +from http.client import HTTPSConnection import yaml -from juju.errors import JujuAPIError +from juju import tag +from juju.errors import JujuAPIError, JujuConnectionError log = logging.getLogger("websocket") @@ -93,6 +95,52 @@ class Connection: raise JujuAPIError(result) return result + def http_headers(self): + """Return dictionary of http headers necessary for making an http + connection to the endpoint of this Connection. + + :return: Dictionary of headers + + """ + if not self.username: + return {} + + creds = u'{}:{}'.format( + tag.user(self.username), + self.password or '' + ) + token = base64.b64encode(creds.encode()) + return { + 'Authorization': 'Basic {}'.format(token.decode()) + } + + def https_connection(self): + """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 + host, remainder = endpoint.split(':', 1) + port = remainder + if '/' in remainder: + port, _ = remainder.split('/', 1) + + conn = HTTPSConnection( + host, int(port), + context=self._get_ssl(self.cacert), + ) + + path = ( + "/model/{}".format(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. @@ -135,8 +183,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 +200,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 +221,35 @@ 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() + if not controller_name: + raise JujuConnectionError('No 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 +296,10 @@ class Connection: "nonce": "".join(random.sample(string.printable, 12)), "macaroons": macaroons or [] }}) - return result['response'] + response = result['response'] + self.build_facades(response.get('facades', {})) + self.info = response.copy() + return response async def redirect_info(self): try: @@ -243,10 +321,10 @@ class JujuData: self.path = os.path.abspath(os.path.expanduser(self.path)) def current_controller(self): - cmd = shlex.split('juju show-controller --format yaml') + cmd = shlex.split('juju list-controllers --format yaml') output = subprocess.check_output(cmd) output = yaml.safe_load(output) - return list(output.keys())[0] + return output.get('current-controller', '') def controllers(self): return self._load_yaml('controllers.yaml', 'controllers')