class TortugaWsApi(RestApiClient): """ Base tortuga ws api class. """ def __init__(self, username: Optional[str] = None, password: Optional[str] = None, baseurl: Optional[str] = None, verify: bool = True): self._cm = ConfigManager() if not baseurl: baseurl = '{}://{}:{}'.format( self._cm.getAdminScheme(), self._cm.getInstaller(), self._cm.getAdminPort() ) if username is None and password is None: logger.debug('Using built-in user credentials') username = self._cm.getCfmUser() password = self._cm.getCfmPassword() super().__init__(username, password, baseurl, verify) self.baseurl = '{}/{}'.format(self.baseurl, WS_API_VERSION) def process_response(self, response: requests.Response): check_status(response.headers) return super().process_response(response)
def get_web_service_config(args: argparse.Namespace): """ Gets url, username, and password for the Tortuga web service. :param argparse.Namespace args: argparse Namespace instance :return tuple: (url, username, password) """ username = password = url = None cfg_file = os.path.join(os.path.expanduser('~'), '.local', 'tortuga', 'credentials') if os.path.exists(cfg_file): cfg = configparser.ConfigParser() cfg.read(cfg_file) username = cfg.get('default', 'username') \ if cfg.has_section('default') and \ cfg.has_option('default', 'username') else None password = cfg.get('default', 'password') \ if cfg.has_section('default') and \ cfg.has_option('default', 'password') else None url = cfg.get('default', 'url') \ if cfg.has_section('default') and \ cfg.has_option('default', 'url') else None if args.url: url = args.url elif os.getenv('TORTUGA_WS_URL'): url = os.getenv('TORTUGA_WS_URL') if args.username: username = args.username elif os.getenv('TORTUGA_WS_USERNAME'): username = os.getenv('TORTUGA_WS_USERNAME') if args.password: password = args.password elif os.getenv('TORTUGA_WS_PASSWORD'): password = os.getenv('TORTUGA_WS_PASSWORD') # # CLI arguments should override the environment variable # if os.getenv('TORTUGA_WS_NO_VERIFY'): verify = False else: verify = args.verify if username is None and password is None: cm = ConfigManager() username = cm.getCfmUser() password = cm.getCfmPassword() return url, username, password, verify
class TortugaWsApiClient: """ Tortuga ws api client class. """ def __init__(self, endpoint: str, username: Optional[str] = None, password: Optional[str] = None, base_url: Optional[str] = None, verify: bool = True) -> None: self._cm = ConfigManager() if not base_url: base_url = '{}://{}:{}'.format( self._cm.getAdminScheme(), self._cm.getInstaller(), self._cm.getAdminPort() ) if username is None and password is None: logger.debug('Using built-in user credentials') username = self._cm.getCfmUser() password = self._cm.getCfmPassword() self._client = RestApiClient( username=username, password=password, baseurl=base_url, verify=verify ) self._client.baseurl = '{}/{}/{}/'.format(base_url, WS_API_VERSION, endpoint) def _build_query_string(self, params: dict) -> str: \ # pylint: disable=no-self-use return '&'.join([f'{k}={v}' for k, v in params.items()]) def list(self, **params) -> list: path = '/' query_string = self._build_query_string(params) if query_string: path += '?{}'.format(query_string) return self._client.get(path) def get(self, id_: str) -> dict: path = '/{}'.format(id_) return self._client.get(path)
class AuthManager(TortugaObjectManager, Singleton): def __init__(self): super(AuthManager, self).__init__() self._configManager = ConfigManager() self.__principals = {} self.__loadPrincipals() def cryptPassword(self, cleartext, salt="$1$"): \ # pylint: disable=no-self-use """ Return crypted password.... """ return crypt.crypt(cleartext, salt) def reloadPrincipals(self): """ This is used to reload the principals in auth manager """ self.__principals.clear() self.__loadPrincipals() def __loadPrincipals(self): """ Load principals for config manager and datastore """ # Create builtin cfm principal cfmUser = AuthPrincipal( self._configManager.getCfmUser(), self.cryptPassword(self._configManager.getCfmPassword()), {'roles': 'cfm'}) # Add cfm user self.__principals[cfmUser.getName()] = cfmUser # Add users from DB if self._configManager.isInstaller(): for admin in getAdminApi().getAdminList(): self.__principals[admin.getUsername()] = AuthPrincipal( admin.getUsername(), admin.getPassword(), attributeDict={'id': admin.getId()}) def getPrincipal(self, username, password): """ Get a principal based on a username and password """ principal = self.__principals.get(username) if principal and principal.getPassword() == crypt.crypt( password, principal.getPassword()): return principal return None
class TortugaWsApiClient: """ Tortuga ws api client class. """ def __init__(self, endpoint: str, token: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, base_url: Optional[str] = None, verify: bool = True) -> None: self._cm = ConfigManager() if not base_url: base_url = '{}://{}:{}'.format(self._cm.getAdminScheme(), self._cm.getInstaller(), self._cm.getAdminPort()) if not token: if username is None and password is None: logger.debug('Using built-in user credentials') username = self._cm.getCfmUser() password = self._cm.getCfmPassword() self._client = RestApiClient(token=token, username=username, password=password, baseurl=base_url, verify=verify) self._client.baseurl = '{}/{}/{}/'.format(base_url, WS_API_VERSION, endpoint) def _build_query_string(self, params: dict) -> str: return '&'.join([f'{k}={v}' for k, v in params.items()]) def list(self, **params) -> list: path = '/' query_string = self._build_query_string(params) if query_string: path += '?{}'.format(query_string) return self._client.get(path) def get(self, id_: str) -> dict: path = '/{}'.format(id_) return self._client.get(path) def post(self, data: dict) -> dict: path = '/' return self._client.post(path, data=data) def put(self, data: dict) -> dict: if not data or 'id' not in data.keys(): raise Exception('Object does not have an id field') id_ = data['id'] if not id_: raise Exception('Object id is invalid') path = '/{}'.format(id_) return self._client.put(path, data=data) def delete(self, id_: str): path = '/{}'.format(id_) return self._client.delete(path)
class TortugaScriptConfig(Config): AUTH_METHOD_PASSWORD = '******' AUTH_METHOD_TOKEN = 'token' NAVOPS_CLI = '/opt/navops-launch/bin/navopsctl' DEFAULT_FILENAME = os.path.join(os.path.expanduser('~'), '.tortuga', 'config') def __init__(self, **kwargs): # # Internal properties # self._filename = None self._cm = ConfigManager() # # Determine the username/password to use as default # default_username = self._cm.getCfmUser() default_password = self._cm.getCfmPassword() if default_password == 'not-set': default_username = None default_password = None # # Check for default navops cli location # default_navops_cli = self.NAVOPS_CLI if not os.path.exists(default_navops_cli): default_navops_cli = None # # Configuration settings # self.url = kwargs.get('url', self._cm.getInstallerUrl()) self.token = kwargs.get('token', None) self.navops_cli = kwargs.get('navops_cli', default_navops_cli) self.username = kwargs.get('username', default_username) self.password = kwargs.get('password', default_password) self.verify = kwargs.get('verify', True) def _load_from_environment(self): if os.getenv('TORTUGA_WS_URL'): self.url = os.getenv('TORTUGA_WS_URL') if os.getenv('TORTUGA_WS_USERNAME'): self.username = os.getenv('TORTUGA_WS_USERNAME') if os.getenv('TORTUGA_WS_PASSWORD'): self.password = os.getenv('TORTUGA_WS_PASSWORD') if os.getenv('TORTUGA_WS_TOKEN'): self.token = os.getenv('TORTUGA_WS_TOKEN') if os.getenv('TORTUGA_WS_NO_VERIFY'): self.verify = False @classmethod def load(cls, filename: str = None) -> 'TortugaScriptConfig': # # If a file name is provided, then we try to load that first # if filename: config = cls._load_from_file(filename) # # If no filename is provided, then we have to figure out where to # get a configuration # else: # # First, check if the user has a config in their home directory # if os.path.exists(cls.DEFAULT_FILENAME): config = cls._load_from_file(cls.DEFAULT_FILENAME) # # Otherwise, create a new config from scratch # else: config = cls() # # Override the config with any settings provided from the # environment # config._load_from_environment() return config @classmethod def _load_from_file(cls, filename) -> 'TortugaScriptConfig': if not os.path.exists(filename): raise ConfigFileNotFoundException( 'Config file not found: {}'.format(filename)) with open(filename) as fp: try: config_data = json.load(fp) except json.JSONDecodeError: raise ConfigException( 'Invalid config file: {}'.format(filename)) try: unmarshalled = TortugaScriptConfigSchema().load(config_data) except ValidationError: raise ConfigException('Invalid config file: {}'.format(filename)) return TortugaScriptConfig(**unmarshalled.data) def save(self, filename: str = None): if not filename: if self._filename: filename = filename else: filename = TortugaScriptConfig.DEFAULT_FILENAME if not os.path.exists(filename): os.makedirs(os.path.dirname(filename), exist_ok=True, mode=0o700) marshalled = TortugaScriptConfigSchema().dump(self) with open(filename, 'w') as fp: json.dump(marshalled.data, fp, indent=4) def get_auth_method(self) -> str: """ Gets the authentication method that should be used. :return str: token or password :raises ConfigException: if no auth method is configured """ # # For the CFM user, always use password authentication # if self.username == self._cm.getCfmUser() and self.password: return self.AUTH_METHOD_PASSWORD # # For all other cases, if the navops CLI is present, or there # is a token, use token-based authentication # if self.navops_cli or self.token: return self.AUTH_METHOD_TOKEN # # Otherwise, fall back to password authentication # if self.username and self.password: return self.AUTH_METHOD_PASSWORD raise ConfigException('Authentication required. Use "tortuga login".') def get_token(self) -> str: """ Gets the current authentication token. :return str: the token :raises ConfigException: if token is unavailable """ if self.navops_cli: try: return self._get_navops_token() except ConfigException: pass if self.token: return self.token raise ConfigException('Authentication required. Use "tortuga login".') def _get_navops_token(self) -> str: cmd = '{} token'.format(self.navops_cli) try: p = executeCommand(cmd) except Exception as ex: logger.info(str(ex)) raise ConfigException(str(ex)) if p.getExitStatus() != 0: raise ConfigException(p.getStdErr()) return p.getStdOut().decode()
class TortugaWsApi: """ Base tortuga ws api class. """ def __init__(self, username=None, password=None): self._logger = logging.getLogger('tortuga.wsapi.{0}'.format( self.__class__.__name__)) self._logger.addHandler(logging.NullHandler()) self._cm = ConfigManager() if username is None and password is None: self._logger.debug('[%s] Using built-in user credentials' % (self.__module__)) username = self._cm.getCfmUser() password = self._cm.getCfmPassword() self._username = username self._password = password self._sm = None def _getWsUrl(self, url): """Extract scheme and net location from provided url. Use defaults if none exist.""" result = urlparse(url) scheme = result.scheme if result.scheme else \ self._cm.getAdminScheme() netloc = result.netloc if result.netloc else \ '{0}:{1}'.format(self._cm.getInstaller(), self._cm.getAdminPort()) return '{0}://{1}'.format(scheme, netloc) def _getSessionManager(self): if not self._sm: self._sm = sessionManager.createSession() return self._sm def getLogger(self): """ Get logger for this class. """ return self._logger def getConfigManager(self): """ Return configmanager reference """ return self._cm def sendSessionRequest(self, url, method='GET', contentType='application/json', data='', acceptType='application/json'): """ Send authorized session request Raises: UserNotAuthorized """ sm = self._getSessionManager() if not sm.hasSession(): if self._username is None: raise UserNotAuthorized('Username not supplied') if self._password is None: raise UserNotAuthorized('Password not supplied') wsUrl = self._getWsUrl(url) # establishSession() sets the 'wsUrl' so the explicit call # to setHost() is not required sm.establishSession(wsUrl, self._username, self._password) return sm.sendRequest(url, method, contentType, data, acceptType=acceptType) def sendRequest(self, url, method='GET', contentType='application/json', data='', acceptType='application/json'): """ Send unauthorized request. """ sm = self._getSessionManager() # Because there's no call to establishSession(), explicitly call # setHost() sm.setHost(self._getWsUrl(url)) return self._getSessionManager().sendRequest(url, method, contentType, data, acceptType)
class OSSupport(OsSupportBase): def __init__(self, osFamilyInfo): super(OSSupport, self).__init__(osFamilyInfo) self._cm = ConfigManager() self._globalParameterDbApi = GlobalParameterDbApi() try: depot_dir = \ self._globalParameterDbApi.getParameter('depot').getValue() except ParameterNotFound: # Fallback to legacy default depot_dir = '/depot' self._cm.setDepotDir(depot_dir) def getPXEReinstallSnippet(self, ksurl, node, hardwareprofile=None, softwareprofile=None): \ # pylint: disable=no-self-use # General kickstart/kernel parameters # Find the first nic marked as bootable nics = [nic for nic in node.nics if nic.boot] if not nics: raise NicNotFound( 'Node [%s] does not have a bootable NIC' % (node.name)) # Choose the first one nic = nics[0] if hardwareprofile is None: hardwareprofile = node.hardwareprofile if softwareprofile is None: softwareprofile = node.softwareprofile # Use settings from software profile, if defined, otherwise use # settings from hardware profile. bootParams = getBootParameters(hardwareprofile, softwareprofile) kernel = bootParams['kernel'] kernelParams = bootParams['kernelParams'] initrd = bootParams['initrd'] bootargs = [ ] if softwareprofile.os.family.version == '7': # RHEL 7.x bootargs.append('inst.ks=%s' % (ksurl)) else: # RHEL 5.x and 6.x bootargs.append('ks=%s' % (ksurl)) bootargs.append('ksdevice=%s' % (nic.networkdevice.name)) # Append kernel parameters, if defined. if kernelParams: bootargs.append(kernelParams) result = '''\ kernel %s append initrd=%s %s''' % (kernel, initrd, ' '.join(bootargs)) return result def __get_kickstart_network_entry(self, dbNode, hardwareprofile, nic): \ # pylint: disable=no-self-use bProvisioningNic = nic.network == hardwareprofile.nics[0].network installer_private_ip = hardwareprofile.nics[0].ip if not bProvisioningNic and not nic.network.usingDhcp and not nic.ip: # Unconfigured public static IP network return None bActivate = False # By default, all interfaces are enabled at on boot bOnBoot = True # Use the network device name, as specified in the hardware profile netargs = [ 'network --device %s' % (nic.networkdevice.name) ] if bProvisioningNic: netargs.append( '--bootproto %s' % ( 'static' if bProvisioningNic or not nic.network.usingDhcp else 'dhcp')) netargs.append('--ip=%s' % (nic.ip)) netargs.append('--netmask=%s' % (nic.network.netmask)) netargs.append('--nameserver=%s' % (installer_private_ip)) bActivate = True else: if nic.network and nic.network.usingDhcp: netargs.append('--bootproto dhcp') else: netargs.append('--bootproto static') if nic.ip: netargs.append('--ip=%s' % (nic.ip)) netargs.append('--netmask=%s' % (nic.network.netmask)) else: # Do not enable interface if it's not configured netargs.append('--onboot=no') bOnBoot = False # Store provisioning network interface device name for # later reference in the template # Ensure all interfaces are activated if bActivate: netargs.append('--activate') bDefaultRoute = True if bProvisioningNic: # This is the nic connected to the provisioning network. if len(dbNode.nics) > 1: # Disable the default route on the management network. netargs.append('--nodefroute') bDefaultRoute = False else: # Disable DNS for all interfaces other than the # provisioning network if bOnBoot: netargs.append('--nodns') if nic.network.gateway and bDefaultRoute: netargs.append('--gateway %s' % (nic.network.gateway)) return ' '.join(netargs) def __validate_node(self, node): \ # pylint: disable=no-self-use """ Raises: NodeNotFound NicNotFound """ if not node.name: raise NodeNotFound('Node must have a name') if not node.nics: raise NicNotFound('Node [%s] has no associated nics' % ( node.name)) def __kickstart_get_timezone(self): tz = self._globalParameterDbApi.getParameter( 'Timezone_zone').getValue() # Ensure timezone does not contain any spaces return tz.replace(' ', '_') def __kickstart_get_network_section(self, node, hardwareprofile): # Ensure nics are processed in order (ie. eth0, eth1, eth2...) nics = node.nics nics.sort(key=lambda nic: nic.networkdevice.name) network_entries = [] hostname_set = False # Iterate over nics, adding 'network' Kickstart entries for each for nic in nics: networkString = self.__get_kickstart_network_entry( node, hardwareprofile, nic) if not networkString: continue if not hostname_set and nic.boot and \ nic.network.type == 'provision': networkString += ' --hostname=%s' % (node.name) hostname_set = True network_entries.append(networkString) return '\n'.join(network_entries) def __kickstart_get_repos(self, dbSwProfile, installer_private_ip): repo_entries = [] for dbComponent in dbSwProfile.components: dbKit = dbComponent.kit if dbKit.isOs or dbKit.name != 'base': # Do not add repos for OS kits continue kitVer = '%s-%s' % (dbKit.version, dbKit.iteration) kitArch = 'noarch' subpath = '%s/%s/%s' % (dbKit.name, kitVer, kitArch) # Check if repository actually exists if not os.path.exists(os.path.join(self._cm.getDepotDir(), 'kits', subpath, 'repodata', 'repomd.xml')): # Repository for specified kit is empty. Nothing to do... continue url = self._cm.getYumRootUrl(installer_private_ip) + \ '/' + subpath repo_entries.append( 'repo --name %s --baseurl=%s' % (dbKit.name, url)) subpath = '3rdparty/%s/%s/%s' % (dbSwProfile.os.family.name, dbSwProfile.os.family.version, dbSwProfile.os.arch) if os.path.exists(os.path.join(self._cm.getRoot(), 'repos', subpath, 'repodata/repomd.xml')): # Third-party repository contains packages, include it in # Kickstart url = '%s/%s' % ( self._cm.getYumRootUrl(installer_private_ip), subpath) repo_entries.append( 'repo --name tortuga-third-party --baseurl=%s' % (url)) return repo_entries def __get_kickstart_template(self, swprofile): ksTemplate = os.path.join( self._cm.getKitConfigBase(), 'kickstart-%s.tmpl' % (swprofile.os.family.name.encode('ascii'))) if not os.path.exists(ksTemplate): ksTemplate = os.path.join( self._cm.getKitConfigBase(), 'kickstart-%s.tmpl' % (swprofile.name.encode('ascii'))) if not os.path.exists(ksTemplate): ksTemplate = os.path.join( self._cm.getKitConfigBase(), 'kickstart.tmpl') return ksTemplate def __kickstart_get_partition_section(self, softwareprofile): buf = """\ #!/bin/sh # Determine how many drives we have """ # Temporary workaround for RHEL 5.7 based distros # https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=709880 if softwareprofile.os.version == '5.7': buf += 'set $(PYTHONPATH=/usr/lib/booty list-harddrives)\n' else: buf += 'set $(list-harddrives)\n' buf += """ d1=$1 d2=$3 d3=$5 d4=$7 """ clearpartstr = ''' cat >/tmp/partinfo << __PARTINFO__ zerombr ''' disksToPreserve = [] # Need to get the drives to clear clearpartstr += 'clearpart ' driveNumbers = [] for dbPartition in softwareprofile.partitions: disk = dbPartition.device.split('.')[0] if disk not in driveNumbers: driveNumbers.append(disk) if not dbPartition.preserve: # This is a partition to clear if len(driveNumbers) == 1: # First drive clearpartstr += ('--all --initlabel' ' --drives="${d%s:-nodisk}' % ( disk)) else: clearpartstr += ',${d%s:-nodisk}' % (disk) else: disksToPreserve.append(disk) clearpartstr += "--none" if not driveNumbers else '"' clearpartstr += '\n' for diskNum in driveNumbers: if diskNum in disksToPreserve: continue buf += ''' dd if=/dev/zero of=$d%s bs=512 count=1 ''' % (diskNum) buf += clearpartstr bootloaderLocation = "mbr" # Now create partitions for dbPartition in softwareprofile.partitions: if dbPartition.bootLoader: # Can't control the partition in anaconda...it will be on # the drive with the boot partition bootloaderLocation = 'partition' buf += self._processPartition(dbPartition) # now do the bootloader buf += ( 'bootloader --location=%s --driveorder=${d1:-nodisk}\n' % ( bootloaderLocation)) buf += '__PARTINFO__\n' return buf def __get_template_subst_dict(self, node, hardwareprofile, softwareprofile): hardwareprofile = hardwareprofile \ if hardwareprofile else node.hardwareprofile softwareprofile = softwareprofile \ if softwareprofile else node.softwareprofile installer_public_fqdn = socket.getfqdn() installer_hostname = installer_public_fqdn.split('.')[0] installer_private_ip = hardwareprofile.nics[0].ip try: private_domain = self._globalParameterDbApi.\ getParameter('DNSZone').getValue() except ParameterNotFound: private_domain = None installer_private_fqdn = '%s%s%s' % ( installer_hostname, get_installer_hostname_suffix( hardwareprofile.nics[0], enable_interface_aliases=None), '.%s' % (private_domain) if private_domain else '') vals = node.name.split('.', 1) domain = vals[1].lower() if len(vals) == 2 else '' d = { 'fqdn': node.name, 'domain': domain, 'hostname': installer_hostname, 'installer_private_fqdn': installer_private_fqdn, 'installer_private_domain': private_domain, 'installer_private_ip': installer_private_ip, 'puppet_master_fqdn': installer_public_fqdn, 'installer_public_fqdn': installer_public_fqdn, 'ntpserver': installer_private_ip, 'os': softwareprofile.os.name, 'osfamily': softwareprofile.os.family.name, 'osfamilyvers': int(softwareprofile.os.family.version), # These are deprecated and included for backwards compatibility # only. Do not reference them in any new kickstart templates. 'primaryinstaller': installer_private_fqdn, 'puppetserver': installer_public_fqdn, 'installerip': installer_private_ip, } # Add entry for install package source d['url'] = '%s/%s/%s/%s' % ( self._cm.getYumRootUrl(installer_private_fqdn), softwareprofile.os.name, softwareprofile.os.version, softwareprofile.os.arch) d['lang'] = 'en_US.UTF-8' d['keyboard'] = 'us' d['networkcfg'] = self.__kickstart_get_network_section( node, hardwareprofile) d['rootpw'] = self._generatePassword() d['timezone'] = self.__kickstart_get_timezone() d['includes'] = '%include /tmp/partinfo' d['repos'] = '\n'.join( self.__kickstart_get_repos( softwareprofile, installer_private_fqdn)) # Retain this for backwards compatibility with legacy Kickstart # templates d['packages'] = '\n'.join([]) d['prescript'] = self.__kickstart_get_partition_section( softwareprofile) d['installer_url'] = self._cm.getInstallerUrl(installer_private_fqdn) d['cfmstring'] = self._cm.getCfmPassword() return d def getKickstartFileContents(self, node, hardwareprofile, softwareprofile): # Perform basic sanity checking before proceeding self.__validate_node(node) template_subst_dict = self.__get_template_subst_dict( node, hardwareprofile, softwareprofile) with open(self.__get_kickstart_template(softwareprofile)) as fp: tmpl = fp.read() return Template(tmpl).render(template_subst_dict) def _generatePassword(self): \ # pylint: disable=no-self-use # Generate a random password, used when creating a Kickstart file # for package-based node provisioning. strlength = 8 strchars = string.ascii_letters + string.digits rootpw = ''.join([choice(strchars) for _ in range(strlength)]) rootpw = crypt.crypt(str(rootpw), str(time.time())) return rootpw def __get_partition_mountpoint(self, dbPartition): \ # pylint: disable=no-self-use if not dbPartition.mountPoint: if dbPartition.fsType == 'swap': mountPoint = 'swap' else: # Any partition that does not have a mountpoint defined # is ignored. return None else: mountPoint = dbPartition.mountPoint return mountPoint def _processPartition(self, dbPartition): \ # pylint: disable=no-self-use mountPoint = dbPartition.mountPoint \ if dbPartition.mountPoint else \ self.__get_partition_mountpoint(dbPartition) if not mountPoint: return '' result = '' # All partitions must have a mount point and partition type result = 'part %s --fstype %s' % (mountPoint, dbPartition.fsType) # This will throw an exception if the size stored in the # partition settings is not an integer. if dbPartition.size: result += ' --size=%d' % (dbPartition.size) else: # If partition size is not set or is zero, use '--recommended' flag if mountPoint == 'swap': result += ' --recommended' disk, part = dbPartition.device.split('.') optionsList = dbPartition.options.split(',') \ if dbPartition.options else [] if dbPartition.grow is not None: result += ' --grow' if dbPartition.maxSize is not None: result += ' --maxsize %d' % (dbPartition.maxSize) if optionsList: # Add the fs options... result += ' --fsoptions="%s"' % (','.join(optionsList)) result += ' --noformat --onpart=${d%s:-nodisk}%s' % (disk, part) \ if dbPartition.preserve else \ ' --ondisk=${d%s:-nodisk}' % str(disk) result += '\n' return result
class AuthManager(TortugaObjectManager): def __init__(self, *, session: Session): super(AuthManager, self).__init__() self.session = session self._configManager = ConfigManager() self.__principals = {} self.__loadPrincipals() def cryptPassword(self, cleartext): \ # pylint: disable=no-self-use """ Return crypted password """ return pbkdf2_sha256.hash(cleartext) def reloadPrincipals(self): """ This is used to reload the principals in auth manager """ self.__principals.clear() self.__loadPrincipals() def __loadPrincipals(self): """ Load principals for config manager and datastore """ from tortuga.admin.api import AdminApi # Create built-in cfm principal cfmUser = AuthPrincipal( self._configManager.getCfmUser(), self.cryptPassword(self._configManager.getCfmPassword()), {'roles': 'cfm'}) # Add cfm user self.__principals[cfmUser.get_name()] = cfmUser # Add users from DB if self._configManager.isInstaller(): for admin in AdminApi().getAdminList(self.session): self.__principals[admin.getUsername()] = AuthPrincipal( admin.getUsername(), admin.getPassword(), attributes={'id': admin.getId()}) def get_principal(self, username: str) -> AuthPrincipal: """ Get a principal by username. :param str username: the username of the principal to lookup :return AuthPrincipal: the principal, if found, otherwise None """ principal: AuthPrincipal = self.__principals.get(username) if not principal: principal = None return principal