Exemplo n.º 1
0
from xpresso.ai.core.logging.xpr_log import XprLogger
from xpresso.ai.admin.controller.xprobject import XprObject
from xpresso.ai.admin.controller.exceptions.xpr_exceptions import *

logger = XprLogger()


class AuthenticationContext(XprObject):
    def __init__(self, credentials):
        super().__init__(credentials)
        if 'uid' not in credentials or not len(credentials['uid']):
            raise InvalidUserIDException
        elif 'pwd' not in credentials or not len(credentials['pwd']):
            raise InvalidPasswordException
class xpruser():
    config_path = XprConfigParser.DEFAULT_CONFIG_PATH

    CONTROLLER_SECTION = 'controller'
    TOKEN_EXPIRY = 'soft_expiry'
    LOGIN_EXPIRY = 'hard_expiry'
    MONGO_SECTION = 'mongodb'
    URL = 'mongo_url'
    DB = 'database'
    UID = 'mongo_uid'
    PWD = 'mongo_pwd'
    W = 'w'

    def __init__(self):
        self.config = XprConfigParser(self.config_path)
        self.db_utils = Utils(url=self.config[self.MONGO_SECTION][self.URL],
                              db=self.config[self.MONGO_SECTION][self.DB],
                              uid=self.config[self.MONGO_SECTION][self.UID],
                              pwd=self.config[self.MONGO_SECTION][self.PWD],
                              w=self.config[self.MONGO_SECTION][self.W])
        self.logger = XprLogger()

    def registeruser(self, user_json):
        """
        register a new user in the db

        checks if the user already exists and then adds to db

        Parameters:
            userjson [json]: json with node information

        Return:
            Success -> 'OK' [str] : returns 'OK' as response
            Failure -> [str] : returns appropriate failure response
        """
        self.logger.debug(f"user info provided is {user_json}")
        info_check = userinfocheck(user_json)
        # user info_check checks if the user_json has sufficient info
        if info_check == -1:
            errcode = error_codes.incomplete_user_information
            self.logger.error("Insufficient information to create a new user")
            return xprresponse('failure', errcode, {})
        elif info_check == 0:
            errcode = error_codes.incorrect_primaryRole
            self.logger.error("Incorrect primaryRole has been provided")
            return xprresponse('failure', errcode, {})

        # Password should not be saved as plain text in db.
        # Encrypting the password before saving it to db
        password = sha512_crypt.encrypt(user_json['pwd'])
        user_json['pwd'] = password
        # checks if the user is already present in the db
        self.logger.info("Registering a new user")
        uid_json = {'uid': user_json['uid']}
        self.logger.info("Checking the db if user is already present")
        user = self.db_utils.find("users", uid_json)
        if len(user) != 0:
            errcode = error_codes.user_exists
            return xprresponse('failure', errcode, {})

        user_json['loginStatus'] = False
        user_json['activationStatus'] = True
        user_json['nodes'] = []
        self.logger.info("adding user to the db")
        add_user = self.db_utils.insert("users", user_json, False)
        if add_user == -1:
            errcode = error_codes.username_already_exists
            self.logger.error("username already exists in the db")
            return xprresponse('failure', errcode, {})

        self.logger.info("user successfully added to the db")

        # NFS User directory changes
        nfs_manager = NFSUserManager(config=self.config)
        nfs_manager.setup_user_folder(user=user_json['uid'])
        return xprresponse('success', '', {})

    def modifyuser(self, token, changesjson):
        """
            modify_user updates the user info in the db

            checks if user is available and then updates
            the info as per changesjson

            Parameters:
                uid [str]: uid of the user
                changesjson [json] : json with user changes info

            Return:
                Success -> 'OK' [str] : returns OK if provision_node succeeds
                Failure -> [str] : returns appropriate failure response
        """
        check = modify_user_check(changesjson)
        if check != 200:
            return xprresponse('failure', check, {})

        uidjson = {"uid": changesjson['uid']}
        self.logger.info(f"Modifying user information of {uidjson}")
        self.logger.debug(f"Info provided to be modified is {changesjson}")
        # checks if the user is present in db
        self.logger.info("Checking if the user is present in the db")
        user = self.db_utils.find("users", uidjson)
        if len(user) == 0:
            errcode = error_codes.user_not_found
            self.logger.error(f"user {uidjson['uid']} not found in the db")
            return xprresponse('failure', errcode, {})

        self.logger.info("updating the user information")
        updateuser = self.db_utils.update("users", uidjson, changesjson)
        return xprresponse('success', '', {})

    def deactivateuser(self, uid):
        """
            Deletes an user and his info from the db

            Deletes the user from database

            Parameters:
                uid [str] : uid of the user

            Return:
                returns appropriate output
        """
        uidjson = {"uid": uid}
        # deletes the user from db

        deluser = self.db_utils.find("users", uidjson)
        if len(deluser) != 0:
            self.logger.info(f"deactivating the user {uidjson['uid']}")
            if 'activationStatus' in deluser[0] and \
                deluser[0]['activationStatus']:
                self.db_utils.update("users", uidjson,
                                     {"activationStatus": False})
                self.logger.info(f"user {uidjson['uid']} successfully deleted")
                return xprresponse('success', '', {})
            else:
                errcode = error_codes.user_already_deactivated
                return xprresponse('failure', errcode, {})
        else:
            errcode = error_codes.user_not_found
            self.logger.info("user not found")
            return xprresponse('failure', errcode, {})

    def getusers(self, filterjson):
        """
            Calls the db with input filters to fetch the list of users.
            After fetching, the users list is filtered before sending
            as output in order to send relevant information only

            Parameters:
                filterjson [json] : json with filter key & value pairs

            Return:
                Success -> [list] : returns list of users
                Failure -> [str] : returns db failure response
        """
        self.logger.info("getting all the users in the db")
        self.logger.debug(f"filterjson is : {filterjson}")
        users = self.db_utils.find("users", filterjson)
        # get users call retrieves whole user info from db
        # Filtering the data that needs to be shown as output
        self.logger.info("filtering the users before sending output")
        users = filteruseroutput(users)
        self.logger.debug(f'Output of users sent: {users}')
        return xprresponse('success', '', users)
 def __init__(self, attribute_name):
     self.logger = XprLogger()
     self.name = attribute_name
     self.metrics = dict()
Exemplo n.º 4
0
class ControllerClient:
    CONTROLLER_SECTION = 'controller'
    SERVER_URL = 'server_url'
    CLIENT_PATH = 'client_path'
    JENKINS_SECTION = 'jenkins'
    JENKINS_HOST = 'master_host'
    relogin_response = {
        "outcome": "failure",
        "error_code": "106",
        "results": {}
    }

    API_JSON_OUTCOME = "outcome"
    API_JSON_RESULTS = "results"
    API_JSON_ERROR_CODE = "error_code"
    API_JSON_SUCCESS = "success"
    API_JSON_FAILURE = "failure"

    def __init__(self, config_path=XprConfigParser.DEFAULT_CONFIG_PATH):
        self.logger = XprLogger()
        self.config = XprConfigParser(config_path)
        self.path = os.path.join(
            os.path.expanduser('~'),
            self.config[self.CONTROLLER_SECTION][self.CLIENT_PATH])
        self.token_file = '{}.current'.format(self.path)
        self.server_path = self.config[self.CONTROLLER_SECTION][
            self.SERVER_URL]

    def sso_login(self):
        """ It performs Single Sign-On authentication for the client.
        It follows following steps
        1. Check if token exists
        2. If exists: Send to the server for validation
            2.1 If token is validated then login is successful
            2.2 If token is not validated, assume token does not exist and go
            to point 3
        3. If no token exists:
            3.1 Print the SSO authentication url for user to login
            3.2 Send request to server every few seconds to check if user
            signed in successful. Wait for 60 seconds. Throw error if not
            logged in
            3.3 When user logged in, fetch the token and save

        """
        self.logger.info('CLIENT : Entering SSO Login Method')

        # Check if token exists:
        try:
            token = self.get_token()
        except ControllerClientResponseException:
            self.logger.info("No Token found")
            token = None

        # Since no token exist, ask for new login
        if token:
            url = f"{self.server_path}/sso/token_login"
            self.logger.debug('CLIENT : Making post request to server')
            data = {"token": token}
            try:
                response = self.send_http_request(url=url,
                                                  header=data,
                                                  http_method=HTTPMethod.POST,
                                                  data=data)
                return response
            except ControllerClientResponseException as e:
                self.logger.info("Assuming logging request failed")
                self.logger.info(e.message)

        url = f"{self.server_path}/sso/get_authentication_url"
        self.logger.debug('CLIENT : Making post request to server')
        response = self.send_http_request(url=url, http_method=HTTPMethod.GET)
        return response

    def sso_validate(self, validation_token):
        """
        Check whether SSO authentication is completed and successful
        Args:
            validation_token: sso validation token which is used to check if
                              a user has logged in or not.
        Returns:
        """
        # We keep requesting the sso server to test for
        interval_second = 2
        wait_second = 60
        start_time = time.time()
        while time.time() - start_time < wait_second:
            self.logger.debug('CLIENT : Making post request to server')
            url = f"{self.server_path}/sso/validate"
            data = {"validation_token": validation_token}
            try:

                response = self.send_http_request(url=url,
                                                  http_method=HTTPMethod.POST,
                                                  data=data)
                self.logger.info("Token validated")
                self.save_token(response["token"])
                return {"message": "SSO Login Successfull"}
            except ControllerClientResponseException:
                time.sleep(interval_second)
        self.logger.info('CLIENT : Existing SSO Login Method')
        raise ControllerClientResponseException("Session over without login",
                                                error_codes.server_error)

    def login(self, username, password):
        """Sends request to Controller server and
        get the status on login request"""
        self.logger.info('CLIENT : entering login method')
        if not os.path.isdir(self.path):
            os.makedirs(self.path, 0o755)
        if os.path.isfile(self.token_file):
            os.remove(self.token_file)

        if not username:
            self.logger.error('CLIENT : Empty username passed. Exiting.')
            raise ControllerClientResponseException("Username can't be empty",
                                                    error_codes.empty_uid)
        if not password:
            self.logger.error('CLIENT : Empty password passed. Exiting.')
            raise ControllerClientResponseException("Password can't be empty",
                                                    error_codes.empty_uid)

        url = f"{self.server_path}/auth"
        credentials = {"uid": username, "pwd": password}
        self.logger.debug('CLIENT : Making post request to server')
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.POST,
                                          data=credentials)

        self.save_token(token=response['access_token'])
        if 'relogin' in response and response['relogin']:
            self.logger.debug('CLIENT : already logged in. Saving new token.')
            return {"message": f"You are already logged in"}
        elif 'relogin' in response and not response['relogin']:
            self.logger.info(
                'CLIENT : Login successful. Writing token to file.')
            return {"message": f"Welcome, {username}!"}
        return response

    def save_token(self, token):
        """Token is saved in the local file system for """
        file = open(self.token_file, 'w+')
        file.write(token)
        file.close()
        self.logger.info('CLIENT : Token written to file. Exiting.')

    def get_token(self):
        """Token is saved in the local file system for """
        token = None
        try:
            with open(self.token_file, "r") as f:
                token = f.read()
        except FileNotFoundError:
            self.logger.error("No Token Found. Need to Relogin")
            raise ControllerClientResponseException(
                "No Session found. Login again", error_codes.expired_token)
        return token

    def logout(self):
        self.logger.info('CLIENT : entering logout method')
        url = f'{self.server_path}/auth'
        token = self.get_token()
        headers = {'token': token}
        self.logger.debug('CLIENT : Making delete request to server')
        self.send_http_request(url=url,
                               http_method=HTTPMethod.DELETE,
                               header=headers)
        os.remove(self.token_file)
        self.logger.info('CLIENT : Logout successful. Exiting.')
        return {"message": "Successfully logged out"}

    def get_clusters(self, argument):
        self.logger.info(f'CLIENT : entering get_clusters method '
                         f'with arguments {argument}')
        url = f'{self.server_path}/clusters'
        headers = {"token": self.get_token()}
        self.logger.debug('CLIENT : Making get request to server')
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.GET,
                                          header=headers,
                                          data=argument)
        self.logger.info('CLIENT : Get request successful. Exiting.')
        return response

    def deactivate_cluster(self, argument):

        self.logger.info('CLIENT : Entering deactivate_cluster method')
        if not argument:
            self.logger.error('CLIENT : No input arguments provided. Exiting.')
            raise ControllerClientResponseException(
                f"Please provide some input arguments ===",
                error_codes.incomplete_cluster_info)
        url = f'{self.server_path}/clusters'
        headers = {"token": self.get_token()}
        self.send_http_request(url=url,
                               http_method=HTTPMethod.DELETE,
                               header=headers,
                               data=argument)
        self.logger.info('CLIENT : Deactivation successful. Exiting.')
        return {"message": "Cluster deactivated."}

    def register_cluster(self, argument):
        self.logger.info('CLIENT : Entering register_cluster '
                         'with arguments {}'.format(argument))
        if not argument:
            self.logger.error('CLIENT : No input arguments provided. Exiting.')
            raise ControllerClientResponseException(
                f"Please provide some input arguments ===",
                error_codes.incomplete_cluster_info)
        url = f'{self.server_path}/clusters'
        headers = {"token": self.get_token()}
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.POST,
                                          header=headers,
                                          data=argument)
        self.logger.info('CLIENT : Cluster registration successful.Exiting.')
        return {
            "message":
            f"Cluster successfully registered with "
            f"ID {response} ###"
        }

    def register_user(self, user_json):
        url = f"{self.server_path}/users"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.POST,
                                          header={"token": self.get_token()},
                                          data=user_json)
        return response

    def get_users(self, filter_json):
        url = f"{self.server_path}/users"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.GET,
                                          header={"token": self.get_token()},
                                          data=filter_json)
        return response

    def modify_user(self, changes_json):
        url = f"{self.server_path}/users"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.PUT,
                                          header={"token": self.get_token()},
                                          data=changes_json)
        return response

    def update_password(self, password_json):
        url = f"{self.server_path}/user/pwd"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.PUT,
                                          header={"token": self.get_token()},
                                          data=password_json)
        return response

    def deactivate_user(self, uid_json):
        url = f"{self.server_path}/users"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.DELETE,
                                          header={"token": self.get_token()},
                                          data=uid_json)
        return response

    def register_node(self, node_json):
        url = f"{self.server_path}/nodes"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.POST,
                                          header={"token": self.get_token()},
                                          data=node_json)
        print(response)
        return response

    def get_nodes(self, filter_json):
        url = f"{self.server_path}/nodes"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.GET,
                                          header={"token": self.get_token()},
                                          data=filter_json)
        return response

    def provision_node(self, changes_json):
        url = f"{self.server_path}/nodes"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.PUT,
                                          header={"token": self.get_token()},
                                          data=changes_json)
        return response

    def deactivate_node(self, node_json):
        url = f"{self.server_path}/nodes"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.DELETE,
                                          header={"token": self.get_token()},
                                          data=node_json)
        return response

    def assign_node(self, assign_json):
        url = f"{self.server_path}/assign_node"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.PUT,
                                          header={"token": self.get_token()},
                                          data=assign_json)
        return response

    def check_for_declarative_json(self, project_json):
        """
        Checks if the provided declarative json exists and replaces that field
        with the contents of declarative json.
        Args:
            project_json: input file from user

        Returns: modified project_json

        """
        for pipeline in project_json['pipelines']:
            if not os.path.isfile(pipeline['declarative_json']):
                self.logger.error("declarative json not found")
                raise FileNotFoundException('Declarative JSON not found.')
            with open(pipeline['declarative_json'], 'r') as f:
                declarative_json_data = json.load(f)
                pipeline['declarative_json'] = declarative_json_data
        return project_json

    def create_project(self, project_json):
        if 'pipelines' in project_json:
            project_json = self.check_for_declarative_json(project_json)
        url = f"{self.server_path}/projects/manage"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.POST,
                                          header={"token": self.get_token()},
                                          data=project_json)
        return response

    def get_project(self, filter_json):
        url = f"{self.server_path}/projects/manage"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.GET,
                                          header={"token": self.get_token()},
                                          data=filter_json)
        return response

    def modify_project(self, changes_json):
        if 'pipelines' in changes_json:
            changes_json = self.check_for_declarative_json(changes_json)
        url = f"{self.server_path}/projects/manage"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.PUT,
                                          header={"token": self.get_token()},
                                          data=changes_json)
        return response

    def deactivate_project(self, project_json):
        url = f"{self.server_path}/projects/manage"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.DELETE,
                                          header={"token": self.get_token()},
                                          data=project_json)
        print("response is ", response)
        return response

    def build_project(self, argument):
        self.logger.info(f'CLIENT : Entering build_project '
                         f'with arguments {argument}')
        if not argument:
            self.logger.error('CLIENT : No input arguments provided. Exiting.')
            raise ControllerClientResponseException(
                f"Please provide some input arguments ===",
                error_codes.incomplete_cluster_info)
        url = f'{self.server_path}/projects/build'
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.POST,
                                          header={"token": self.get_token()},
                                          data=argument)
        self.logger.info('CLIENT : Project build successful.Exiting.')
        return {
            "message":
            "Project build successful!",
            "Build IDS":
            response,
            "Jenkins Pipeline":
            f"{self.config[self.JENKINS_SECTION][self.JENKINS_HOST]}"
            f"/blue/pipelines"
        }

    def get_build_version(self, argument):
        self.logger.info(f'CLIENT : entering get_build_version method '
                         f'with arguments {argument}')
        url = f'{self.server_path}/projects/build'
        self.logger.debug('CLIENT : Making get request to server')
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.GET,
                                          header={"token": self.get_token()},
                                          data=argument)
        return response

    def deploy_project(self, argument):
        self.logger.info(f'CLIENT : Entering deploy_project '
                         f'with arguments {argument}')
        if not argument:
            self.logger.error('CLIENT : No input arguments provided. Exiting.')

            raise ControllerClientResponseException(
                f"Please provide some input arguments ===",
                error_codes.incomplete_cluster_info)
        url = f'{self.server_path}/projects/deploy'
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.POST,
                                          header={"token": self.get_token()},
                                          data=argument)
        self.logger.info('CLIENT : Project deployed successfully.Exiting.')
        return {
            "message": "Project deployed successfully on the below IPs!",
            "Output": response
        }

    def undeploy_project(self, argument):
        self.logger.info(f'CLIENT : Entering undeploy_project '
                         f'with arguments {argument}')
        if not argument:
            self.logger.error('CLIENT : No input arguments provided. Exiting.')
            raise ControllerClientResponseException(
                f"Please provide some input arguments ===",
                error_codes.incomplete_cluster_info)

        url = f'{self.server_path}/projects/deploy'
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.DELETE,
                                          header={"token": self.get_token()},
                                          data=argument)
        self.logger.info('CLIENT : Project undeployed successfully.Exiting.')
        return {"message": "Project undeployed successfully!"}

    def update_xpresso(self):
        """
        Update xpresso project to the latest commit
        """
        # Send request to update server
        server_update_is_success = False
        url = f'{self.server_path}/update_xpresso'
        try:
            self.send_http_request(url, HTTPMethod.POST)
            server_update_is_success = True
        except ControllerClientResponseException as e:
            self.logger.error(e)

        # Update local
        package_manager = PackageManager()
        package_manager.run(package_to_install="UpdateLocalXpressoPackage",
                            execution_type=ExecutionType.INSTALL)
        response = {"client": "Updated"}
        if server_update_is_success:
            response["server"] = "Updated"
        else:
            response["server"] = "Update Failed"
        return response

    def fetch_version(self):
        """
        Fetches server version and client version, convert to a dict and
        returns.
        """
        url = f'{self.server_path}/version'
        json_response = self.send_http_request(url, HTTPMethod.GET)
        server_version = "None"
        if "version" in json_response:
            server_version = json_response["version"]
        client_version = get_version()
        return {
            "client_version": client_version,
            "server_version": server_version
        }

    def send_http_request(self,
                          url: str,
                          http_method: HTTPMethod,
                          data=None,
                          header: dict = None):
        request = HTTPRequest(method=http_method,
                              url=url,
                              headers=header,
                              data=data)
        handler = HTTPHandler()
        try:
            response = handler.send_request(request)
            json_response = response.get_data_as_json()
            if not json_response:
                raise ControllerClientResponseException(
                    "Request Failed", error_codes.server_error)
            elif (json_response[self.API_JSON_OUTCOME] == self.API_JSON_SUCCESS
                  and self.API_JSON_RESULTS in json_response):
                return json_response[self.API_JSON_RESULTS]
            elif (json_response[self.API_JSON_OUTCOME] == self.API_JSON_SUCCESS
                  and self.API_JSON_RESULTS not in json_response):
                return {}
            elif (self.API_JSON_RESULTS in json_response
                  and self.API_JSON_ERROR_CODE in json_response):
                raise ControllerClientResponseException(
                    json_response[self.API_JSON_RESULTS],
                    json_response[self.API_JSON_ERROR_CODE])
            elif self.API_JSON_ERROR_CODE in json_response:
                raise ControllerClientResponseException(
                    "Request Failed", json_response[self.API_JSON_ERROR_CODE])
            raise ControllerClientResponseException("Request Failed", -1)
        except (HTTPRequestFailedException, HTTPInvalidRequestException) as e:
            self.logger.error(str(e))
            raise ControllerClientResponseException("Server is not accessible",
                                                    error_codes.server_error)
        except JSONDecodeError as e:
            self.logger.error(str(e))
            raise ControllerClientResponseException(
                "Invalid response from server", error_codes.server_error)

    def create_repo(self, repo_json):
        """
        creates a repo on pachyderm cluster

        :param repo_json:
            information of repo i.e. name and description
        :return:
            returns operation status
        """
        url = f"{self.server_path}/repo"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.POST,
                                          header={"token": self.get_token()},
                                          data=repo_json)
        return response

    # def get_repo(self):
    #     """
    #
    #     :return:
    #     """
    #     url = f"{self.server_path}/repo"
    #     response = self.send_http_request(url=url,
    #                                       http_method=HTTPMethod.GET,
    #                                       header={"token": self.get_token()},
    #                                       data={})
    #     return response

    def create_branch(self, branch_json):
        """
        creates a branch in a repo

        :param branch_json:
            information of branch i.e. repo and branch names
        :return:
            operation status
        """
        url = f"{self.server_path}/repo"
        response = self.send_http_request(url=url,
                                          http_method=HTTPMethod.PUT,
                                          header={"token": self.get_token()},
                                          data=branch_json)
        return response

    def push_dataset(self, dataset_json):
        """
        pushes a dataset into pachyderm cluster

        :param dataset_json:
            information of dataset
        :return:
            operation status
        """
        url = f"{self.server_path}/dataset/manage"
        self.send_http_request(url=url,
                               http_method=HTTPMethod.PUT,
                               header={"token": self.get_token()},
                               data=dataset_json)
        manager = repo_manager.PachydermRepoManager()
        try:
            commit_id = manager.push_files(dataset_json)
            return {
                "message": f"Dataset push successful. commit id: {commit_id}"
            }
        except XprExceptions as err:
            return err.message

    def pull_dataset(self, dataset_json):
        """
        pulls a dataset from pachyderm cluster

        :param dataset_json:
            info of the dataset on pachyderm cluster
        :return:
            path of the dataset on user system
        """
        url = f"{self.server_path}/dataset/manage"
        self.send_http_request(url=url,
                               http_method=HTTPMethod.GET,
                               header={"token": self.get_token()},
                               data=dataset_json)
        manager = repo_manager.PachydermRepoManager()
        try:
            dataset_path = manager.manage_xprctl_dataset('pull', dataset_json)
            return {
                "message": f"Pull Successful, find the files at {dataset_path}"
            }
        except XprExceptions as err:
            return err.message

    def list_dataset(self, filter_json):
        """
        lists datasets saved on pachyderm cluster as per filter specs

        :param filter_json:
            info to filter required dataset
        :return:
            list of all the files and their props as per filter specs
        """
        url = f"{self.server_path}/dataset/list"
        self.send_http_request(url=url,
                               http_method=HTTPMethod.GET,
                               header={"token": self.get_token()},
                               data=filter_json)
        manager = repo_manager.PachydermRepoManager()
        try:
            dataset_list = manager.manage_xprctl_dataset('list', filter_json)
            return dataset_list
        except XprExceptions as err:
            return err.message
class PackageManager:
    """Manages the request for package installation and setup
    """

    MANIFEST_SCRIPT_KEY = "script_path"
    MANIFEST_MULTI_ARG_KEY = "script_multi_arguments"
    MANIFEST_DEPENDENCY_KEY = "dependency_packages"
    MANIFEST_SSH_CONFIG = "ssh_config"

    def __init__(self, config_path=XprConfigParser.DEFAULT_CONFIG_PATH):
        """
        1. Generate metadata of existing VM
        2. Reads the arguments and initiates the instance variable
        """

        self.logger = XprLogger()

        self.python_version = sys.version.split('\n')
        self.system = platform.system()
        self.machine = platform.machine()
        self.platform = platform.platform()
        self.uname = platform.uname()
        self.version = platform.version()
        self.arch = platform.architecture()
        self.config_path = config_path
        self.package_dependency = PackageDependency(config_path=config_path)

    def list(self):
        """ List all supported package group name

        Returns:
            list: list of available packages
        """
        return self.package_dependency.list_all()

    def run(self,
            package_to_install: str,
            execution_type: ExecutionType,
            parameters: dict = None):
        """
        Perform provided execution type on the given package name

        Args:
            parameters(dict): Additional parameters
            package_to_install(str): name of the package to install. Must match
                                     the supported package names
            execution_type(ExecutionType): Type of execution
        """
        if not self.package_dependency.check_if_supported(package_to_install):
            self.logger.error(
                "Unsupported Package Name : {}".format(package_to_install))
            return False

        dependency_list = self.package_dependency.get_dependency(
            package_to_install)
        self.logger.info(dependency_list)
        response = self.execute_recursive(dependency_list,
                                          0,
                                          execution_type,
                                          parameters=parameters)
        if not response:
            raise PackageFailedException("Package installation failed!!")
        self.logger.info(
            "{} installed successfully".format(package_to_install))
        return True

    def execute_recursive(self,
                          dependency_list: list,
                          current_index: int,
                          execution_type: ExecutionType,
                          parameters: dict = None):
        """
        Execute  recursively. If something failed then rollback
        """
        if current_index >= len(dependency_list):
            return True

        package_string = dependency_list[current_index]
        self.logger.info(package_string)
        try:
            self.execute(package_class=self.package_str_to_class(
                package_string, packages),
                         execution_type=execution_type,
                         parameters=parameters)
            current_index += 1
            return self.execute_recursive(dependency_list,
                                          current_index,
                                          execution_type,
                                          parameters=parameters)
        except PackageFailedException:
            self.logger.error("Failed to execute package {}".format(
                str(package_string)))
        return False

    def execute(self,
                package_class,
                execution_type: ExecutionType,
                parameters: dict = None):
        """
        Perform provided execution type on the given package name

        Args:
            parameters: Additional parameter required for installation
            package_class: name of the package to install.
                           Must match the supported package names.
            execution_type(ExecutionType): Type of execution
        Returns:
            bool: True,if the execution is successful
        """
        if package_class is None:
            self.logger.info(
                "{} Not Found in the hirarchy".format(package_class))
            return False
        self.logger.info(f"Running package {package_class} with parameters"
                         f"{parameters}")
        package_obj = package_class(config_path=self.config_path)
        if (execution_type == ExecutionType.INSTALL
                and package_obj.status(parameters=parameters)):
            return True
        elif execution_type == ExecutionType.INSTALL:
            return package_obj.install(parameters=parameters)
        elif execution_type == ExecutionType.UNINSTALL:
            return package_obj.uninstall(parameters=parameters)
        elif execution_type == ExecutionType.STATUS:
            return package_obj.status(parameters=parameters)
        elif execution_type == ExecutionType.START:
            return package_obj.start(parameters=parameters)
        elif execution_type == ExecutionType.STOP:
            return package_obj.stop(parameters=parameters)

        self.logger.error(str(package_obj) + " Not defined")
        return False

    def package_str_to_class(self, target_class_name: str, package_name):
        """ Converts class name into python class object. It looks for all
        classes in a package and matches the name to the class object

        Args:
            package_name(package): Find the target class name within this
                                   package name
            target_class_name(str): exact name of the class. It should match the
                                    class name as well

        Returns:
            Object: returns python class object, None otherwise
        """

        for _, modname, is_pkg in \
            pkgutil.iter_modules(package_name.__path__,
                                 package_name.__name__ + "."):
            imported_module = __import__(modname, fromlist=["dummy"])
            matched_class_name = None
            if is_pkg:
                matched_class_name = self.package_str_to_class(
                    target_class_name, imported_module)
            if matched_class_name:
                return matched_class_name
            for name, obj in inspect.getmembers(imported_module):
                if inspect.isclass(obj) and name == target_class_name:
                    return obj
        return None
class Project(XprObject):
    """
    This class represents a User
    """
    project_config_path = "/opt/xpresso.ai/config/project_config.json"
    with open(project_config_path, "r", encoding="utf-8") as config_file:
        try:
            project_config = json.load(config_file)
        except (FileNotFoundError, json.JSONDecodeError):
            raise ProjectConfigException

    def __init__(self, project_json=None):
        self.logger = XprLogger()
        """
        Constructor:
        """
        self.logger.debug(f"Project constructor called with {project_json}")
        super().__init__(project_json)

        # List of all the fields project can contain
        self.complete_fields = self.project_config["project_key_list"]
        # These are mandatory fields that needs to be provided in user_json
        self.mandatory_fields = ["name", "description", "owner"]

        # primaryRole of a user has to one of these
        self.valid_values = {"primaryRole": ["Dev", "PM", "DH", "Admin", "Su"]}

        # fields that cannot be modified
        self.unmodifiable_fields = ["name"]

        # fields that should be displayed in the output
        self.display_fields = self.project_config["project_output_fields"]
        self.logger.debug("User constructed successfully")

    def check_fields(self) -> int:
        """
            checks all the input fields in project information json

            checks if the provided fields type and format is same and in
            accordance with project format.
            Checks if the mandatory fields are provided.
        """
        # checks if the type of the input fields is valid
        for (field, field_type) in self.project_config["project_field_types"].items():
            if field in self.data:
                if type(self.data[field]).__name__ != field_type:
                    raise ProjectFieldsException(
                        f"type of '{field}' in project info is invalid"
                    )

        # checks if the mandatory fields are provided and if they are not empty
        for (field, field_flag) in self.project_config["project_field_flags"].items():
            if field_flag:
                if field in self.data and len(self.data[field]):
                    continue
                else:
                    raise IncompleteProjectInfoException()
        return 200

    @staticmethod
    def check_duplicate_component(component_list):
        """
            checks if any component is specified/used more than once
        """
        component_names = []
        for component in component_list:
            if component["name"] not in component_names:
                component_names.append(component["name"])
            else:
                component_name = component["name"]
                raise DuplicateComponentException(
                    f"Component/Pipeline with name '{component_name}' "
                    f"already exists in this project"
                )

    def component_info_check(self, component_list):
        """
            checks if the components are specified correctly

            checks if the type, flavor and name for each of the component
            is specified correctly in input json
        """
        component_field_set = self.project_config["component_fields"]
        for component in component_list:
            # check if each of type, name & flavor are specified correctly
            for (key, val) in component.items():
                # checks if only allowed keys are present in the component spec
                if key not in component_field_set:
                    raise ComponentFieldException(
                        f"'{key}' field not specified in component information"
                    )
                elif type(val).__name__ != component_field_set[key]:
                    raise ComponentFieldException(
                        f"Invalid field type of '{key}'"
                    )
                elif not len(val):
                    raise ComponentFieldException(f"'{key}' field is empty")

            # checks if all the mandatory fields are provided in the components
            for field in component_field_set:
                if field not in component:
                    raise ComponentFieldException(
                        f"'{field}' is not in the allowed fields of components"
                    )

            temp_type = component["type"]
            temp_flavor = component["flavor"]
            if temp_type not in self.project_config["component_flavors"]:
                raise ComponentFieldException(
                    f"Component type '{temp_type}' is not valid"
                )
            elif temp_flavor not in\
                    self.project_config["component_flavors"][temp_type]:
                raise ComponentFieldException(
                    f"'{temp_flavor}' flavor unavailable for {temp_type}"
                )

    def check_owner(self, persistence_manager):
        if 'uid' not in self.data["owner"]:
            raise ProjectOwnerException("Owner uid needs to be provided")
        elif not len(self.data["owner"]["uid"]):
            raise ProjectOwnerException("Owner uid is empty")
        else:
            uid_json = {'uid': self.data["owner"]["uid"]}
        owner = persistence_manager.find("users", uid_json)
        if not len(owner):
            raise ProjectOwnerException(
                "User not found with provided owner uid"
            )

    def check_developers(self, persistence_manager, devs=None):
        if not devs:
            developers = self.data["developers"]
        else:
            developers = devs
        for dev_name in developers:
            if type(dev_name).__name__ != 'str':
                raise ProjectDeveloperException(
                    f"Developer name should be string"
                )
            if not len(dev_name):
                raise ProjectDeveloperException(
                    "Developer name should not be empty"
                )
            else:
                developer = persistence_manager.find(
                    "users", {"uid": dev_name}
                    )
                if not len(developer):
                    raise ProjectDeveloperException(
                        f"Developer {dev_name} not found"
                    )

    def check_pipelines(self, pipelines, persistence_manager,
                        project_components):
        """
        validates the pipeline format provided and checks for consistency,
        duplicity.
        Args:
            pipelines: input pipeline info
            persistence_manager: persistence manager
            project_components : list of names of project components

        Returns:

        """
        default_pipeline_keys = self.project_config['pipeline_fields'].keys()
        keys_without_version = list(self.project_config['pipeline_fields'].keys())
        keys_without_version.remove('deploy_version_id')
        declarative_pipeline_builder = DeclarativePipelineBuilder(
            persistence_manager)
        for pipeline in pipelines:
            if set(default_pipeline_keys) != set(pipeline.keys()) and \
                    set(keys_without_version) != set(pipeline.keys()):
                self.logger.error("default keys defined incorrectly")
                raise ProjectPipelinesException
            for component in pipeline['components']:
                if component not in project_components:
                    self.logger.error('pipeline component not found in '
                                      'project components')
                    raise ComponentsSpecifiedIncorrectlyException(
                        f'Pipeline component "{component}" not '
                        f'found in project components.')
            declarative_pipeline_builder.prevalidate_declarative_json(
                pipeline['declarative_json'])
        self.check_duplicate_component(pipelines)

    def project_info_check(self, persistence_manager):
        """
        checks if the information provided for project creation is valid

        checks all the mandatory fields and its format. checks the
        components, developers and owner information.
        """
        keys = self.data.keys()
        # checks if the input fields provided are valid and present
        # support case type mistakes in next release
        for key in keys:
            if key not in self.project_config["project_key_list"]:
                raise ProjectFormatException(
                    f"Invalid field '{key}' in project json"
                    )

        # checks if all fields are specified correctly
        self.check_fields()
        # checks if the components are provided with info and validates format
        if "components" in self.data:
            self.component_info_check(self.data["components"])
            self.check_duplicate_component(self.data["components"])

        self.check_owner(persistence_manager)
        if "pipelines" in self.data:
            project_component_names = \
                [component['name'] for component in self.data['components']]
            self.check_pipelines(self.data['pipelines'], persistence_manager,
                                 project_component_names)
        # checks the developers format is valid if specified
        if "developers" in self.data and len(self.data["developers"]):
            self.check_developers(persistence_manager)

    def complete_project_info(self):
        out_json = deepcopy(self.project_config["sample_json"])
        name = self.data["name"]
        for key, val in self.data.items():
            if key != "components":
                out_json[key] = val
        if "components" in self.data:
            for component in self.data["components"]:
                component_name = component["name"]
                out_component = deepcopy(self.project_config["sample_component"])
                for key, val in component.items():
                    if key in self.project_config["sample_component"]:
                        out_component[key] = val
                out_component["dockerPrefix"] = (
                    f"dockerregistry.xpresso.ai/xprops/{name}/{component_name}--"
                )
                out_json["components"].append(out_component)
        self.data = out_json
        if "pipelines" in self.data:
            for pipeline in self.data["pipelines"]:
                if "deploy_version_id" not in pipeline.keys():
                    pipeline['deploy_version_id'] = 1

    def modify_info_check(self, changes_json, persistence_manager):
        # if not self.data["activationStatus"]:
        #     if "activationStatus" not in changes_json:
        #         return error_codes.activate_project_first
        #     elif not changes_json["activationStatus"]:
        #         return error_codes.activate_project_first
        #     else:
        #         changes_json["activationStatus"] = True
        if "activationStatus" in changes_json and \
                not changes_json["activationStatus"]:
            raise ProjectNotFoundException("Project is currently not active.")

        keys = changes_json.keys()
        # support case type mistakes in next release
        for key in keys:
            if key not in self.project_config["project_key_list"]:
                raise ProjectFieldsException(
                    f"Invalid field '{key}' provided for modify_project"
                )

        for (field, field_type) in\
                self.project_config["project_field_types"].items():
            if field in changes_json:
                if type(changes_json[field]).__name__ != field_type:
                    raise ProjectFieldsException(
                        f"Invalid type of field '{field}'"
                    )
        if "owner" in changes_json and len(changes_json["owner"]):
            self.check_owner(changes_json["owner"])

        if "developers" in changes_json and len(changes_json["developers"]):
            self.check_developers(
                persistence_manager, devs=changes_json["developers"]
                )

        if "components" in changes_json and len(changes_json["components"]):
            old_components = self.data["components"]
            new_components = changes_json["components"]
            self.component_info_check(new_components)
            self.check_duplicate_component(old_components + new_components)

        if "pipelines" in changes_json and len(changes_json["pipelines"]):
            new_pipeline = changes_json['pipelines']
            project_components = \
                [component['name'] for component in self.data['components']]
            if "components" in changes_json.keys():
                for component in changes_json['components']:
                    project_components.append(component['name'])
            self.check_pipelines(new_pipeline,
                                 persistence_manager, project_components)

    def modify_project_info(self, changes_json):
        name = self.data["name"]
        out_json = deepcopy(changes_json)
        out_json["components"] = self.data["components"]
        if "components" in changes_json:
            for component in changes_json["components"]:
                component_name = component["name"]
                out_component = deepcopy(self.project_config["sample_component"])
                for key, val in component.items():
                    if key in self.project_config["sample_component"]:
                        out_component[key] = val
                out_component["dockerPrefix"] = (
                    f"dockerregistry.xpresso.ai/xprops/{name}/{component_name}--"
                )
                out_json["components"].append(out_component)
        if 'pipelines' in changes_json:
            for pipeline in changes_json['pipelines']:
                if "deploy_version_id" not in pipeline.keys():
                    pipeline['deploy_version_id'] = 1
            if 'pipelines' not in self.data.keys():
                out_json['pipelines'] = changes_json['pipelines']
            else:
                out_json['pipelines'] = changes_json['pipelines'] + \
                                        self.data['pipelines']

        return out_json
Exemplo n.º 7
0
 def __init__(self, persistence_manager):
     self.logger = XprLogger()
     self.config = XprConfigParser()
     self.persistence_manager = persistence_manager
     self.ldapmanager = LdapManager()
Exemplo n.º 8
0
 def __init__(self, config, persistence_manager: MongoPersistenceManager):
     self.config = config
     self.logger = XprLogger()
     self.persistence_manager = persistence_manager
 def __init__(self, config_path):
     self.config = XprConfigParser(config_file_path=config_path)
     self.logger = XprLogger()
     self.metrics_list = None
     self.persistence_manager = None
Exemplo n.º 10
0
class UserManager:

    CONTROLLER_SECTION = 'controller'
    TOKEN_EXPIRY = 'soft_expiry'
    LOGIN_EXPIRY = 'hard_expiry'
    AUTHENTICATION_TYPE = "authentication_type"

    def __init__(self, persistence_manager):
        self.logger = XprLogger()
        self.config = XprConfigParser()
        self.persistence_manager = persistence_manager
        self.ldapmanager = LdapManager()

    def register_user(self, user_json):
        """
        register a new user in the persistence

        checks if the user already exists and then adds to persistence

        Parameters:
            user_json [json]: json with node information

        Return:
            Success -> 'OK' [str] : returns 'OK' as response
            Failure -> [str] : returns appropriate failure response
        """
        self.logger.debug(f"Entered register_user with {user_json}")
        # create user object
        new_user = User(user_json)
        # run validations
        new_user.validate_mandatory_fields()
        new_user.validate_field_values()
        # valid inputs - exception would have been raised in case of missing /
        # invalid info

        # now, set other fields as required
        # Password should not be saved as plain text in database.
        # Encrypting the password before saving it to database
        self.logger.info("Registering a new user")
        user_pwd = user_json["pwd"]
        new_user.set('pwd', sha512_crypt.encrypt(user_json['pwd']))
        new_user.set('loginStatus', False)
        new_user.set('activationStatus', True)
        new_user.set('nodes', [])
        self.logger.info("adding user to the database")

        self.persistence_manager.insert("users", new_user.data, False)
        self.logger.info("user successfully added to the persistence")

        try:
            self.ldapmanager.add(user_json["uid"], user_pwd)
        except Exception as e:
            self.logger.error("Unable to add user")
            print("unable to add user to ldap server : ", e)
            return XprResponse("failure", None, str(e))

        # NFS User directory changes
        self.logger.info("Setting up NFS for the user")
        nfs_manager = NFSUserManager(config=self.config)
        nfs_manager.setup_user_folder(user=user_json['uid'])
        self.logger.info("NFS set up")

        return XprResponse("success", None, None)

    def modify_user(self, filter_json, changes_json):
        """
            modify_user updates the user info in the persistence

            checks if user is available and then updates
            the info as per changes_json

            Parameters:
                filter_json: filter to find user
                changes_json: json with user changes info

            Return:
                Success -> 'OK' [str] : returns OK if provision_node succeeds
                Failure -> [str] : returns appropriate failure response
        """

        self.logger.debug(
            f"Modifying user information of {filter_json} to {changes_json}")
        # checks if the user is present in database
        self.logger.info("Checking if the user is present in the database")
        users = self.persistence_manager.find("users", filter_json)
        if not users or len(users) == 0:
            self.logger.error(f"user {filter_json} not found in the database")
            raise UserNotFoundException()

        # checks if the user password is also present in changes_json
        temp_user = User(changes_json)
        temp_user.validate_field_values()
        temp_user.validate_modifiable_fields()

        self.logger.info("updating the user information")
        self.persistence_manager.update("users", filter_json, changes_json)
        return XprResponse('success', '', {})

    def deactivate_user(self, uid):
        """
            Deletes an user and his info from the persistence

            Deletes the user from database

            Parameters:
                uid [str] : uid of the user

            Return:
                returns appropriate output
        """
        uid_json = {"uid": uid}
        # deletes the user from persistence

        del_users = self.persistence_manager.find("users", uid_json)
        if del_users and len(del_users) != 0:
            self.logger.info(f"deactivating the user {uid_json['uid']}")
            if 'activationStatus' in del_users[0] and \
                    del_users[0]['activationStatus']:
                self.persistence_manager.update("users", uid_json,
                                                {"activationStatus": False})
                self.logger.info(
                    f"user {uid_json['uid']} successfully deactivated")
                return XprResponse('success', '', {})
            else:
                raise DeactivatedUserException
        else:
            raise UserNotFoundException()

    def get_users(self, filter_json, apply_display_filter=True):
        """
            Calls the persistence with input filters to fetch the list of users.
            After fetching, the users list is filtered before sending
            as output in order to send relevant information only

            Parameters:
                filter_json [json] : json with filter key & value pairs

            Return:
                Success -> [list] : returns list of users
                Failure -> [str] : returns persistence failure response
        """
        self.logger.info("getting all the users in the persistence")
        self.logger.debug(f"filter_json is : {filter_json}")
        users = self.persistence_manager.find("users", filter_json)

        # filter user fields before sending the output
        if apply_display_filter:
            new_json = []
            for user_json in users:
                user = User(user_json)
                user.filter_display_fields()
                new_json.append(user.data)
            users = new_json
        # get users call retrieves whole user info from persistence
        # Filtering the data that needs to be shown as output
        self.logger.debug(f'Output of users sent: {users}')
        return users

    def update_password(self, password_json):
        """
        Updates user password

        Checks the password and updates the password on ldap and database

        :param password_json:
            contains the uid, old password & new password
        :return:
            raises exception in case of error
        """
        # uid is mandatory
        if "uid" not in password_json:
            self.logger.info("uid not provided for update password")
            raise IncompleteUserInfoException("User 'uid' not provided")
        uid_json = {"uid": password_json["uid"]}
        # fetches the user information
        users = self.persistence_manager.find("users", uid_json)
        if not len(users):
            self.logger.info("User not found for updating password")
            raise UserNotFoundException()
        # creates user object
        new_user = User(users[0])
        old_password_hash = users[0]["pwd"]
        old_password = password_json["old_pwd"]
        new_password = password_json["new_pwd"]
        # checks if the old password provided is same as the one saved in db
        if not sha512_crypt.verify(old_password, old_password_hash):
            raise InvalidPasswordException("Current password is incorrect")
        # Current and new password should not be same
        if old_password == new_password:
            raise InvalidPasswordException("Current and new password is same.")
        # checks if the password is valid and secure enough
        new_user.check_password(password_json["new_pwd"])
        # updates the password on ldap server
        self.ldapmanager.update_password(password_json["uid"], old_password,
                                         new_password)
        hashed_pwd = sha512_crypt.encrypt(new_password)
        update_json = {"pwd": hashed_pwd}
        self.persistence_manager.update("users", uid_json, update_json)
Exemplo n.º 11
0
class AbstractPackage:
    """
    Abstract base class for packages. It has one job to do, is execute one
    packages
    """
    def __init__(
            self,
            executor: CommandExecutor = None,
            config_path: XprConfigParser = XprConfigParser.DEFAULT_CONFIG_PATH
    ):
        self.executor = executor
        self.config_path = config_path
        self.logger = XprLogger()

    @abstractmethod
    def install(self, **kwargs):
        """
        Run installation scripts
        """

    @abstractmethod
    def uninstall(self, **kwargs):
        """
        Removes installed libraries
        """

    @abstractmethod
    def start(self, **kwargs):
        """
        Start the service/stop if required
        """

    @abstractmethod
    def stop(self, **kwargs):
        """
        Stop the service/stop if required
        """

    @abstractmethod
    def status(self, **kwargs):
        """
        Checks is the libraries are installed and running
        Returns:
             bool: True, if libraries are setup correctly
        """

    def execute_command(self, command):
        self.logger.info(f"Running command: {command}")
        try:
            return self.executor.execute(command)
        except CommandExecutionFailedException:
            self.logger.error("Command failed {}".format(command))
            raise PackageFailedException(
                "Base Ubuntu Package Installation Failed")

    def execute_command_with_output(self, command):
        self.logger.info(f"Running command: {command}")
        try:
            return self.executor.execute_with_output(command)
        except CommandExecutionFailedException:
            self.logger.error("Command failed {}".format(command))
            raise PackageFailedException(
                "Base Ubuntu Package Installation Failed")
    def execute(self, **kwargs):
        """
        Mounts the NFS on the VM
        """
        logger = XprLogger()
        if not linux_utils.check_root():
            logger.fatal("Please run this as root")
        logger.info("Mounting NFS File")
        subnet_to_nfs_map = self.config[self.NFS_SECTION][self.SUBNET_MAP_KEY]
        ip_address = linux_utils.get_ip_address()
        matched_nfs = None
        for nfs, subnet in subnet_to_nfs_map.items():
            logger.info("Matching {} {}".format(subnet, ip_address))
            check = re.match(subnet, ip_address)
            print(check)
            if check:
                matched_nfs = nfs
                break

        if not matched_nfs:
            logger.info("Could not determine nfs value")
            return False

        mount_location = self.config[self.NFS_SECTION][self.MOUNT_LOCATION_KEY]
        nfs_location = self.config[self.NFS_SECTION][self.NFS_LOCATION_KEY]
        mount_script = "mount {}:{} {}".format(matched_nfs, nfs_location,
                                               mount_location)
        logger.info("Mounting {}".format(mount_script))
        try:
            linux_utils.create_directory(mount_location, 0o755)
            self.executor.execute(mount_script)
            logger.info("Mount Succesful")
            logger.info("Updating fstab file")
            with open(self.FSTAB_FILE, "a+") as f:
                fstab_statement = "{}:{}    {}   nfs " \
                                  "auto,nofail,noatime,nolock,intr,tcp," \
                                  "actimeo=1800 0 0" \
                    .format(matched_nfs, nfs_location, mount_location)
                logger.info(
                    "Updating fstab file with {}".format(fstab_statement))
                f.write(fstab_statement)
                logger.info("Update Successful")
        except CommandExecutionFailedException as e:
            logger.error("Script Failed to run = {}\n{}".format(
                mount_script, str(e)))
            return False
        return True
Exemplo n.º 13
0
    def __init__(self, persistence_manager):
        self.persistence_manager = persistence_manager
        self.logger = XprLogger()

        config_path = XprConfigParser.DEFAULT_CONFIG_PATH
        self.config = XprConfigParser(config_path)
Exemplo n.º 14
0
class PackageDependency:
    """
    Created a  directed acyclic package dependency graph
    using a given dependency json.
    """

    NONE_PACKAGE = "None"
    DEPENDENCY_SECTION = "pkg_dependency"
    DEPENDENCY_CONFIG_FILE = "dependency_config_file"

    def __init__(self, config_path=XprConfigParser.DEFAULT_CONFIG_PATH):
        super().__init__()
        self.config = XprConfigParser(config_path)["packages_setup"]
        self.logger = XprLogger()
        dependency_config_file = self.config[self.DEPENDENCY_SECTION][
            self.DEPENDENCY_CONFIG_FILE]

        if not os.path.exists(dependency_config_file):
            self.logger.error(("Unable to find the dependency js"
                               "file at the mentioned path"))
            raise PackageFailedException("Invalid dependency  config file")

        try:
            with open(dependency_config_file) as config_fs:
                dependency_config = json.load(config_fs)
        except EnvironmentError as err:
            self.logger.fatal(err)
            raise PackageFailedException("Invalid config file")

        self.graph = nx.DiGraph()
        edges = list()

        for key in dependency_config:
            for value in dependency_config[key]:
                edges.append((key, value))

        self.graph.add_edges_from(edges)
        if not nx.is_directed_acyclic_graph(self.graph):
            self.logger.fatal(("Unable to handle dependencies due to cyclic "
                               "loop"))
            self.graph = None
            raise PackageFailedException("Cyclic Dependency Found")

    def visualize_dependency_graph(self):
        """
        Created a plot for the directed dependency graph
        """
        if self.graph is None:
            self.logger.error("Graph value none cannot be plotted")
            return

        nx.draw(self.graph, cmap=plt.get_cmap('jet'), with_labels=True)
        plt.show()

    def check_if_supported(self, package_name: str):
        """
        Args:
            package_name(str)

        :return:
            bool: Return True if supported. False, otherwise
        """
        return bool(self.graph.has_node(package_name))

    def list_all(self):
        """
        Extracts the value of all nodes(packages) present in graph

        Returns:
            list: Array consisting of all node(packages) value
        """
        if self.graph is None:
            self.logger.error("Graph value none cannot be iterated")
            return list()

        nodes = list()
        for node in self.graph.nodes():
            if node == self.NONE_PACKAGE:
                continue
            nodes.append(node)
        return nodes

    def get_dependency(self, package_name: str) -> list:
        """
        List of dependencies

        Args:
            package_name(str): Name of the package

        Returns:
            list: List of dependencies required for the package_name
                  installation
        """

        if not self.check_if_supported(package_name=package_name):
            self.logger.error(
                "{} package not present in config".format(package_name))
            return list()

        self.logger.info(("Running Topological sorting on "
                          "Package Dependency Graph"))

        try:
            topological_sort_list = list(
                reversed(list(nx.topological_sort(self.graph))))
        except nx.NetworkXError as error:
            self.logger.error(error)
            raise PackageFailedException("Topological sort is defined for "
                                         "directed graphs only")
        except nx.NetworkXUnfeasible as error:
            self.logger.error(error)
            raise PackageFailedException(
                "Not a directed acyclic graph (DAG) "
                "and hence no topological sort exists")

        descendants = nx.descendants(self.graph, package_name)
        dependent_packages = []
        for pkg in topological_sort_list:
            if pkg in descendants and pkg != self.NONE_PACKAGE:
                dependent_packages.append(pkg)
        if package_name != self.NONE_PACKAGE:
            dependent_packages.append(package_name)

        return dependent_packages
Exemplo n.º 15
0
class AuthenticationManager:
    """
    This class provides user authentication functionality for Xpresso Controller
    """

    AUTHENTICATION_TYPE = "authentication_type"

    def __init__(self, persistence_manager):
        self.persistence_manager = persistence_manager
        self.logger = XprLogger()
        self.ldapmanager = LdapManager()
        config_path = XprConfigParser.DEFAULT_CONFIG_PATH
        self.config = XprConfigParser(config_path)

    def login(self, credentials):
        """
        Authentication method for user login.
        Args:
            credentials: object containing uid and pwd

        Returns: dictionary object consisting of access token

        """
        try:
            # validate data
            authentication_context = AuthenticationContext(credentials)
            uid = authentication_context.get('uid')
            self.logger.debug('entering login method '
                              'with uid {}'.format(uid))
            check = self.validate_credentials(authentication_context)
            if check == error_codes.already_logged_in:
                self.logger.debug('Relogin request from {}.'.format(uid))
                relogin = True
            else:
                relogin = False
            self.logger.debug('Providing new token to {}.'.format(uid))
            token_info = self.generate_token()
            self.save_token({"uid": uid}, token_info)
            credentials = {
                'access_token': token_info.token,
                'relogin': relogin
            }
            response = XprResponse('success', None, credentials)
            self.logger.debug('Login successful. Exiting.')
            return response

        except AuthenticationFailedException as e:
            self.logger.error('Authentication failed for user {}'.format(uid))
            return XprResponse("failure", e.error_code,
                               {"message": "Authentication failed"})

    def validate_credentials(self, authentication_context):

        uid = authentication_context.get('uid')
        pwd = authentication_context.get('pwd')
        self.logger.debug('validating credentials for {}'.format(uid))

        users = UserManager(self.persistence_manager).get_users({"uid": uid},
                                                                False)
        print('validate_credentials: {}'.format(users))
        if not users:
            self.logger.error("User {} not found".format(uid))
            raise UserNotFoundException(f"User {uid} not found")
        hashed_pwd = users[0]['pwd']
        if not self.authenticate_password(uid, pwd, hashed_pwd):
            self.logger.error("Wrong password entered by {}".format(uid))
            raise WrongPasswordException("Wrong password")
        elif not users[0]["activationStatus"]:
            self.logger.error("User {} has been deactivated".format(uid))
            raise DeactivatedUserException("This user is deactivated. "
                                           "Please reactivate first")
        elif users[0]['loginStatus']:
            self.logger.debug("User '{}' is already logged in.".format(uid))
            return error_codes.already_logged_in
        elif not users[0]['activationStatus']:
            raise DeactivatedUserException(
                "This user is deactivated. Please reactivate first")
        print('credentials validated for {}'.format(uid))
        return True

    def authenticate_password(self, uid, pwd, hashed_pwd):
        """
        Uses differnet authentication method to check if credentials are
        valid
        """
        if self.config[self.AUTHENTICATION_TYPE] == "ldap":
            try:
                self.ldapmanager.authenticate(uid, pwd)
                return True
            except ldap.INVALID_CREDENTIALS as e:
                self.logger.error("Wrong password entered by {}".format(uid))
                raise WrongPasswordException(str(e))
            except ldap.LDAPException as e:
                self.logger.error("Invalid credentials {}".format(uid))
                raise AuthenticationFailedException(str(e))
        elif self.config[self.AUTHENTICATION_TYPE] == "mongodb":
            if not sha512_crypt.verify(pwd, hashed_pwd):
                self.logger.error("Wrong password entered by {}".format(uid))
                raise WrongPasswordException("Wrong Password")
            return True
        return False

    def generate_token(self):
        self.logger.debug('Generating token')
        token_info = TokenInfo(None)
        token_info.generate()
        return token_info

    def save_token(self, search_filter, token_info):
        # print(search_filter)
        UserManager(self.persistence_manager).modify_user(
            search_filter, {
                "token": token_info.token,
                "loginStatus": token_info.login_status,
                "loginExpiry": token_info.login_expiry,
                "tokenExpiry": token_info.token_expiry
            })

    def logout(self, token):
        """
        Authentication method for user logout
        Args:
            token: access token

        Returns: Deletion status (True/False)

        """
        self.logger.info('entering logout method')
        status = self.delete_token(token)
        self.logger.debug(
            'exiting logout method with status {}'.format(status))
        return XprResponse("success", None, {})

    def delete_token(self, token):
        # delete token from database & change status
        token_info = TokenInfo(None)
        self.save_token({"token": token}, token_info)
        return True

    def modify_user_access(self, token, uid):
        users = UserManager(self.persistence_manager).get_users(
            {"token": token}, False)
        if users and len(users) > 0:
            if (uid == users[0]['uid']) or (users[0]['primaryRole']
                                            == 'Admin'):
                return True
            else:
                return False
        else:
            return False

    def validate_token(self, token, access_level):
        users = UserManager(self.persistence_manager).get_users(
            {"token": token}, False)
        if not users:
            self.logger.debug('Tokens do not match. Re-login needed.')
            raise IncorrectTokenException
        else:
            user = User(users[0])
            token_info = user.get_token_info()
            if token_info.has_expired():
                self.logger.debug('Token expired for {}.'
                                  'Logging out.'.format(user.get('uid')))
                token_info = TokenInfo(None)
                self.save_token({"token": token}, token_info)
                raise ExpiredTokenException
            elif not self.check_access(user.get('primaryRole'), access_level):
                self.logger.debug('User access check failed! Exiting.')
                raise PermissionDeniedException("Permission Denied")
        self.logger.info('revalidating token')
        self.revalidate_token(token_info)
        self.logger.info('exiting validate_token method')
        return True

    def validate_build_deploy_token(self, token, project):
        users = UserManager(self.persistence_manager).get_users(
            {"token": token}, False)
        if not users or len(users) == 0:
            self.logger.debug('Tokens do not match. Re-login needed.')
            raise IncorrectTokenException
        else:
            user = User(users[0])
            token_info = user.get_token_info()
            if token_info.has_expired():
                self.logger.debug('Token expired for {}.'
                                  'Logging out.'.format(users[0]['uid']))
                token_info = TokenInfo(None)
                self.save_token({"token": token}, token_info)
                raise ExpiredTokenException

        if "name" not in project:
            raise ProjectNotFoundException("Project name is empty")

        search_filter = {"name": project["name"]}
        project_info = self.persistence_manager.find('projects', search_filter)
        if not project_info:
            self.logger.error('No such project found.')
            raise ProjectNotFoundException("No such Project Found")
        if users[0]['uid'] != project_info[0]['owner']['uid'] and \
                levels[users[0]['primaryRole']] < levels['Admin']:
            self.logger.debug('User access check failed! Exiting.')
            raise PermissionDeniedException
        self.logger.info('revalidating token')
        self.revalidate_token(token_info)
        self.logger.info('exiting validate_token method')
        return project_info

    def revalidate_token(self, token_info):
        token_info.revalidate()
        self.save_token({"token": token_info.token}, token_info)
        self.logger.info('token revalidated.')

    def check_access(self, primary_role, access_level):
        self.logger.debug('Checking access')
        return levels[access_level] <= levels[primary_role]