def testGettingBackendManagerWithCustomConfig(tempDir): backendsDir = os.path.join(tempDir, 'backendsss') bmDir = os.path.join(tempDir, 'bm') dispatchConfig = os.path.join(bmDir, 'dispatch.conf') extensionDir = os.path.join(bmDir, 'extension') os.mkdir(bmDir) os.mkdir(extensionDir) os.mkdir(backendsDir) with open(dispatchConfig, 'w') as dpconf: dpconf.write(""" .* : file """) kwargs = { "dispatchConfigFile": dispatchConfig, "backendConfigDir": backendsDir, "extensionConfigDir": extensionDir, } with getFileBackend(path=tempDir): # We need to make sure there is a file.conf for the backend. os.link(os.path.join(tempDir, 'etc', 'opsi', 'backends', 'file.conf'), os.path.join(backendsDir, 'file.conf')) backend = BackendManager(**kwargs) assert backend.backend_info()
def testGettingBackendManagerWithDefaultConfig(): requiredPaths = ( u'/etc/opsi/backendManager/dispatch.conf', u'/etc/opsi/backends', u'/etc/opsi/backendManager/extend.d', u'/var/lib/opsi/config/depots', ) for path in requiredPaths: if not os.path.exists(path): pytest.skip("Missing {0}".format(path)) backend = BackendManager() assert backend.backend_info()
def backendManager(_serverBackend, tempDir): """ Returns an `OPSI.Backend.BackendManager.BackendManager` for testing. The returned instance is set up to have access to backend extensions. """ defaultConfigDir = _getOriginalBackendLocation() shutil.copytree(defaultConfigDir, os.path.join(tempDir, 'etc', 'opsi')) yield BackendManager(backend=_serverBackend, extensionconfigdir=os.path.join( tempDir, 'etc', 'opsi', 'backendManager', 'extend.d'))
def getLocalDepot(): """ Get the local depot from the backend. :raises BackendConfigurationError: If no backend initialisation is possible. :raises BackendMissingDataError: If no depot is found. :return: The local depot. :rtype: OpsiDepotserver """ from OPSI.Backend.BackendManager import BackendManager try: with BackendManager() as backend: depot = backend.host_getObjects(type='OpsiDepotserver', id=getLocalFQDN()) return depot[0] except IndexError: raise BackendMissingDataError("No depots found!") except Exception as err: LOGGER.warning("Has the backend been initialized?") raise BackendConfigurationError( "Unable to instantiate a backend: {}".format(err))
def testBackendManagerDispatchesCallsToExtensionClass(): """ Make sure that calls are dispatched to the extension class. These calls should not fail. """ class TestClass(object): def methodOnBackend(self, y): assert y == 'yyyyyyyy' def checkIfOptionsExist(self): options = self.backend_getOptions() assert options cdb = ConfigDataBackend() bm = BackendManager(backend=cdb, extensionClass=TestClass) bm.methodOnBackend('yyyyyyyy') bm.checkIfOptionsExist()
#!/bin/python # some functions grabbed from http://svn.opsi.org/opsi4.1/misc/time-driven-maintenance-tools/wake_clients_for_setup.py from __future__ import print_function from datetime import datetime import os import sys import pprint from OPSI.Logger import Logger, LOG_WARNING from OPSI.Util.Ping import ping import time logger = Logger() from OPSI.Backend.BackendManager import BackendManager backending = BackendManager() def getClientIDsFromGroup(backend, groupName): group = backend.group_getObjects(id=groupName, type="HostGroup") try: group = group[0] except IndexError: raise ValueError("No HostGroup with id '{0}' found!".format(groupName)) return [ mapping.objectId for mapping in backend.objectToGroup_getObjects(groupId=group.id) ] def wakeClient(backend, clientId): logger.info("Waking {}", clientId) backend.hostControlSafe_start(clientId)
def editConfigDefaults(): bmconfig = dict( dispatchConfigFile=u'/etc/opsi/backendManager/dispatch.conf', backendConfigDir=u'/etc/opsi/backends', extensionConfigDir=u'/etc/opsi/backendManager/extend.d', depotbackend=False) with BackendManager(**bmconfig) as backend: configs = backend.config_getObjects() configs = [ config for config in configs if not config.id.startswith(u'configed.saved_search.') ] if not configs: raise BackendMissingDataError("Backend misses configurations!") maxConfigIdLen = max(len(config.id) for config in configs) entryFormat = u"%-10s %-" + str(maxConfigIdLen) + "s = %s" with disableConsoleLogging(), _getUI() as ui: while True: entries = [] for config in configs: configType = '[unicode]' if config.getType() == 'BoolConfig': configType = '[bool]' values = u', '.join(forceUnicodeList(config.defaultValues)) values = shortenStr(values, 60) entries.append({ "id": config.id, "name": entryFormat % (configType, config.id, values) }) selection = ui.getSelection( entries, radio=True, width=100, height=10, title=u'Please select config value to change', okLabel='Change', cancelLabel='Quit') if not selection: return configId = None for entry in entries: if selection[0] == entry['name']: configId = entry['id'] break selectedConfig = -1 for index, config in enumerate(configs): if config.id == configId: selectedConfig = index break addNewValue = False cancelLabel = u'Back' title = u'Edit default values for: %s' % configs[ selectedConfig].id text = configs[selectedConfig].description or u'' if configs[selectedConfig].possibleValues: entries = [] for possibleValue in configs[ selectedConfig].possibleValues: entries.append({ 'name': possibleValue, 'value': possibleValue, 'selected': possibleValue in configs[selectedConfig].defaultValues }) radio = not configs[selectedConfig].multiValue if configs[selectedConfig].editable: entries.append({ 'name': '<other value>', 'value': '<other value>', 'selected': False }) selection = ui.getSelection(entries, radio=radio, width=65, height=10, title=title, text=text, cancelLabel=cancelLabel) if selection is None: continue if "<other value>" in selection: addNewValue = True else: configs[selectedConfig].setDefaultValues(selection) else: addNewValue = True if addNewValue: default = u'' if configs[selectedConfig].defaultValues: default = configs[selectedConfig].defaultValues[0] value = ui.getValue(width=65, height=13, title=title, default=default, password=False, text=text, cancelLabel=cancelLabel) if value is None: continue possibleValues = configs[selectedConfig].getPossibleValues( ) if value not in possibleValues: possibleValues.append(value) configs[selectedConfig].setPossibleValues( possibleValues) configs[selectedConfig].setDefaultValues(value) backend.config_updateObjects([configs[selectedConfig]])
def cleanupBackend(backend=None): """ Clean up data from your backends. This method uses different cleanup methods to ensure that no obsolete data is present in your backend. :param backend: the backend to check. If ``None`` this will create a \ BackendManager from default paths. :type backend: OPSI.Backend.Backend """ def usesMysqlBackend(): LOGGER.notice(u"Parsing dispatch.conf") bdc = BackendDispatchConfigFile( u'/etc/opsi/backendManager/dispatch.conf') dispatchConfig = bdc.parse() for entry in dispatchConfig: (regex, backends) = entry if not re.search(regex, u'backend_createBase'): continue if 'mysql' in backends: return True return False LOGGER.debug("Cleaning backend chunk size: {0}".format(_CHUNK_SIZE)) if backend is None: backend = BackendManager( dispatchConfigFile=u'/etc/opsi/backendManager/dispatch.conf', backendConfigDir=u'/etc/opsi/backends', extensionConfigDir=u'/etc/opsi/backendManager/extend.d', depotbackend=False) try: if usesMysqlBackend(): LOGGER.notice( u"Mysql-backend detected. Trying to cleanup mysql-backend first" ) # ToDo: backendConfigFile should be as dynamic as possible # What if we have 2 mysql backends set up? cleanUpMySQL() except Exception as error: LOGGER.warning(error) LOGGER.notice(u"Cleaning up groups") cleanUpGroups(backend) LOGGER.notice(u"Cleaning up products") cleanUpProducts(backend) LOGGER.debug(u'Getting current depots...') depotIds = set(depot.id for depot in backend.host_getObjects( type=["OpsiConfigserver", "OpsiDepotserver"])) # pylint: disable=maybe-no-member LOGGER.debug(u'Depots are: {0}'.format(depotIds)) LOGGER.debug(u'Getting current products...') productIdents = set( product.getIdent(returnType='unicode') for product in backend.product_getObjects()) LOGGER.debug(u'Product idents are: {0}'.format(productIdents)) LOGGER.notice(u"Cleaning up product on depots") cleanUpProductOnDepots(backend, depotIds, productIdents) LOGGER.notice(u"Cleaning up product on clients") cleanUpProductOnClients(backend) LOGGER.notice(u"Cleaning up product properties") productPropertyIdents = set() deleteProductProperties = [] productPropertiesToCleanup = {} for productProperty in backend.productProperty_getObjects(): # pylint: disable=maybe-no-member productIdent = u"%s;%s;%s" % (productProperty.productId, productProperty.productVersion, productProperty.packageVersion) if not productProperty.editable and productProperty.possibleValues: productPropertyIdent = u"%s;%s" % (productIdent, productProperty.propertyId) productPropertiesToCleanup[productPropertyIdent] = productProperty if productIdent not in productIdents: LOGGER.info( u"Marking productProperty %s of non existent product '%s' for deletion" % (productProperty, productIdent)) deleteProductProperties.append(productProperty) else: productPropertyIdent = u'%s;%s' % (productProperty.productId, productProperty.propertyId) productPropertyIdents.add(productPropertyIdent) if deleteProductProperties: for productProperties in chunk(deleteProductProperties, _CHUNK_SIZE): LOGGER.debug(u"Deleting product properties: {0!r}".format( productProperties)) backend.productProperty_deleteObjects(productProperties) # pylint: disable=maybe-no-member LOGGER.notice(u"Cleaning up product property states") deleteProductPropertyStates = [] for productPropertyState in backend.productPropertyState_getObjects(): # pylint: disable=maybe-no-member productPropertyIdent = u'%s;%s' % (productPropertyState.productId, productPropertyState.propertyId) if productPropertyIdent not in productPropertyIdents: LOGGER.info( u"Marking productPropertyState %s of non existent productProperty '%s' for deletion" % (productPropertyState, productPropertyIdent)) deleteProductPropertyStates.append(productPropertyState) if deleteProductPropertyStates: for productPropertyStates in chunk(deleteProductPropertyStates, _CHUNK_SIZE): LOGGER.debug(u"Deleting product property states: {0!r}".format( productPropertyStates)) backend.productPropertyState_deleteObjects(productPropertyStates) # pylint: disable=maybe-no-member for depot in backend.host_getObjects(type='OpsiDepotserver'): # pylint: disable=maybe-no-member objectIds = set( ClientToDepot['clientId'] for ClientToDepot in backend.configState_getClientToDepotserver( depotIds=depot.id)) objectIds.add(depot.id) productOnDepotIdents = {} for productOnDepot in backend.productOnDepot_getObjects( depotId=depot.id): # pylint: disable=maybe-no-member productIdent = u"%s;%s;%s" % (productOnDepot.productId, productOnDepot.productVersion, productOnDepot.packageVersion) productOnDepotIdents[productOnDepot.productId] = productIdent if not productOnDepotIdents: continue deleteProductPropertyStates = [] updateProductPropertyStates = [] for productPropertyState in backend.productPropertyState_getObjects( # pylint: disable=maybe-no-member objectId=objectIds, productId=productOnDepotIdents.keys(), propertyId=[]): productIdent = productOnDepotIdents.get( productPropertyState.productId) if not productIdent: continue productPropertyIdent = u"%s;%s" % (productIdent, productPropertyState.propertyId) productProperty = productPropertiesToCleanup.get( productPropertyIdent) if not productProperty: continue changed = False newValues = [] removeValues = [] changedValues = [] for value in productPropertyState.values: if value in productProperty.possibleValues: newValues.append(value) continue if productProperty.getType( ) == u'BoolProductProperty' and forceBool( value) in productProperty.possibleValues: newValues.append(forceBool(value)) changedValues.append(value) changed = True continue if productProperty.getType() == u'UnicodeProductProperty': newValue = None for possibleValue in productProperty.possibleValues: if forceUnicodeLower( possibleValue) == forceUnicodeLower(value): newValue = possibleValue break if newValue: newValues.append(newValue) changedValues.append(value) changed = True continue removeValues.append(value) changed = True if changed: if not newValues: LOGGER.info( u"Marking productPropertyState %s for deletion: no value in possible values (%s)" % (productPropertyState, removeValues)) deleteProductPropertyStates.append(productPropertyState) else: productPropertyState.setValues(newValues) LOGGER.info( u"Marking productPropertyState %s for update: values not in possible values: %s, values corrected: %s" % (productPropertyState, removeValues, changedValues)) updateProductPropertyStates.append(productPropertyState) if deleteProductPropertyStates: for productPropertyStates in chunk(deleteProductPropertyStates, _CHUNK_SIZE): LOGGER.debug(u"Deleting product property states: {0!r}".format( productPropertyStates)) backend.productPropertyState_deleteObjects( productPropertyStates) # pylint: disable=maybe-no-member del deleteProductPropertyStates if updateProductPropertyStates: for productPropertyStates in chunk(updateProductPropertyStates, _CHUNK_SIZE): LOGGER.debug(u"Updating product property states: {0!r}".format( productPropertyStates)) backend.productPropertyState_updateObjects( productPropertyStates) # pylint: disable=maybe-no-member del updateProductPropertyStates LOGGER.notice(u"Cleaning up config states") cleanUpConfigStates(backend) LOGGER.notice(u"Cleaning up audit softwares") cleanUpAuditSoftwares(backend) LOGGER.notice(u"Cleaning up audit software on clients") cleanUpAuditSoftwareOnClients(backend)
def initializeBackends(ipAddress=None): """ Initial backend setup based on the current configuration. This will create required folders aswell as set up the current backend for use with opsi. :param ipAddress: Force the function to work with the given IP address. :type ipAddress: str """ _setupPasswdFile() from OPSI.Backend.BackendManager import BackendManager managerConfig = { "dispatchConfigFile": u'/etc/opsi/backendManager/dispatch.conf', "backendConfigDir": u'/etc/opsi/backends', "extensionConfigDir": u'/etc/opsi/backendManager/extend.d', "depotbackend": False } with BackendManager(**managerConfig) as backend: backend.backend_createBase() networkConfig = getNetworkConfiguration(ipAddress) fqdn = getLocalFqdn() LOGGER.info(u"Trying to find a Configserver...") configServer = backend.host_getObjects(type='OpsiConfigserver') if not configServer and not backend.host_getIdents(type='OpsiConfigserver', id=fqdn): depot = backend.host_getObjects(type='OpsiDepotserver', id=fqdn) if not depot: LOGGER.notice(u"Creating config server '%s'" % fqdn) serverConfig = _getServerConfig(fqdn, networkConfig) backend.host_createOpsiConfigserver(**serverConfig) configServer = backend.host_getObjects(type='OpsiConfigserver', id=fqdn) else: LOGGER.notice(u"Converting depot server '%s' to config server" % fqdn) configServer = OpsiConfigserver.fromHash(depot[0].toHash()) backend.host_createObjects(configServer) # list expected in further processing configServer = [configServer] else: depot = backend.host_getObjects(type='OpsiDepotserver', id=fqdn) if not depot: LOGGER.notice(u"Creating depot server '%s'" % fqdn) serverConfig = _getServerConfig(fqdn, networkConfig) backend.host_createOpsiDepotserver(**serverConfig) if configServer: if configServer[0].id == fqdn: try: configServer = backend.host_getObjects(type='OpsiConfigserver')[0] except IndexError: raise Exception(u"Config server '%s' not found" % fqdn) if networkConfig['ipAddress']: configServer.setIpAddress(networkConfig['ipAddress']) if networkConfig['hardwareAddress']: configServer.setHardwareAddress(networkConfig['hardwareAddress']) # make sure the config server is present in all backends or we get reference error later on backend.host_insertObject(configServer) # initializeConfigs does only handle a single object configServer = forceList(configServer)[0] initializeConfigs(backend=backend, configServer=configServer) _setupDepotDirectory() _setupWorkbenchDirectory()