コード例 #1
0
def getLocalFQDN():
    'Get the FQDN of the local machine.'
    # Lazy imports to not hinder other tests.
    from OPSI.Types import forceHostId
    from OPSI.Util import getfqdn

    return forceHostId(getfqdn())
コード例 #2
0
ファイル: DHCPD.py プロジェクト: mpice-mn/python-opsi
    def _getDepotConnection(self, depotId):
        depotId = forceHostId(depotId)
        if depotId == self._depotId:
            return self

        try:
            return self._depotConnections[depotId]
        except KeyError:
            if not self._opsiHostKey:
                depots = self._context.host_getObjects(id=self._depotId)  # pylint: disable=maybe-no-member
                if not depots or not depots[0].getOpsiHostKey():
                    raise BackendMissingDataError(
                        u"Failed to get opsi host key for depot '{0}'".format(
                            self._depotId))
                self._opsiHostKey = depots[0].getOpsiHostKey()

            try:
                self._depotConnections[depotId] = JSONRPCBackend(
                    address=u'https://%s:4447/rpc/backend/dhcpd' % (depotId),
                    username=self._depotId,
                    password=self._opsiHostKey)
            except Exception as error:
                raise BackendUnableToConnectError(
                    u"Failed to connect to depot '%s': %s" % (depotId, error))

            return self._depotConnections[depotId]
コード例 #3
0
def testCertificateFileAfterRenewal(tempDir):
    exampleCertificate = getAbsolutePathToTestCert('example.pem')

    certificate_folder = tempDir
    shutil.copy(exampleCertificate, certificate_folder)
    certificate_path = os.path.join(certificate_folder, 'example.pem')
    assert os.path.exists(certificate_path)

    old_config = loadConfigurationFromCertificate(certificate_path)

    configForCreating = old_config
    configForCreating['commonName'] = forceHostId(getfqdn())
    renewCertificate(path=certificate_path, config=configForCreating)

    assert os.path.exists(certificate_path)
    backupFile = '{file}.bak'.format(file=certificate_path)
    assert os.path.exists(backupFile), u"Missing backup-file!"

    new_config = loadConfigurationFromCertificate(certificate_path)

    keysToCompare = ('organizationalUnit', 'commonName', 'country', 'state',
                     'locality', 'organization', 'emailAddress')

    for key in keysToCompare:
        assert old_config[key] == new_config[key], (
            u"Difference at key {0!r} between old and new: {1!r} vs. {2!r}".
            format(key, old_config[key], new_config[key]))

    assert old_config['serialNumber'] != new_config['serialNumber']
コード例 #4
0
ファイル: HostControl.py プロジェクト: mpice-mn/python-opsi
 def __init__(self,
              hostControlBackend,
              hostId,
              address,
              username,
              password,
              method,
              params=[],
              hostPort=0):
     KillableThread.__init__(self)
     self.hostControlBackend = hostControlBackend
     self.hostId = forceHostId(hostId)
     self.address = forceIpAddress(address)
     self.username = forceUnicode(username)
     self.password = forceUnicode(password)
     self.method = forceUnicode(method)
     self.params = forceList(params)
     self.error = None
     self.result = None
     self.started = 0
     self.ended = 0
     if hostPort:
         self.hostPort = forceInt(hostPort)
     else:
         self.hostPort = self.hostControlBackend._opsiclientdPort
コード例 #5
0
    def opsipxeconfd_updatePXEBootConfiguration(self, clientId, data=None):
        """
		Update the boot configuration of a specific client.
		This method will relay calls to opsipxeconfd who does the handling.

		:param clientId: The client whose boot configuration should be updated.
		:type clientId: str
		:param data: Collected data for opsipxeconfd.
		:type data: dict
		"""
        clientId = forceHostId(clientId)
        logger.debug("Updating PXE boot config of {!r}", clientId)

        command = 'update {}'.format(clientId)
        if data:
            cacheFilePath = self._cacheOpsiPXEConfdData(clientId, data)
            if cacheFilePath:
                command = 'update {} {}'.format(clientId, quote(cacheFilePath))

        with self._updateThreadsLock:
            if clientId not in self._updateThreads:
                updater = UpdateThread(self, clientId, command)
                self._updateThreads[clientId] = updater
                updater.start()
            else:
                self._updateThreads[clientId].delay()
コード例 #6
0
ファイル: HostControl.py プロジェクト: mpice-mn/python-opsi
 def __init__(self, hostControlBackend, hostId, address):
     KillableThread.__init__(self)
     self.hostControlBackend = hostControlBackend
     self.hostId = forceHostId(hostId)
     self.address = forceIpAddress(address)
     self.result = False
     self.started = 0
     self.ended = 0
コード例 #7
0
    def __init__(self, **kwargs):
        ConfigDataBackend.__init__(self, **kwargs)

        self._name = 'opsipxeconfd'
        self._port = u'/var/run/opsipxeconfd/opsipxeconfd.socket'
        self._timeout = 10
        self._depotId = forceHostId(getfqdn())
        self._opsiHostKey = None
        self._depotConnections = {}
        self._updateThreads = {}
        self._updateThreadsLock = threading.Lock()
        self._parseArguments(kwargs)
コード例 #8
0
def customConfig():
    hostname = forceHostId(getfqdn())
    yield {
        'organizationalUnit': u'asdf',
        'expires': 3,
        'commonName': hostname,
        'country': u'ZZ',  # Top
        'state': u'HE',
        'locality': u'Breidenbach',
        'organization': u'Unittest',
        'emailAddress': u'*****@*****.**',
        'serialNumber': 1010,
    }
コード例 #9
0
    def __init__(self, backend, **kwargs):
        self._name = 'depotserver'

        ExtendedBackend.__init__(self, backend)

        self._packageLog = os.path.join(LOG_DIR, 'package.log')
        self._sshRSAPublicKeyFile = u'/etc/ssh/ssh_host_rsa_key.pub'

        self._depotId = forceHostId(getfqdn())
        if not self._context.host_getIdents(id=self._depotId):  # pylint: disable=maybe-no-member
            raise BackendMissingDataError(u"Depot '%s' not found in backend" %
                                          self._depotId)
        self._packageManager = DepotserverPackageManager(self)
コード例 #10
0
ファイル: DHCPD.py プロジェクト: mpice-mn/python-opsi
    def __init__(self, **kwargs):
        self._name = 'dhcpd'

        ConfigDataBackend.__init__(self, **kwargs)

        self._dhcpdConfigFile = System.Posix.locateDHCPDConfig(
            u'/etc/dhcp3/dhcpd.conf')
        self._reloadConfigCommand = '/usr/bin/sudo {command}'.format(
            command=System.Posix.getDHCPDRestartCommand(
                default='/etc/init.d/dhcp3-server restart'))

        self._fixedAddressFormat = u'IP'
        self._defaultClientParameters = {
            'next-server': socket.gethostbyname(getfqdn()),
            'filename': u'linux/pxelinux.0'
        }
        self._dhcpdOnDepot = False

        # Parse arguments
        for (option, value) in kwargs.items():
            option = option.lower()
            if option == 'dhcpdconfigfile':
                self._dhcpdConfigFile = value
            elif option == 'reloadconfigcommand':
                self._reloadConfigCommand = value
            elif option == 'defaultclientparameters':
                self._defaultClientParameters = forceDict(value)
            elif option == 'fixedaddressformat':
                if value not in (u'IP', u'FQDN'):
                    raise BackendBadValueError(
                        u"Bad value '%s' for fixedAddressFormat, possible values are %s"
                        % (value, u', '.join(('IP', 'FQDN'))))
                self._fixedAddressFormat = value
            elif option == 'dhcpdondepot':
                self._dhcpdOnDepot = forceBool(value)

        if self._defaultClientParameters.get(
                'next-server'
        ) and self._defaultClientParameters['next-server'].startswith(u'127'):
            raise BackendBadValueError(
                u"Refusing to use ip address '%s' as default next-server" %
                self._defaultClientParameters['next-server'])

        self._dhcpdConfFile = DHCPDConfFile(self._dhcpdConfigFile)
        self._reloadEvent = threading.Event()
        self._reloadEvent.set()
        self._reloadLock = threading.Lock()
        self._reloadThread = None
        self._depotId = forceHostId(getfqdn())
        self._opsiHostKey = None
        self._depotConnections = {}
コード例 #11
0
ファイル: Replicator.py プロジェクト: mpice-mn/python-opsi
    def __init__(self,
                 readBackend,
                 writeBackend,
                 newServerId=None,
                 oldServerId=None,
                 cleanupFirst=True):
        self.__readBackend = readBackend
        self.__writeBackend = writeBackend

        self._extendedReadBackend = ExtendedConfigDataBackend(
            self.__readBackend)
        self._extendedWriteBackend = ExtendedConfigDataBackend(
            self.__writeBackend)

        self.__newServerId = None
        self.__oldServerId = None

        self.__cleanupFirst = forceBool(cleanupFirst)
        self.__strict = False
        self.__serverIds = []
        self.__depotIds = []
        self.__clientIds = []
        self.__groupIds = []
        self.__productIds = []

        if newServerId:
            self.__newServerId = forceHostId(newServerId)
        if oldServerId:
            self.__oldServerId = forceHostId(oldServerId)

        self.__overallProgressSubject = ProgressSubject(
            id=u'overall_replication',
            title=u'Replicating',
            end=100,
            fireAlways=True)
        self.__currentProgressSubject = ProgressSubject(
            id=u'current_replication', fireAlways=True)
コード例 #12
0
    def _getDepotConnection(self, depotId):
        depotId = forceHostId(depotId)
        if depotId == self._depotId:
            return self

        try:
            return self._depotConnections[depotId]
        except KeyError:
            if not self._opsiHostKey:
                depots = self._context.host_getObjects(id=self._depotId)  # pylint: disable=maybe-no-member
                if not depots or not depots[0].getOpsiHostKey():
                    raise BackendMissingDataError(
                        u"Failed to get opsi host key for depot '%s'" %
                        self._depotId)
                self._opsiHostKey = depots[0].getOpsiHostKey()

            self._depotConnections[
                depotId] = self._getExternalBackendConnection(
                    depotId, self._depotId, self._opsiHostKey)
            return self._depotConnections[depotId]
コード例 #13
0
ファイル: test_acl.py プロジェクト: mpice-mn/python-opsi
def testDenyingAccessToOtherObjects(extendedConfigDataBackend):
    """
    It must be possible to deny access to foreign objects.

    In this test we first make sure that the access to productOnClient_create
    is possible for the object accessing the backend.
    After that we test the same referencing another object which we
    want to fail.
    """
    backend = extendedConfigDataBackend

    serverFqdn = forceHostId(getfqdn())  # using local FQDN
    depotserver1 = {
        "isMasterDepot": True,
        "type": "OpsiConfigserver",
        "id": serverFqdn,
    }

    backend.host_createObjects(depotserver1)

    clients = getClients()
    backend.host_createObjects(clients)
    client1 = clients[0]
    client2 = clients[1]

    products = getProducts()
    backend.product_createObjects(products)

    product1 = products[0]

    backend.config_createObjects([{
        "id": u'clientconfig.depot.id',
        "type": "UnicodeConfig",
    }])
    backend.configState_create(u'clientconfig.depot.id',
                               client1.getId(),
                               values=[depotserver1['id']])

    productOnDepot1 = OPSI.Object.ProductOnDepot(
        productId=product1.getId(),
        productType=product1.getType(),
        productVersion=product1.getProductVersion(),
        packageVersion=product1.getPackageVersion(),
        depotId=depotserver1['id'],
        locked=False)

    backend.productOnDepot_createObjects([productOnDepot1])

    backendAccessControl = BackendAccessControl(
        username=client1.id,
        password=client1.opsiHostKey,
        backend=backend,
        acl=[
            [
                'productOnClient_create',
                [{
                    'type': u'self',
                    'ids': [],
                    'denyAttributes': [],
                    'allowAttributes': []
                }]
            ],
        ])

    backendAccessControl.productOnClient_create(productId=product1.id,
                                                productType=product1.getType(),
                                                clientId=client1.id,
                                                installationStatus='installed')

    with pytest.raises(Exception):
        backendAccessControl.productOnClient_create(
            productId=product1.id,
            productType=product1.getType(),
            clientId=client2.id,  # here is the difference
            installationStatus='installed')
コード例 #14
0
ファイル: Certificate.py プロジェクト: mpice-mn/python-opsi
def createCertificate(path=None, config=None):
	"""
	Creates a certificate.

	Will overwrite any certificate that may exists in ``path``.

	.. versionchanged:: 4.0.6.2

		Incrementing previously set serial number on re-creation.
		For new certificates a random number will be generated.


	:param path: The path of the certificate. \
If this is `None` the default will be used.
	:type path: str
	:param config: The configuration of the certificate. \
If not given will use a default.
	:type config: dict
	:raises CertificateCreationError: If errors exist in configuration.
	"""
	try:
		which("ucr")
		LOGGER.notice(u"Don't use certificate creation method on UCS-Systems")
		return
	except Exception:
		pass

	if path is None:
		path = OPSICONFD_CERTFILE

	if config is None:
		certparams = DEFAULT_CERTIFICATE_PARAMETERS
	else:
		certparams = config

	try:
		certparams["expires"] = forceInt(certparams["expires"])
	except Exception:
		raise CertificateCreationError(
			u"No valid expiration date given. Must be an integer."
		)

	if certparams["commonName"] != forceHostId(getfqdn()):
		raise CertificateCreationError(
			u"commonName must be the FQDN of the local server"
		)

	LOGGER.notice(u"Creating new opsiconfd cert")
	LOGGER.notice(u"Generating new key pair")
	k = crypto.PKey()
	k.generate_key(crypto.TYPE_RSA, 2048)

	LOGGER.notice(u"Generating new self-signed cert")
	cert = crypto.X509()
	cert.get_subject().C = certparams['country']
	cert.get_subject().ST = certparams['state']
	cert.get_subject().L = certparams['locality']
	cert.get_subject().O = certparams['organization']
	cert.get_subject().CN = certparams['commonName']

	try:
		if certparams['organizationalUnit']:
			cert.get_subject().OU = certparams['organizationalUnit']
		else:
			del certparams['organizationalUnit']
	except KeyError:
		pass

	try:
		if certparams['emailAddress']:
			cert.get_subject().emailAddress = certparams['emailAddress']
		else:
			del certparams['emailAddress']
	except KeyError:
		pass

	LOGGER.notice("Generating new Serialnumber")
	# As described in RFC5280 this value is required and must be a
	# positive and unique integer.
	# Source: http://tools.ietf.org/html/rfc5280#page-19
	#
	# We currently do not have the ability to make the serial unique
	# but we assume that this is called only once in 2-3 years.
	# If we have an old serial number present we increment it by 1.
	# If we do not have an old serial number we create a random one.
	try:
		serialNumber = int(certparams['serialNumber']) + 1
	except (KeyError, ValueError):
		LOGGER.debug(u"Reading in the existing serial number failed.")
		LOGGER.info(u"Creating new random serial number.")
		serialNumber = random.randint(0, pow(2, 16))
	cert.set_serial_number(serialNumber)

	LOGGER.notice(
		u"Setting new expiration date (%d years)" % certparams["expires"]
	)
	cert.gmtime_adj_notBefore(0)
	cert.gmtime_adj_notAfter(certparams["expires"] * 365 * 24 * 60 * 60)

	LOGGER.notice(u"Filling certificate with new data")
	cert.set_issuer(cert.get_subject())
	cert.set_pubkey(k)
	cert.set_version(2)

	LOGGER.notice(u"Signing Certificate")
	cert.sign(k, str('sha512'))

	certcontext = "".join(
		(
			crypto.dump_certificate(crypto.FILETYPE_PEM, cert),
			crypto.dump_privatekey(crypto.FILETYPE_PEM, k)
		)
	)

	LOGGER.notice(u"Beginning to write certificate.")
	with open(path, "wt") as certfile:
		certfile.write(certcontext)

	with NamedTemporaryFile(mode="wt") as randfile:
		LOGGER.notice(u"Generating and filling new randomize string")
		randomBytes = os.urandom(512)
		randfile.write(randomBytes)

		execute(
			u"{command} dhparam -rand {tempfile} 512 >> {target}".format(
				command=which("openssl"), tempfile=randfile.name, target=path
			)
		)

	LOGGER.notice(u'Certificate creation done.')
コード例 #15
0
ファイル: Certificate.py プロジェクト: mpice-mn/python-opsi
from OpenSSL import crypto

from OPSI.Logger import Logger
from OPSI.System import which, execute
from OPSI.Types import forceHostId, forceInt
from OPSI.Util import getfqdn

OPSICONFD_CERTFILE = u'/etc/opsi/opsiconfd.pem'
DEFAULT_CERTIFICATE_PARAMETERS = {
	"country": "DE",
	"state": "RP",
	"locality": "Mainz",
	"organization": "uib gmbh",
	"organizationalUnit": "",
	"commonName": forceHostId(getfqdn()),
	"emailAddress": "",
	"expires": 2,
}
LOGGER = Logger()


class NoCertificateError(Exception):
	pass


class CertificateCreationError(Exception):
	pass


class UnreadableCertificateError(Exception):
コード例 #16
0
def testForceHostIdRaisesExceptionIfInvalid(hostId):
    with pytest.raises(ValueError):
        forceHostId(hostId)
コード例 #17
0
def testForceHostId(hostId, expected):
    assert expected == forceHostId(u'client.test.invalid')
コード例 #18
0
    def _getRepository(self,
                       config,
                       section,
                       forceRepositoryActivation=False,
                       repositoryName=None,
                       installAllAvailable=False,
                       proxy=None):
        active = False
        baseUrl = None
        opsiDepotId = None
        for (option, value) in config.items(section):
            option = option.lower()
            value = value.strip()
            if option == 'active':
                active = forceBool(value)
            elif option == 'baseurl':
                if value:
                    baseUrl = forceUrl(value)
            elif option == 'opsidepotid':
                if value:
                    opsiDepotId = forceHostId(value)
            elif option == 'proxy':
                if value:
                    proxy = forceUrl(value)

        repoName = section.replace('repository_', '', 1)

        if forceRepositoryActivation:
            if repoName == repositoryName:
                logger.debug("Activation for repository {0} forced.", repoName)
                active = True
            else:
                active = False

        repository = None
        if opsiDepotId:
            if not self.backend:
                raise RequiringBackendError(
                    "Repository section '{0}' supplied an depot ID but we have no backend to check."
                    .format(section))

            depots = self.backend.host_getObjects(type='OpsiDepotserver',
                                                  id=opsiDepotId)
            if not depots:
                raise ConfigurationError(u"Depot '%s' not found in backend" %
                                         opsiDepotId)
            if not depots[0].repositoryRemoteUrl:
                raise ConfigurationError(
                    u"Repository remote url for depot '%s' not found in backend"
                    % opsiDepotId)

            repository = ProductRepositoryInfo(
                name=repoName,
                baseUrl=depots[0].repositoryRemoteUrl,
                dirs=['/'],
                username=self.depotId,
                password=self.depotKey,
                opsiDepotId=opsiDepotId,
                active=active)

        elif baseUrl:
            if proxy:
                logger.info(u"Repository {} is using proxy {}", repoName,
                            proxy)

            repository = ProductRepositoryInfo(name=repoName,
                                               baseUrl=baseUrl,
                                               proxy=proxy,
                                               active=active)
        else:
            raise MissingConfigurationValueError(
                u"Repository section '{0}': neither baseUrl nor opsiDepotId set"
                .format(section))

        for (option, value) in config.items(section):
            if option.lower() == 'username':
                repository.username = forceUnicode(value.strip())
            elif option.lower() == 'password':
                repository.password = forceUnicode(value.strip())
                if repository.password:
                    logger.addConfidentialString(repository.password)
            elif option.lower() == 'authcertfile':
                repository.authcertfile = forceFilename(value.strip())
            elif option.lower() == 'authkeyfile':
                repository.authkeyfile = forceFilename(value.strip())
            elif option.lower() == 'autoinstall':
                repository.autoInstall = forceBool(value.strip())
            elif option.lower() == 'autoupdate':
                repository.autoUpdate = forceBool(value.strip())
            elif option.lower() == 'autosetup':
                repository.autoSetup = forceBool(value.strip())
            elif option.lower() == 'onlydownload':
                repository.onlyDownload = forceBool(value.strip())
            elif option.lower() == 'inheritproductproperties':
                if not opsiDepotId:
                    logger.warning(
                        u"InheritProductProperties not possible with normal http ressource."
                    )
                    repository.inheritProductProperties = False
                else:
                    repository.inheritProductProperties = forceBool(
                        value.strip())
            elif option.lower() == 'dirs':
                repository.dirs = []
                for directory in splitAndStrip(value, ','):
                    repository.dirs.append(forceFilename(directory))
            elif option.lower() == 'excludes':
                repository.excludes = []
                for exclude in splitAndStrip(value, ','):
                    repository.excludes.append(re.compile(exclude))
            elif option.lower() == 'includeproductids':
                repository.includes = []
                for include in splitAndStrip(value, ','):
                    repository.includes.append(re.compile(include))
            elif option.lower() == 'description':
                repository.description = forceUnicode(value)

        if installAllAvailable:
            repository.autoInstall = True
            repository.autoUpdate = True
            repository.excludes = []

        return repository
コード例 #19
0
    def selectDepotserver(self,
                          configService,
                          mode="mount",
                          event=None,
                          productIds=[],
                          masterOnly=False):  # pylint: disable=dangerous-default-value,too-many-arguments,too-many-locals,too-many-branches,too-many-statements,redefined-builtin
        assert mode in ("mount", "sync")
        productIds = forceProductIdList(productIds)

        logger.notice("Selecting depot for products %s", productIds)
        logger.notice("MasterOnly --> '%s'", masterOnly)

        if event and event.eventConfig.useCachedProducts:
            cacheDepotDir = os.path.join(
                self.get('cache_service', 'storage_dir'),
                'depot').replace('\\', '/').replace('//', '/')
            logger.notice("Using depot cache: %s", cacheDepotDir)
            self.set_temporary_depot_path(cacheDepotDir)
            if RUNNING_ON_WINDOWS:
                self.setTemporaryDepotDrive(cacheDepotDir.split(':')[0] + ':')
            else:
                self.setTemporaryDepotDrive(cacheDepotDir)
            self.set(
                'depot_server', 'url', 'smb://localhost/noshare/' +
                ('/'.join(cacheDepotDir.split('/')[1:])))
            return

        if not configService:
            raise Exception("Not connected to config service")

        selectedDepot = None

        configService.backend_setOptions({"addConfigStateDefaults": True})

        depotIds = []
        configStates = []
        dynamicDepot = False
        depotProtocol = 'cifs'
        configStates = configService.configState_getObjects(configId=[
            'clientconfig.depot.dynamic', 'clientconfig.depot.protocol',
            'opsiclientd.depot_server.depot_id', 'opsiclientd.depot_server.url'
        ],
                                                            objectId=self.get(
                                                                'global',
                                                                'host_id'))
        for configState in configStates:
            if not configState.values or not configState.values[0]:
                continue

            if configState.configId == 'opsiclientd.depot_server.url' and configState.values:
                try:
                    depotUrl = forceUrl(configState.values[0])
                    self.set('depot_server', 'depot_id', '')
                    self.set('depot_server', 'url', depotUrl)
                    logger.notice(
                        "Depot url was set to '%s' from configState %s",
                        depotUrl, configState)
                    return
                except Exception as err:  # pylint: disable=broad-except
                    logger.error(
                        "Failed to set depot url from values %s in configState %s: %s",
                        configState.values, configState, err)
            elif configState.configId == 'opsiclientd.depot_server.depot_id' and configState.values:
                try:
                    depotId = forceHostId(configState.values[0])
                    depotIds.append(depotId)
                    logger.notice("Depot was set to '%s' from configState %s",
                                  depotId, configState)
                except Exception as err:  # pylint: disable=broad-except
                    logger.error(
                        "Failed to set depot id from values %s in configState %s: %s",
                        configState.values, configState, err)
            elif not masterOnly and (
                    configState.configId
                    == 'clientconfig.depot.dynamic') and configState.values:
                dynamicDepot = forceBool(configState.values[0])

            elif configState.configId == 'clientconfig.depot.protocol' and configState.values:
                depotProtocol = configState.values[0]
                logger.info("Using depot protocol '%s' from config state '%s'",
                            depotProtocol, configState.configId)

        if event and event.eventConfig.depotProtocol:
            logger.info("Using depot protocol '%s' from event '%s'",
                        event.eventConfig.depotProtocol,
                        event.eventConfig.getName())
            depotProtocol = event.eventConfig.depotProtocol

        if depotProtocol not in ("webdav", "cifs"):
            logger.error("Invalid protocol %s specified, using cifs",
                         depotProtocol)
            depotProtocol = "cifs"

        #if depotProtocol == "webdav" and mode == "mount" and not RUNNING_ON_LINUX and not self.get('global', 'install_opsi_ca_into_os_store'):
        #	logger.error("Using cifs instead of webdav to mount depot share because global.install_opsi_ca_into_os_store is disabled")
        #	depotProtocol = "cifs"

        if dynamicDepot:
            if not depotIds:
                logger.info("Dynamic depot selection enabled")
            else:
                logger.info(
                    "Dynamic depot selection enabled, but depot is already selected"
                )
        else:
            logger.info("Dynamic depot selection disabled")

        if not depotIds:
            clientToDepotservers = configService.configState_getClientToDepotserver(
                clientIds=[self.get('global', 'host_id')],
                masterOnly=bool(not dynamicDepot),
                productIds=productIds)
            if not clientToDepotservers:
                raise Exception("Failed to get depot config from service")

            depotIds = [clientToDepotservers[0]['depotId']]
            if dynamicDepot:
                depotIds.extend(clientToDepotservers[0].get(
                    'alternativeDepotIds', []))

        logger.debug("Fetching depot servers %s from config service", depotIds)
        masterDepot = None
        alternativeDepots = []
        for depot in configService.host_getObjects(type='OpsiDepotserver',
                                                   id=depotIds):
            logger.trace("Depot: %s", depot)
            if depot.id == depotIds[0]:
                masterDepot = depot
            else:
                alternativeDepots.append(depot)

        if not masterDepot:
            raise Exception(
                f"Failed to get info for master depot '{depotIds[0]}'")

        logger.info("Master depot for products %s is %s", productIds,
                    masterDepot.id)
        selectedDepot = masterDepot
        if dynamicDepot:
            if alternativeDepots:
                logger.info("Got alternative depots for products: %s",
                            productIds)
                for index, depot in enumerate(alternativeDepots, start=1):
                    logger.info("%d. alternative depot is %s", index, depot.id)

                try:
                    clientConfig = {
                        "clientId": self.get('global', 'host_id'),
                        "opsiHostKey": self.get('global', 'opsi_host_key'),
                        "ipAddress": None,
                        "netmask": None,
                        "defaultGateway": None
                    }
                    try:
                        gateways = netifaces.gateways()  # pylint: disable=c-extension-no-member
                        clientConfig["defaultGateway"], iface_name = gateways[
                            'default'][netifaces.AF_INET]  # pylint: disable=c-extension-no-member
                        addr = netifaces.ifaddresses(iface_name)[
                            netifaces.AF_INET][0]  # pylint: disable=c-extension-no-member
                        clientConfig["netmask"] = addr["netmask"]
                        clientConfig["ipAddress"] = addr["addr"]
                    except Exception as gwe:
                        raise RuntimeError(
                            f"Failed to get network interface with default gateway: {gwe}"
                        ) from gwe

                    logger.info(
                        "Passing client configuration to depot selection algorithm: %s",
                        clientConfig)

                    depotSelectionAlgorithm = configService.getDepotSelectionAlgorithm(
                    )
                    logger.trace("depotSelectionAlgorithm:\n%s",
                                 depotSelectionAlgorithm)

                    currentLocals = locals()
                    exec(depotSelectionAlgorithm, None, currentLocals)  # pylint: disable=exec-used
                    selectDepot = currentLocals['selectDepot']

                    selectedDepot = selectDepot(
                        clientConfig=clientConfig,
                        masterDepot=masterDepot,
                        alternativeDepots=alternativeDepots)
                    if not selectedDepot:
                        selectedDepot = masterDepot
                except Exception as err:  # pylint: disable=broad-except
                    logger.error("Failed to select depot: %s",
                                 err,
                                 exc_info=True)
            else:
                logger.info("No alternative depot for products: %s",
                            productIds)

        logger.notice("Selected depot for mode '%s' is '%s', protocol '%s'",
                      mode, selectedDepot, depotProtocol)
        self.set('depot_server', 'depot_id', selectedDepot.id)
        if depotProtocol == 'webdav':
            self.set('depot_server', 'url', selectedDepot.depotWebdavUrl)
        else:
            self.set('depot_server', 'url', selectedDepot.depotRemoteUrl)
コード例 #20
0
    def set(self, section, option, value):  # pylint: disable=too-many-branches,too-many-statements
        if not section:
            section = 'global'

        section = str(section).strip().lower()
        if section == 'system':
            return

        option = str(option).strip().lower()
        if isinstance(value, str):
            value = value.strip()

        # Rename legacy options
        if option == 'warning_time':
            option = 'action_warning_time'
        elif option == 'user_cancelable':
            option = 'action_user_cancelable'
        elif option == 'w10bitlockersuspendonreboot':
            option = 'suspend_bitlocker_on_reboot'

        # Check if empty value is allowed
        if (  # pylint: disable=too-many-boolean-expressions
                value == '' and 'command' not in option
                and 'productids' not in option
                and 'exclude_product_group_ids' not in option
                and 'include_product_group_ids' not in option
                and 'proxy_url' not in option
                and 'working_window' not in option):
            if section == 'action_processor' and option == 'remote_common_dir':
                return
            logger.warning("Refusing to set empty value config %s.%s", section,
                           option)
            return

        if section == 'depot_server' and option == 'drive':
            if (RUNNING_ON_LINUX
                    or RUNNING_ON_DARWIN) and not value.startswith("/"):
                logger.warning("Refusing to set %s.%s to '%s' on posix",
                               section, option, value)
                return

        # Preprocess values, convert to correct type
        if option in ('exclude_product_group_ids',
                      'include_product_group_ids'):
            if not isinstance(value, list):
                value = [x.strip() for x in value.split(",") if x.strip()]
            value = forceList(value)

        if RUNNING_ON_WINDOWS and (option.endswith("_dir")
                                   or option.endswith("_file")):
            if ":" in value and ":\\" not in value:
                logger.warning("Correcting path '%s' to '%s'", value,
                               value.replace(":", ":\\"))
                value = value.replace(":", ":\\")

        if option.endswith("_dir") or option.endswith("_file"):
            arch = '64' if '64' in platform.architecture()[0] else '32'
            value = value.replace('%arch%', arch)

        if section.startswith("event_") or section.startswith("precondition_"):
            if option.endswith('_warning_time') or option.endswith(
                    '_user_cancelable'):
                try:
                    value = int(value)
                except ValueError:
                    value = 0
            elif option in ('active', ):
                value = forceBool(value)

        elif section in self._config and option in self._config[section]:
            if section == 'config_service' and option == 'url':
                urls = value
                if not isinstance(urls, list):
                    urls = str(urls).split(',')

                value = []
                for url in urls:
                    url = url.strip()
                    if not re.search('https?://[^/]+', url):
                        logger.error("Bad config service url '%s'", url)
                    if not url in value:
                        value.append(url)
            else:
                try:
                    if isinstance(self._config[section][option], bool):
                        value = forceBool(value)
                    elif self._config[section][option] is not None:
                        _type = type(self._config[section][option])
                        value = _type(value)
                except ValueError as err:
                    logger.error(
                        "Failed to set value '%s' for config %s.%s: %s", value,
                        section, option, err)
                    return

                # Check / correct value
                if option in ('connection_timeout',
                              'user_cancelable_after') and value < 0:
                    value = 0
                elif option == 'opsi_host_key':
                    if len(value) != 32:
                        raise ValueError("Bad opsi host key, length != 32")
                    secret_filter.add_secrets(value)
                elif option in ('depot_id', 'host_id'):
                    value = forceHostId(value.replace('_', '-'))

        else:
            logger.warning(
                "Refusing to set value '%s' for invalid config %s.%s", value,
                section, option)
            return

        logger.info("Setting config %s.%s to %r", section, option, value)

        if section not in self._config:
            self._config[section] = {}
        self._config[section][option] = value

        if section == 'global' and option == 'log_level':
            logging_config(file_level=self._config[section][option])
        elif section == 'global' and option == 'server_cert_dir':
            if not os.path.exists(self._config[section][option]):
                os.makedirs(self._config[section][option])