예제 #1
0
def updateMySQLBackend(backendConfigFile=u'/etc/opsi/backends/mysql.conf',
                       additionalBackendConfiguration={}):
    """
	Applies migrations to the MySQL backend.

	:param backendConfigFile: Path to the file where the backend \
configuration is read from.
	:type backendConfigFile: str
	:param additionalBackendConfiguration: Additional / different \
settings for the backend that will extend / override the configuration \
read from `backendConfigFile`.
	:type additionalBackendConfiguration: dict
	"""

    config = getBackendConfiguration(backendConfigFile)
    config.update(additionalBackendConfiguration)
    LOGGER.info(u"Current mysql backend config: %s" % config)

    LOGGER.notice(u"Connection to database '%s' on '%s' as user '%s'" %
                  (config['database'], config['address'], config['username']))
    mysql = MySQL(**config)

    schemaVersion = readSchemaVersion(mysql)
    LOGGER.debug("Found database schema version {0}", schemaVersion)

    if schemaVersion is None:
        LOGGER.notice("Missing information about database schema. Creating...")
        createSchemaVersionTable(mysql)
        with updateSchemaVersion(mysql, version=0):
            _processOpsi40migrations(mysql)

        schemaVersion = readSchemaVersion(mysql)

    # The migrations that follow are each a function that will take the
    # established database connection as first parameter.
    # Do not change the order of the migrations once released, because
    # this may lead to hard-to-debug inconsistent version numbers.
    migrations = [
        _dropTableBootconfiguration,
        _addIndexOnProductPropertyValues,
        _addWorkbenchAttributesToHosts,
        _adjustLengthOfGroupId,
        _increaseInventoryNumberLength,
    ]

    for newSchemaVersion, migration in enumerate(migrations, start=1):
        if schemaVersion < newSchemaVersion:
            with updateSchemaVersion(mysql, version=newSchemaVersion):
                migration(mysql)

    LOGGER.debug("Expected database schema version: {0}",
                 DATABASE_SCHEMA_VERSION)
    if not readSchemaVersion(mysql) == DATABASE_SCHEMA_VERSION:
        raise BackendUpdateError("Not all migrations have been run!")

    with MySQLBackend(**config) as mysqlBackend:
        # We do this to make sure all tables that are currently
        # non-existing will be created. That creation will give them
        # the currently wanted schema.
        mysqlBackend.backend_createBase()
def testDropTableBootConfiguration(mysqlBackendConfig, mySQLBackendConfigFile):
    """
    Test if the BOOT_CONFIGURATION table gets dropped with an update.
    """
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createRequiredTables(db)

        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)

        assert 'BOOT_CONFIGURATION' not in getTableNames(db)
def testAddingIndexToProductPropertyValues(mysqlBackendConfig,
                                           mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createRequiredTables(db)

        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)
        # Just making sure nothing breaks because checking if the right
        # index exists in mysql comes near totally senseless torture.

        # Calling the update procedure a second time must not fail.
        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)
def testCorrectingObjectToGroupGroupIdFieldLength(mysqlBackendConfig,
                                                  mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createRequiredTables(db)

        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)

        for column in getTableColumns(db, 'OBJECT_TO_GROUP'):
            if column.name == 'groupId':
                assert column.type.lower().startswith('varchar(')
                assert getColumnLength(column.type) == 255
                break
def testUpdatingSchemaVersion(mysqlBackendConfig, mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createSchemaVersionTable(db)

        version = readSchemaVersion(db)
        assert version is None

        with updateSchemaVersion(db, version=2):
            pass  # NOOP

        version = readSchemaVersion(db)
        assert version == 2
def testReadingSchemaVersionFailsOnUnfinishedUpdate(mysqlBackendConfig,
                                                    mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createSchemaVersionTable(db)

        try:
            with updateSchemaVersion(db, version=1):
                raise RuntimeError("For testing.")
        except RuntimeError:
            pass

        with pytest.raises(DatabaseMigrationUnfinishedError):
            readSchemaVersion(db)
예제 #7
0
def cleanUpMySQL(backendConfigFile=u'/etc/opsi/backends/mysql.conf'):
    """
	Clean up an MySQL backend.

	**This does not work with any backend other than MySQL.**

	:param backendConfigFile: The configuration file of the currently \
used MySQL backend.
	:type backendConfigFile: str
	"""
    config = backendUtil.getBackendConfiguration(backendConfigFile)
    LOGGER.info(u"Current mysql backend config: %s" % config)

    LOGGER.notice(
        u"Connection to database '{database}' on '{address}' as user "
        u"'{username}'".format(**config))
    mysql = MySQL(**config)

    LOGGER.notice(u"Cleaning up defaultValues in productProperties")
    deleteIds = []
    found = []
    for res in mysql.getSet(
            "SELECT * FROM PRODUCT_PROPERTY_VALUE WHERE isDefault like '1'"):
        ident = ';'.join([
            res['propertyId'], res['productId'], res['productVersion'],
            res['productVersion'], res['value']
        ])
        if ident not in found:
            found.append(ident)
        else:
            if res['value'] in (
                    '0', '1') and res['product_property_id'] not in deleteIds:
                deleteIds.append(res['product_property_id'])

    for ID in deleteIds:
        LOGGER.notice(u"Deleting PropertyValue id: {0}".format(ID))
        mysql.execute("DELETE FROM `PRODUCT_PROPERTY_VALUE` where "
                      "`product_property_id` = '{0}'".format(ID))
def testIncreasingInventoryNumberFieldLength(mysqlBackendConfig,
                                             mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createRequiredTables(db)

        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)

        for column in getTableColumns(db, 'HOST'):
            if column.name == 'inventoryNumber':
                assert column.type.lower().startswith('varchar(')
                assert getColumnLength(column.type) == 64
                break
        else:
            raise RuntimeError("Expected to find matching column.")
def testUpdatingCurrentBackendDoesBreakNothing(mysqlBackendConfig,
                                               mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)):
        with MySQLBackend(**mysqlBackendConfig) as freshBackend:
            freshBackend.backend_createBase()

        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)
        # Updating again. Should break nothing.
        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)

        with MySQLBackend(**mysqlBackendConfig) as anotherBackend:
            # We want to have the latest schema version
            assert DATABASE_SCHEMA_VERSION == readSchemaVersion(
                anotherBackend._sql)
def testCorrectingProductIdLength(mysqlBackendConfig, mySQLBackendConfigFile):
    """
    Test if the product id length is correctly set.
    """
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createRequiredTables(db)

        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)

        for tableName in ('PRODUCT_PROPERTY', ):
            print("Checking {0}...".format(tableName))

            assert tableName in getTableNames(db)

            assertColumnIsVarchar(db, tableName, 'productId', 255)
def testInsertingSchemaNumber(mysqlBackendConfig, mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createRequiredTables(db)

        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)

        assert 'OPSI_SCHEMA' in getTableNames(db)

        for column in getTableColumns(db, 'OPSI_SCHEMA'):
            name = column.name
            if name == 'version':
                assert column.type.lower().startswith('int')
            elif name == 'updateStarted':
                assert column.type.lower().startswith('timestamp')
            elif name == 'updateEnded':
                assert column.type.lower().startswith('timestamp')
            else:
                raise Exception("Unexpected column!")
def testReadingSchemaVersionOnlyReturnsNewestValue(mysqlBackendConfig,
                                                   mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createSchemaVersionTable(db)

        with updateSchemaVersion(db, version=1):
            pass

        with updateSchemaVersion(db, version=15):
            pass

        for number in range(1, 4):
            with updateSchemaVersion(db, version=number * 2):
                pass

        with updateSchemaVersion(db, version=3):
            pass

        assert readSchemaVersion(db) == 15
def testCorrectingLicenseOnClientLicenseKeyLength(mysqlBackendConfig,
                                                  mySQLBackendConfigFile):
    """
    Test if the license key length is correctly set.

    An backend updated from an older version has the field 'licenseKey'
    on the LICENSE_ON_CLIENT table as VARCHAR(100).
    A fresh backend has the length of 1024.
    The size should be the same.
    """
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createRequiredTables(db)

        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)

        for tableName in ('LICENSE_ON_CLIENT', 'SOFTWARE_CONFIG',
                          'SOFTWARE_LICENSE_TO_LICENSE_POOL'):
            print("Checking {0}...".format(tableName))

            assert tableName in getTableNames(db)

            assertColumnIsVarchar(db, tableName, 'licenseKey', 1024)
def testAddingWorkbenchAttributesToHost(mysqlBackendConfig,
                                        mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createRequiredTables(db)

        updateMySQLBackend(backendConfigFile=mySQLBackendConfigFile)

        changesFound = 0
        for column in getTableColumns(db, 'HOST'):
            if column.name == 'workbenchLocalUrl':
                assert column.type.lower().startswith('varchar(')
                assert getColumnLength(column.type) == 128
                changesFound += 1
            elif column.name == 'workbenchRemoteUrl':
                assert column.type.lower().startswith('varchar(')
                assert getColumnLength(column.type) == 255
                changesFound += 1

            if changesFound == 2:
                break

        assert changesFound == 2
def testReadingSchemaVersionIfTableIsMissing(mysqlBackendConfig,
                                             mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        assert readSchemaVersion(db) is None
def testCreatingBackendSetsTheLatestSchemaVersion(mysqlBackendConfig,
                                                  mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        with MySQLBackend(**mysqlBackendConfig) as freshBackend:
            freshBackend.backend_createBase()
            assert readSchemaVersion(db) == DATABASE_SCHEMA_VERSION
def testReadingSchemaVersionFromEmptyTable(mysqlBackendConfig,
                                           mySQLBackendConfigFile):
    with cleanDatabase(MySQL(**mysqlBackendConfig)) as db:
        createSchemaVersionTable(db)

        assert readSchemaVersion(db) is None