示例#1
0
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)
示例#2
0
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
示例#3
0
文件: client.py 项目: dtougas/tortuga
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)
示例#4
0
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
示例#5
0
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)
示例#6
0
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()
示例#7
0
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)
示例#8
0
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
示例#9
0
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