예제 #1
0
async def add_default_models_to_database(c_name, cred_name, username,
                                         juju_username, controller, user_info):
    """Adds the default models, that have been created by new controller, to the
    database."""
    c_info = datastore.get_controller(c_name)
    model_facade = client.ModelManagerFacade.from_connection(
        controller.connection)
    controller_facade = client.ControllerFacade.from_connection(
        controller.connection)
    user = tag.user(juju_username)
    models = await controller_facade.AllModels()
    for model in models.user_models:
        if model:
            m_key = juju.construct_model_key(c_info['name'], model.model.name)
            logger.info(model.model.name)
            if username != settings.JUJU_ADMIN_USER:
                model_tag = tag.model(model.model.uuid)
                changes = client.ModifyModelAccess('admin', 'grant', model_tag,
                                                   user)
                await model_facade.ModifyModelAccess([changes])
            datastore.create_model(m_key,
                                   model.model.name,
                                   state='Model is being deployed',
                                   uuid='')
            datastore.add_model_to_controller(c_name, m_key)
            datastore.set_model_state(m_key,
                                      'ready',
                                      credential=cred_name,
                                      uuid=model.model.uuid)
            datastore.set_model_access(m_key, username, 'admin')
            ssh_keys = user_info["ssh_keys"]
            if len(ssh_keys) > 0:
                juju.update_ssh_keys_model(username, ssh_keys, c_name, m_key)
예제 #2
0
    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())}
예제 #3
0
async def create_model(c_name, m_name, usr, pwd, cred_name):
    try:
        token = JuJu_Token()
        token.username = usr
        token.password = pwd
        controller = juju.Controller_Connection(token, c_name)
        c_type = controller.c_type
        credential = 't{}'.format(hashlib.md5(cred_name.encode('utf')).hexdigest())
        async with controller.connect(token) as con_juju:
            logger.info('%s -> Creating model: %s', m_name, m_name)
            model = await con_juju.add_model(
                m_name,
                cloud_name=c_type,
                credential_name=credential,
                owner=tag.user(usr)
            )
            logger.info('%s -> model deployed on juju', m_name)
            ds.set_model_access(c_name, m_name, usr, 'admin')
            ds.set_model_state(c_name, m_name, 'ready', cred_name, model.info.uuid)
            logger.info('%s -> Adding ssh-keys to model: %s', m_name, m_name)
            for key in ds.get_ssh_keys(usr):
                try:
                    await model.add_ssh_key(usr, key)
                except (JujuAPIError, JujuError):
                    pass
            logger.info('%s -> retrieving users: %s', m_name, ds.get_controller_users(c_name))
            for u in ds.get_controller_users(c_name):
                if u['access'] == 'superuser' and u['name'] != usr:
                    await model.grant(u['name'], acl='admin')
                    ds.set_model_access(c_name, m_name, u['name'], 'admin')
                    for key in ds.get_ssh_keys(u['name']):
                        try:
                            await model.add_ssh_key(u['name'], key['key'])
                        except (JujuAPIError, JujuError):
                            pass
            logger.info('%s -> succesfully deployed model', m_name)
    except Exception as e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
        for l in lines:
            logger.error(l)
        if 'model' in locals():
            ds.set_model_state(c_name, m_name, 'ready', cred_name, model.info.uuid)
        else:
            ds.set_model_state(c_name, m_name, 'error: {}'.format(lines))
    finally:
        if 'model' in locals():
            await model.disconnect()
        await controller.disconnect()
예제 #4
0
    async def connect(
        cls,
        endpoint=None,
        uuid=None,
        username=None,
        password=None,
        cacert=None,
        bakery_client=None,
        loop=None,
        max_frame_size=None,
    ):
        """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.
        :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 asyncio.BaseEventLoop loop: The event loop to use for async
            operations.
        :param int max_frame_size: The maximum websocket frame size to allow.
        """
        self = cls()
        if endpoint is None:
            raise ValueError('no endpoint provided')
        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.loop = loop or asyncio.get_event_loop()

        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.cacert = None
        self.info = None

        # Create that _Task objects but don't start the tasks yet.
        self._pinger_task = _Task(self._pinger, self.loop)
        self._receiver_task = _Task(self._receiver, self.loop)

        self.facades = {}
        self.messages = IdQueue(loop=self.loop)
        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
        await self._connect_with_redirect([(endpoint, cacert)])
        return self
예제 #5
0
    async def connect(
        cls,
        endpoint=None,
        uuid=None,
        username=None,
        password=None,
        cacert=None,
        bakery_client=None,
        loop=None,
        max_frame_size=None,
        retries=3,
        retry_backoff=10,
        specified_facades=None,
    ):
        """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 asyncio.BaseEventLoop loop: The event loop to use for async
            operations.
        :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: Define a series of facade versions you wish to override
            to prevent using the conservative client pinning with in the client.
        """
        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.loop = loop or asyncio.get_event_loop()

        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

        # Create that _Task objects but don't start the tasks yet.
        self._pinger_task = _Task(self._pinger, self.loop)
        self._receiver_task = _Task(self._receiver, self.loop)

        self._retries = retries
        self._retry_backoff = retry_backoff

        self.facades = {}
        self.specified_facades = specified_facades or {}

        self.messages = IdQueue(loop=self.loop)
        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

        _endpoints = [(endpoint, cacert)] if isinstance(
            endpoint, str) else [(e, cacert) for e in endpoint]
        for _ep in _endpoints:
            try:
                await self._connect_with_redirect([_ep])
                return self
            except OSError as e:
                logging.debug("Cannot access endpoint {}: {}".format(
                    _ep, e.strerror))
                continue
        raise Exception("Unable to connect to websocket")
예제 #6
0
async def bootstrap_azure_controller(c_name, region, cred_name, username,
                                     password):
    try:
        tengu_username = settings.JUJU_ADMIN_USER
        tengu_password = settings.JUJU_ADMIN_PASSWORD
        juju_cred_name = 't{}'.format(
            hashlib.md5(cred_name.encode('utf')).hexdigest())
        credential = juju.get_credential(username, cred_name)

        # Check if the credential is valid.
        juju.get_controller_types()['azure'].check_valid_credentials(
            credential)

        temp_cred = create_temporary_cred_file(juju_cred_name, credential)

        logger.info('Adding credential to JuJu...')
        check_call(
            ['juju', 'add-credential', 'azure', '-f', temp_cred, '--replace'])

        logger.info('Bootstrapping controller in Azure cloud...')
        check_call([
            'juju', 'bootstrap', '--agent-version=2.3.0', 'azure', c_name,
            '--credential', juju_cred_name
        ])

        # Remove temporary credentials.
        os.remove(temp_cred)

        logger.info('Setting admin password...')
        check_output(['juju', 'change-user-password', 'admin', '-c', c_name],
                     input=bytes(
                         '{}\n{}\n'.format(tengu_password, tengu_password),
                         'utf-8'))

        logger.info('Updating controller in database...')
        con_data = update_controller_database(c_name)

        logger.info('Connecting to controller...')
        controller = Controller()
        await controller.connect(
            con_data['controllers'][c_name]['api-endpoints'][0],
            tengu_username, tengu_password,
            con_data['controllers'][c_name]['ca-cert'])

        user_info = datastore.get_user(username)
        juju_username = user_info["juju_username"]
        user = tag.user(juju_username)

        logger.info('Adding existing credentials to new controller...')
        await update_credentials_new_controller(controller, username,
                                                juju_username, cred_name)

        model_facade = client.ModelManagerFacade.from_connection(
            controller.connection)
        controller_facade = client.ControllerFacade.from_connection(
            controller.connection)
        if username != tengu_username:
            user_facade = client.UserManagerFacade.from_connection(
                controller.connection)
            users = [
                client.AddUser(display_name=juju_username,
                               username=juju_username,
                               password=password)
            ]
            await user_facade.AddUser(users)
            changes = client.ModifyControllerAccess('superuser', 'grant', user)
            await controller_facade.ModifyControllerAccess([changes])

        logger.info('Adding default models to database...')
        await add_default_models_to_database(c_name, cred_name, username,
                                             juju_username, controller,
                                             user_info)

        logger.info('Controller succesfully created!')
    except Exception:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
        for l in lines:
            logger.error(l)
        datastore.set_controller_state(c_name, 'error')
    finally:
        if 'controller' in locals():
            await juju.disconnect(controller)
예제 #7
0
async def bootstrap_aws_controller(c_name, region, cred_name, username,
                                   password):  #pylint: disable=E0001
    try:
        # Check if the credential is valid.
        tengu_username = settings.JUJU_ADMIN_USER
        tengu_password = settings.JUJU_ADMIN_PASSWORD
        valid_cred_name = 't{}'.format(
            hashlib.md5(cred_name.encode('utf')).hexdigest())
        credential = juju.get_credential(username, cred_name)
        logger.info(credential)

        juju.get_controller_types()['aws'].check_valid_credentials(credential)

        # Create credential file that can be used to bootstrap controller.
        cred_path = '/home/{}/credentials'.format(settings.SOJOBO_USER)
        if not os.path.exists(cred_path):
            os.mkdir(cred_path)
        filepath = '{}/aws-{}.json'.format(cred_path, valid_cred_name)
        with open(filepath, 'w+') as credfile:
            json.dump(credential['credential'], credfile)
        path = '/tmp/credentials.yaml'
        data = {
            'credentials': {
                'aws': {
                    valid_cred_name: {
                        'auth-type': 'access-key',
                        'access-key': credential['credential']['access-key'],
                        'secret-key': credential['credential']['secret-key']
                    }
                }
            }
        }
        with open(path, 'w') as dest:
            yaml.dump(data, dest, default_flow_style=True)
        logger.info(valid_cred_name)
        logger.info(data)
        check_call(['juju', 'add-credential', 'aws', '-f', path, '--replace'])
        logger.info(path)
        check_call([
            'juju', 'bootstrap', '--agent-version=2.3.0',
            'aws/{}'.format(region), c_name, '--credential', valid_cred_name
        ])
        os.remove(path)

        logger.info('Setting admin password')
        check_output(['juju', 'change-user-password', 'admin', '-c', c_name],
                     input=bytes(
                         '{}\n{}\n'.format(tengu_password, tengu_password),
                         'utf-8'))

        con_data = {}
        logger.info('Updating controller in database')
        with open(
                os.path.join(str(Path.home()), '.local', 'share', 'juju',
                             'controllers.yaml'), 'r') as data:
            con_data = yaml.load(data)
        datastore.set_controller_state(
            c_name,
            'ready',
            endpoints=con_data['controllers'][c_name]['api-endpoints'],
            uuid=con_data['controllers'][c_name]['uuid'],
            ca_cert=con_data['controllers'][c_name]['ca-cert'])

        logger.info('Connecting to controller')
        controller = Controller()

        logger.info(
            'Adding existing credentials and default models to database...')
        credentials = datastore.get_cloud_credentials('aws', username)
        logger.info(credentials)
        await controller.connect(
            endpoint=con_data['controllers'][c_name]['api-endpoints'][0],
            username=tengu_username,
            password=tengu_password,
            cacert=con_data['controllers'][c_name]['ca-cert'])
        user_info = datastore.get_user(username)
        juju_username = user_info["juju_username"]
        for cred in credentials:
            if username != tengu_username:
                await juju.update_cloud(controller, 'aws', cred['name'],
                                        juju_username, username)
                logger.info('Added credential %s to controller %s',
                            cred['name'], c_name)
            elif cred['name'] != cred_name:
                await juju.update_cloud(controller, 'aws', cred['name'],
                                        juju_username, username)
        user = tag.user(juju_username)
        model_facade = client.ModelManagerFacade.from_connection(
            controller.connection)
        controller_facade = client.ControllerFacade.from_connection(
            controller.connection)
        if username != tengu_username:
            user_facade = client.UserManagerFacade.from_connection(
                controller.connection)
            users = [
                client.AddUser(display_name=juju_username,
                               username=juju_username,
                               password=password)
            ]
            await user_facade.AddUser(users)
            changes = client.ModifyControllerAccess('superuser', 'grant', user)
            await controller_facade.ModifyControllerAccess([changes])

        c_info = datastore.get_controller(c_name)
        models = await controller_facade.AllModels()
        for model in models.user_models:
            if model:
                m_key = juju.construct_model_key(c_info['name'],
                                                 model.model.name)
                logger.info(model.model.name)
                if username != tengu_username:
                    model_tag = tag.model(model.model.uuid)
                    changes = client.ModifyModelAccess('admin', 'grant',
                                                       model_tag, user)
                    await model_facade.ModifyModelAccess([changes])
                datastore.create_model(m_key,
                                       model.model.name,
                                       state='Model is being deployed',
                                       uuid='')
                datastore.add_model_to_controller(c_name, m_key)
                datastore.set_model_state(m_key,
                                          'ready',
                                          credential=cred_name,
                                          uuid=model.model.uuid)
                datastore.set_model_access(m_key, username, 'admin')
                ssh_keys = user_info["ssh_keys"]
                if len(ssh_keys) > 0:
                    juju.update_ssh_keys_model(username, ssh_keys, c_name,
                                               m_key)
        logger.info('Controller succesfully created!')
    except Exception:  #pylint: disable=W0703
        exc_type, exc_value, exc_traceback = sys.exc_info()
        lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
        for l in lines:
            logger.error(l)
        datastore.set_controller_state(c_name, 'error')
    finally:
        if 'controller' in locals():
            await juju.disconnect(controller)
예제 #8
0
    async def connect(
            cls,
            endpoint=None,
            uuid=None,
            username=None,
            password=None,
            cacert=None,
            bakery_client=None,
            loop=None,
            max_frame_size=None,
            retries=3,
            retry_backoff=10,
    ):
        """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.
        :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 asyncio.BaseEventLoop loop: The event loop to use for async
            operations.
        :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).
        """
        self = cls()
        if endpoint is None:
            raise ValueError('no endpoint provided')
        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.loop = loop or asyncio.get_event_loop()

        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.cacert = None
        self.info = None

        # Create that _Task objects but don't start the tasks yet.
        self._pinger_task = _Task(self._pinger, self.loop)
        self._receiver_task = _Task(self._receiver, self.loop)

        self._retries = retries
        self._retry_backoff = retry_backoff

        self.facades = {}
        self.messages = IdQueue(loop=self.loop)
        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
        await self._connect_with_redirect([(endpoint, cacert)])
        return self
예제 #9
0
async def create_model(c_name, m_name, usr, pwd, url, port, cred_name):
    try:
        logger.info('%s -> Setting up Controllerconnection for %s', m_name,
                    c_name)
        users = redis.StrictRedis(host=url,
                                  port=port,
                                  charset="utf-8",
                                  decode_responses=True,
                                  db=11)
        controllers = redis.StrictRedis(host=url,
                                        port=port,
                                        charset="utf-8",
                                        decode_responses=True,
                                        db=10)
        controller = Controller()
        await controller.connect(
            json.loads(controllers.get(c_name))['endpoints'][0], usr, pwd)
        c_type = json.loads(controllers.get(c_name))['type']
        logger.info('%s -> Adding credentials', m_name)
        cloud_facade = client.CloudFacade.from_connection(
            controller.connection)
        for cred in json.loads(users.get(usr))['credentials']:
            if cred['name'] == cred_name:
                credential = cred
                cloud_cred = client.UpdateCloudCredential(
                    client.CloudCredential(cred['key'], cred['type']),
                    tag.credential(c_type, usr, cred['name']))
                await cloud_facade.UpdateCredentials([cloud_cred])
        logger.info('%s -> Creating model: %s', m_name, m_name)

        model = await controller.add_model(m_name,
                                           cloud_name=c_type,
                                           credential_name=credential['name'],
                                           owner=tag.user(usr))

        logger.info('%s -> model deployed on juju', m_name)
        set_model_access(c_name, m_name, usr, users, 'admin')
        set_model_state(c_name, m_name, 'ready', controllers, model.info.uuid)
        logger.info('%s -> Adding ssh-keys to model: %s', m_name, m_name)
        for key in json.loads(users.get(usr))['ssh-keys']:
            try:
                await model.add_ssh_key(usr, key)
            except (JujuAPIError, JujuError):
                pass
        for u in json.loads(controllers.get(c_name))['users']:
            if u['access'] == 'superuser':
                await model.grant(u['name'], acl='admin')
                set_model_access(c_name, m_name, u['name'], users, 'admin')
                for key in json.loads(users.get(u['name']))['ssh-keys']:
                    try:
                        await model.add_ssh_key(u['name'], key)
                    except (JujuAPIError, JujuError):
                        pass
        logger.info('%s -> succesfully deployed model', m_name)
    except Exception as e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
        for l in lines:
            logger.error(l)
        if 'model' in locals():
            set_model_state(c_name, m_name, 'ready', controllers,
                            model.info.uuid)
        else:
            set_model_state(c_name, m_name, 'error', controllers)
    finally:
        if 'model' in locals():
            await model.disconnect()
        await controller.disconnect()