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)
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())}
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()
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
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")
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)
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)
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
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()