Martin Mucha has uploaded a new change for review. Change subject: core: scoped mac pool manager ......................................................................
core: scoped mac pool manager add scoped manager, methods allowing querying/creating/removing pools for related scope. Change-Id: If522a0b0d5f810281bed010b46b5242f7dbdcc29 Bug-Url: https://bugzilla.redhat.com/1078844 Signed-off-by: Martin Mucha <mmu...@redhat.com> --- A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/AbstractScope.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/DataCenterScope.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/DefaultScopeManipulation.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/GlobalScope.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ObjectCounter.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/Scope.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopeManipulation.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopeManipulationImpl.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopedMacPoolManager.java 9 files changed, 606 insertions(+), 2 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/99/26799/1 diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/AbstractScope.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/AbstractScope.java new file mode 100644 index 0000000..ab1d1a0 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/AbstractScope.java @@ -0,0 +1,50 @@ +package org.ovirt.engine.core.bll.network; + +import org.ovirt.engine.core.compat.Guid; + +/* + * Some sort of SPI for {@link org.ovirt.engine.core.bll.network.macPoolManager.Scope} implementations. Implemented as + * a abstract class instead of interface, because there is need of having class that would implement both this SPI and + * {@link Scope} interface, and this SPI is definitely not an extension of Scope. + */ +abstract class AbstractScope implements Scope { + + /** + * Called on initialization {@link ScopedMacPoolManager}. + * Based on given scope, locate all existing scopes and init them. + * For example {@link GlobalScope} no scope will be initialized, since everything belongs to default scope, while + * for DataCenterScope will be initialized one scope per each storage_pool table, which has configured + * macPool ranges. + */ + public abstract void initialize(); + + /** + * + * @param scopeGuid guid identifying pool + * @return pool for given guid if exists of default(global) pool. + */ + abstract MacPoolManagerStrategy getPool(Guid scopeGuid); + + /** + * @param scopeGuid guid identifying pool + * @param ranges pool ranges to init the pool + * + * @return true if request was accepted and pool was created. + */ + abstract boolean createPool(Guid scopeGuid, String ranges); + + /** + * @param scopeGuid guid identifying pool + * @param ranges pool ranges to re-init the pool + * + * @return true if request was accepted and pool was altered. + */ + abstract boolean modifyPool(Guid scopeGuid, String ranges); + + /** + * @param scopeGuid guid identifying pool + * + * @return true if request was accepted and pool was removed. + */ + abstract boolean removePool(Guid scopeGuid); +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/DataCenterScope.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/DataCenterScope.java new file mode 100644 index 0000000..58700d9 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/DataCenterScope.java @@ -0,0 +1,169 @@ +package org.ovirt.engine.core.bll.network; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.ovirt.engine.core.common.businessentities.StoragePool; +import org.ovirt.engine.core.common.businessentities.VM; +import org.ovirt.engine.core.common.businessentities.network.VmNic; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.dal.dbbroker.DbFacade; +import org.ovirt.engine.core.utils.log.LogFactory; + +/** + * One pool per data center if one is configured to have its own mac pool. + */ +class DataCenterScope extends AbstractScope { + protected static final org.ovirt.engine.core.utils.log.Log log = LogFactory.getLog(ScopedMacPoolManager.class); + + private final Map<Guid, MacPoolManagerStrategy> scopedPools; + private final int maxMacsCountInPool; + private final boolean allowDuplicates; + private final MacPoolManagerStrategy defaultScope; + + + DataCenterScope(int maxMacsCountInPool, boolean allowDuplicates, MacPoolManagerStrategy defaultScope) { + this.maxMacsCountInPool = maxMacsCountInPool; + this.allowDuplicates = allowDuplicates; + this.defaultScope = defaultScope; + + scopedPools = new HashMap<>(); + } + + @Override + public ScopeManipulation storagePool(Guid storagePoolId) { + return new ScopeManipulationImpl(storagePoolId, hasPoolForScope(storagePoolId), true, this); + } + + @Override + public ScopeManipulation vm(VM vm) { + if (vm == null) { + log.error("Trying to obtain macPool for 'null' scope; returning default scope instead", + new IllegalArgumentException()); + return new DefaultScopeManipulation(defaultScope); + } + + + if (vm.getStoragePoolId() == null) { + final Guid vmId = vm.getId(); + vm = queryVm(vmId); + } + return storagePool(vm.getStoragePoolId()); + } + + @Override + public ScopeManipulation vm(Guid vmId) { + return storagePool(queryVm(vmId).getStoragePoolId()); + } + + + VM queryVm(Guid vmId) { + return DbFacade.getInstance().getVmDao().get(vmId); + } + + @Override + public ScopeManipulation vmNic(VmNic vmNic) { + return vm(vmNic.getVmId()); + } + + @Override + public void initialize() { + final List<StoragePool> storagePools = DbFacade.getInstance().getStoragePoolDao().getAll(); + for (StoragePool storagePool : storagePools) { + final String macPoolRanges = storagePool.getMacPoolRanges(); + if (macPoolRanges != null) { + createPool(storagePool.getId(), macPoolRanges); + } + } + } + + @Override + MacPoolManagerStrategy getPool(Guid scopeGuid) { + final MacPoolManagerStrategy pool = scopedPools.get(scopeGuid); + if (pool == null) { + log.debug("Returning default mac pool for scope ID=" + scopeGuid); + return defaultScope; + } else { + return pool; + } + } + + boolean hasPoolForScope(Guid scopeGuid) { + return scopedPools.containsKey(scopeGuid); + } + + @Override + boolean createPool(Guid scopeGuid, String ranges) { + if (scopedPools.containsKey(scopeGuid) || rangesDefinitionIsEmpty(ranges)) { + return false; + } + + try { + MacPoolManagerRanges poolForScope = createAndInitPool(ranges); + scopedPools.put(scopeGuid, poolForScope); + return true; + } catch (Exception e) { + log.error("Failed initialization of pool ID='"+scopeGuid+"', check it's configuration ("+ranges+"). " + + "This pool will not be created and default one will be used instead", e); + return false; + } + } + + private MacPoolManagerRanges createAndInitPool(String ranges) { + MacPoolManagerRanges poolForScope = new MacPoolManagerRanges(maxMacsCountInPool, ranges, allowDuplicates); + poolForScope.initialize(); + return poolForScope; + } + + /** + * {@inheritDoc} + * <p/> + * when pool configuration changes its content is not lost, only it's ranges changes. And as a consequence already + * assigned MAC from mac pool may not be from specified range any more and has to be added as a custom one. + * So in that case 'normal' MAC become custom ones and vice versa. + */ + @Override + boolean modifyPool(Guid scopeGuid, String ranges) { + final boolean scopeExist = scopedPools.containsKey(scopeGuid); + if (!scopeExist) { + return createPool(scopeGuid, ranges); + } + + final boolean poolDefinitionRemoved = rangesDefinitionIsEmpty(ranges); + if (poolDefinitionRemoved) { + return removePool(scopeGuid); + } + + final MacPoolManagerStrategy currentPool = getPool(scopeGuid); + final MacPoolManagerRanges newPool = createAndInitPool(ranges); + + currentPool.copyContent(newPool); + scopedPools.put(scopeGuid, newPool); //replace currentPool + + return true; + } + + /** + * {@inheritDoc} + * <p/> + * when pool gets removed its content is not lost, it's copied to global pool. Notice, that it's ranges may be + * different. In that case 'normal' MAC become custom ones and vice versa. + */ + @Override + boolean removePool(Guid scopeGuid) { + if (!scopedPools.containsKey(scopeGuid)) { + return false; + } else { + final MacPoolManagerStrategy currentPool = getPool(scopeGuid); + currentPool.copyContent(defaultScope); //copy current content to default pool; + final boolean removed = scopedPools.remove(scopeGuid) != null; + + return removed; + } + } + + private boolean rangesDefinitionIsEmpty(String ranges) { + return ranges == null || ranges.isEmpty(); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/DefaultScopeManipulation.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/DefaultScopeManipulation.java new file mode 100644 index 0000000..9e29e87 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/DefaultScopeManipulation.java @@ -0,0 +1,47 @@ +package org.ovirt.engine.core.bll.network; + +/** + * ScopeManipulation which uses one specified pool, which exists, and cannot be modified. + */ +final class DefaultScopeManipulation implements ScopeManipulation { + + private final MacPoolManagerStrategy defaultScope; + + DefaultScopeManipulation(MacPoolManagerStrategy defaultScope) { + if (defaultScope == null) { + throw new IllegalArgumentException(); + } + + this.defaultScope = defaultScope; + } + + @Override + public MacPoolManagerStrategy getPool() { + return defaultScope; + } + + @Override + public void createPool(String macPoolRanges) { + //do nothing. There's just one scope and ever will be. + } + + @Override + public void removePool() { + //do nothing. There's just one scope and ever will be. + } + + @Override + public void modifyPool(String macPoolRanges) { + //do nothing. There's just one scope, ever will be, and you will not change it no matter what. + } + + @Override + public boolean isScopeExist() { + return true; + } + + @Override + public boolean isPoolManipulationAllowed() { + return false; + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/GlobalScope.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/GlobalScope.java new file mode 100644 index 0000000..e92bb85 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/GlobalScope.java @@ -0,0 +1,64 @@ +package org.ovirt.engine.core.bll.network; + +import org.ovirt.engine.core.common.businessentities.VM; +import org.ovirt.engine.core.common.businessentities.network.VmNic; +import org.ovirt.engine.core.compat.Guid; + +/** + * One pool for all. No creation/modification/removal of pools, there's just one app-wide pool. + */ +class GlobalScope extends AbstractScope { + + private final DefaultScopeManipulation scopeManipulation; + + GlobalScope(MacPoolManagerStrategy defaultScope) { + scopeManipulation = new DefaultScopeManipulation(defaultScope); + } + + @Override + public ScopeManipulation storagePool(Guid storagePoolId) { + return scopeManipulation; + } + + @Override + public ScopeManipulation vm(VM vm) { + return scopeManipulation; + + } + + @Override + public ScopeManipulation vm(Guid vmId) { + return scopeManipulation; + } + + @Override + public ScopeManipulation vmNic(VmNic vmNic) { + return scopeManipulation; + } + + @Override + public void initialize() { + //do nothing. + } + + @Override + MacPoolManagerStrategy getPool(Guid scopeGuid) { + return scopeManipulation.getPool(); + } + + @Override + boolean createPool(Guid scopeGuid, String ranges) { + return false; + } + + @Override + boolean modifyPool(Guid scopeGuid, String ranges) { + return false; + } + + @Override + boolean removePool(Guid scopeGuid) { + return false; + } + +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ObjectCounter.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ObjectCounter.java index 2df7445..0b12142 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ObjectCounter.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ObjectCounter.java @@ -8,6 +8,10 @@ import org.ovirt.engine.core.utils.log.Log; import org.ovirt.engine.core.utils.log.LogFactory; +/** + * Associative array counting instances of given object. + * @param <T> class of instances being count. + */ class ObjectCounter<T> implements Iterable<T>{ private static final Log log = LogFactory.getLog(ObjectCounter.class); @@ -19,6 +23,11 @@ this.allowDuplicate = allowDuplicate; } + /** + * add instance if possible, incrementing number of its occurrences. + * @param key instance to add. + * @return true if instance was added && count incremented. + */ public boolean add(T key) { ModifiableInteger counter = map.get(key); @@ -33,7 +42,12 @@ } } - + /** + * decrements number of its occurrences, removing instance if possible(count reaches zero). + * + * @param key instance to remove. + * @return true if instance was removed && count decremented. + */ public boolean remove(T key) { ModifiableInteger counter = map.get(key); if (counter == null) { @@ -56,16 +70,29 @@ } + /** + * @param key instance to look for + * @return true if there's at least one occurrence of given instance. + */ public boolean contains(T key) { return map.containsKey(key); } + /** + * @param key instance to look for + * @return number of occurrences of given instance. When instance was not added + */ public int count(T key) { - return this.map.get(key).toInt(); + final ModifiableInteger modifiableInteger = this.map.get(key); + return modifiableInteger == null ? 0 : modifiableInteger.toInt(); } + /** + * @return unmodifiable iterator over all 'registered' instances (i.e. all instances having count >= 0). + */ @Override public Iterator<T> iterator() { + //noinspection unchecked return UnmodifiableIterator.decorate(map.keySet().iterator()); } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/Scope.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/Scope.java new file mode 100644 index 0000000..e1097e9 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/Scope.java @@ -0,0 +1,41 @@ +package org.ovirt.engine.core.bll.network; + +import org.ovirt.engine.core.common.businessentities.VM; +import org.ovirt.engine.core.common.businessentities.network.VmNic; +import org.ovirt.engine.core.compat.Guid; + +/** + * Encapsulates logic for choosing scope from provided data. + */ +public interface Scope { + + /** + * @param storagePoolId id of storage pool + * + * @return scope related to specified storagePool + */ + ScopeManipulation storagePool(Guid storagePoolId); + + /** + * @param vm vm + * + * @return scope related to specified vm + */ + ScopeManipulation vm(VM vm); + + + /** + * @param vmId id of mv + * + * @return scope related to specified vm + */ + ScopeManipulation vm(Guid vmId); + + /** + * @param vmNic vmNic + * + * @return scope related to specified vmNic. + */ + ScopeManipulation vmNic(VmNic vmNic); + +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopeManipulation.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopeManipulation.java new file mode 100644 index 0000000..8143288 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopeManipulation.java @@ -0,0 +1,55 @@ +package org.ovirt.engine.core.bll.network; + +/** + * Encapsulates operations for specific scope. Methods for manipulating with scoped pool as a whole like its creation, + * removal or just getting existing pool instance for given scope. + */ +public interface ScopeManipulation { + /** + * @return mac pool for given scope. If not such scope exists, default (global) pool should be returned instead. + */ + MacPoolManagerStrategy getPool(); + + /** + * creates new mac pool for given scope IF given scope configuration allows it and if not exists yet. + * Otherwise returns silently. Client code should not make any effort trying figure out outcome of this method, + * since implementation of {@link ScopeManipulation} can change + * and it's this class who decides whether your request is to be performed. + * + * @param macPoolRanges pool configuration for new pool. + */ + void createPool(String macPoolRanges); + + /** + * removes mac pool for given scope IF given scope configuration allows it and if pool for this scope exists. + * Otherwise returns silently. Client code should not make any effort trying figure out outcome of this method, + * since implementation of {@link ScopeManipulation} can change + * and it's this class who decides whether your request is to be performed. + */ + void removePool(); + + /** + * changes mac pool configuration for given scope IF given scope configuration allows it and if pool for this scope + * exist. Otherwise returns silently. Client code should not make any effort trying figure out outcome of this + * method, since implementation of {@link ScopeManipulation} can + * change and it's this class who decides whether your request is to be performed. + * + * @param macPoolRanges pool configuration for altered pool. + */ + void modifyPool(String macPoolRanges); + + /** + * When you need to modify/remove/alter pool for given scope and obtaining pool configuration is somewhat expensive + * to do it without being absolutely positive that those will be needed for let's say pool creation (which will not + * be performed anyway if pool already exists). + * + * @return true if given scope exists, false otherwise. + */ + boolean isScopeExist(); + + /** + * @see ScopeManipulation#isScopeExist() + * @return true if given scope allows adding/altering/removing pool related to it. + */ + boolean isPoolManipulationAllowed(); +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopeManipulationImpl.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopeManipulationImpl.java new file mode 100644 index 0000000..78e0ef5 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopeManipulationImpl.java @@ -0,0 +1,96 @@ +package org.ovirt.engine.core.bll.network; + +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.ovirt.engine.core.compat.Guid; + +/** + * synchronized access to pool, delegating back to + * {@link AbstractScope}. It serves just for more readable code && reuse of synchronization. + */ +class ScopeManipulationImpl implements ScopeManipulation { + + private final Guid scopeId; + private final boolean scopeExist; + private final boolean poolManipulationAllowed; + private final AbstractScope scope; + + private final ReentrantReadWriteLock lockObj = new ReentrantReadWriteLock(); + + public ScopeManipulationImpl(Guid scopeId, boolean scopeExist, boolean poolManipulationAllowed, AbstractScope scope) { + this.scopeId = scopeId; + this.scopeExist = scopeExist; + this.poolManipulationAllowed = poolManipulationAllowed; + this.scope = scope; + } + + @Override + public MacPoolManagerStrategy getPool() { + + lockObj.readLock().lock(); + try { + return scope.getPool(scopeId); + } finally { + lockObj.readLock().unlock(); + } + } + + @Override + public void createPool(String macPoolRanges) { + if (scopeExist || !poolManipulationAllowed) { + return; + } + + lockObj.writeLock().lock(); + try { + scope.createPool(scopeId, macPoolRanges); + } finally { + lockObj.writeLock().unlock(); + } + } + + @Override + public void removePool() { + if (!scopeExist || !poolManipulationAllowed) { + return; + } + + lockObj.writeLock().lock(); + try { + scope.removePool(scopeId); + } finally { + lockObj.writeLock().unlock(); + } + } + + @Override + public void modifyPool(String macPoolRanges) { + if (!poolManipulationAllowed) { + return; + } + + lockObj.writeLock().lock(); + try { + scope.modifyPool(scopeId, macPoolRanges); + } finally { + lockObj.writeLock().unlock(); + } + } + + @Override + public boolean isScopeExist() { + + lockObj.readLock().lock(); + try { + return scopeExist; + } finally { + lockObj.writeLock().unlock(); + } + } + + @Override + public boolean isPoolManipulationAllowed() { + return poolManipulationAllowed; + } +} + diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopedMacPoolManager.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopedMacPoolManager.java new file mode 100644 index 0000000..da919be --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/ScopedMacPoolManager.java @@ -0,0 +1,55 @@ +package org.ovirt.engine.core.bll.network; + +import org.ovirt.engine.core.common.config.Config; +import org.ovirt.engine.core.common.config.ConfigValues; +import org.ovirt.engine.core.utils.log.LogFactory; + +public class ScopedMacPoolManager { + + private static final org.ovirt.engine.core.utils.log.Log log = LogFactory.getLog(ScopedMacPoolManager.class); + private static final ScopedMacPoolManager INSTANCE = new ScopedMacPoolManager(); + + private final MacPoolManagerStrategy defaultScope; + private final AbstractScope scope; + + + private ScopedMacPoolManager() { + Boolean allowDuplicates = Config.getValue(ConfigValues.AllowDuplicateMacAddresses); + Integer maxMacsCountInPool = Config.getValue(ConfigValues.MaxMacsCountInPool); + String macPoolRanges = Config.getValue(ConfigValues.MacPoolRanges); + + defaultScope = new MacPoolManagerRanges(maxMacsCountInPool, macPoolRanges, allowDuplicates); + scope = new GlobalScope(defaultScope); + } + + /** + * @return default (global) pool. + */ + public static MacPoolManagerStrategy defaultScope() { + return INSTANCE.defaultScope; + } + + /** + * @return {@link Scope} implementation to choose scope based on + * various data. + */ + public static Scope scopeFor() { + final StackTraceElement[] stackTraceElements = new Exception().getStackTrace(); + final StackTraceElement stackTraceElement = stackTraceElements[1]; + final String method = stackTraceElement.getClassName() + "#" + stackTraceElement.getMethodName(); + log.debug("scopeFor called from: '" + method+"'"); + + return INSTANCE.scope; + } + + /** + * Initialization method to initialize default(global) pool and all 'scoped' pools. Must be called once on app + * start up, prior to any pool usage. Not conforming to this yields {@linkplain IllegalStateException} + * + * @throws IllegalStateException when called more than once. + */ + public static void initialize() { + ScopedMacPoolManager.defaultScope().initialize(); + INSTANCE.scope.initialize(); + } +} -- To view, visit http://gerrit.ovirt.org/26799 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: If522a0b0d5f810281bed010b46b5242f7dbdcc29 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Martin Mucha <mmu...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches