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())
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]
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']
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
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()
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
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)
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, }
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)
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 = {}
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)
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]
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')
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.')
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):
def testForceHostIdRaisesExceptionIfInvalid(hostId): with pytest.raises(ValueError): forceHostId(hostId)
def testForceHostId(hostId, expected): assert expected == forceHostId(u'client.test.invalid')
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
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)
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])