package org.ovirt.engine.core.bll; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import org.ovirt.engine.core.common.action.DetachAdGroupFromTimeLeasedPoolParameters; import org.ovirt.engine.core.common.action.DetachUserFromTimeLeasedPoolParameters; import org.ovirt.engine.core.common.action.VmPoolToAdGroupParameters; import org.ovirt.engine.core.common.action.VmPoolUserParameters; import org.ovirt.engine.core.common.businessentities.ad_groups; import org.ovirt.engine.core.common.businessentities.time_lease_vm_pool_map; import org.ovirt.engine.core.common.queries.GetAdGroupByIdParameters; import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.common.users.VdcUser; import org.ovirt.engine.core.compat.LogCompat; import org.ovirt.engine.core.compat.LogFactoryCompat; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.utils.timer.OnTimerMethodAnnotation; import org.ovirt.engine.core.utils.timer.SchedulerUtilQuartzImpl; /** * This class handle desktop pool of time leased typ */ public final class TimeLeasedVmPoolManager { private static LogCompat log = LogFactoryCompat.getLog(TimeLeasedVmPoolManager.class); private static final TimeLeasedVmPoolManager _instance = new TimeLeasedVmPoolManager(); private TimeLeasedVmPoolManager() { SchedulerUtilQuartzImpl.getInstance().scheduleAFixedDelayJob(this, "OnStartTimer", new Class[0], new Object[0], 0, 3, TimeUnit.SECONDS); SchedulerUtilQuartzImpl.getInstance().scheduleAFixedDelayJob(this, "OnEndTimer", new Class[0], new Object[0], 0, 3, TimeUnit.SECONDS); SchedulerUtilQuartzImpl.getInstance().scheduleAFixedDelayJob(this, "OnActionsTimer", new Class[0], new Object[0], 0, 3, TimeUnit.SECONDS); List<time_lease_vm_pool_map> allPools = DbFacade.getInstance().getVmPoolDAO().getAllTimeLeasedVmPoolMaps(); for (time_lease_vm_pool_map map : allPools) { try { ConcreteAddAction(map, false); PrintQueues(); } catch (java.lang.Exception e) { log.error(e); } } log.infoFormat("TimeLeasedVmPoolManager constractor entered"); } public static TimeLeasedVmPoolManager getInstance() { return _instance; } /** * Queue of users/groups with vm pool map, wating to be attached */ private SortedMap<Date, List<time_lease_vm_pool_map>> _startQueue = new TreeMap<Date, List<time_lease_vm_pool_map>>(); /** * Queue of users/groups with vm pool map, wating to be detach */ private SortedMap<Date, List<time_lease_vm_pool_map>> _endQueue = new TreeMap<Date, List<time_lease_vm_pool_map>>(); /** * Time to attach vm pools to user/group */ @OnTimerMethodAnnotation("OnStartTimer") public void OnStartTimer() { try { DoAction(_startQueue, true); } catch (java.lang.Exception e) { } } /** * Time to detach vm pools from user/group */ @OnTimerMethodAnnotation("OnEndTimer") public void OnEndTimer() { try { DoAction(_endQueue, false); } catch (java.lang.Exception e) { } } /** * This function called then time lease starts/ends. Generally it call by * start/end timers. Algorithm: check appropriate timer on actions can be * done, do these actions, remove it from queues. * * @param queue * @param isAttach */ private void DoAction(SortedMap<Date, List<time_lease_vm_pool_map>> queue, boolean isAttach) { if (queue.size() != 0) { /** * Store all times to remove in another list */ java.util.ArrayList<java.util.Date> toRemove = new java.util.ArrayList<java.util.Date>(); for (Map.Entry<Date, List<time_lease_vm_pool_map>> value : queue.entrySet()) { if (value.getKey().after(new java.util.Date())) { break; } toRemove.add(value.getKey()); for (time_lease_vm_pool_map map : value.getValue()) { time_lease_vm_pool_map currMap = DbFacade.getInstance().getVmPoolDAO().getTimeLeasedVmPoolMapByIdForVmPool( map.getid(), map.getvm_pool_id()); if (currMap != null) { CommandBase command = createCommand(currMap, isAttach); command.ExecuteAction(); } } } for (java.util.Date time : toRemove) { queue.remove(time); } } } /** * This function used to create appropriate command should be executed * * @param map * @param isAttach * @return */ private CommandBase createCommand(time_lease_vm_pool_map map, boolean isAttach) { CommandBase returnValue = null; if (isAttach) { if (map.gettype() == 0) { /** * User will be attached to pool only if it not attached yet */ if (DbFacade.getInstance().getTagDAO().getVmPoolTagsByVmPoolIdAndAdElementId(map.getvm_pool_id(), map.getid()) .size() == 0) // DbFacade.Instance.GetUserVmPoolByUserIdAndPoolId(map.id, // map.vm_pool_id) == null) { VmPoolUserParameters tempVar = new VmPoolUserParameters(map.getvm_pool_id(), new VdcUser( map.getid(), "", ""), true); tempVar.setShouldBeLogged(true); // old time-lease pools implementation // should be re-implemented, don't remove comments below // returnValue = // CommandsFactory.CreateCommand(VdcActionType.AttachVmPoolToUser, // tempVar); } } else { /** * group will be attached to pool only if it not attached yet */ if (DbFacade.getInstance().getTagDAO().getVmPoolTagsByVmPoolIdAndAdElementId(map.getvm_pool_id(), map.getid()) == null) { VdcQueryReturnValue ret = Backend.getInstance().runInternalQuery(VdcQueryType.GetAdGroupById, new GetAdGroupByIdParameters(map.getid())); if (ret != null && ret.getReturnValue() != null) { Object tempVar2 = ret.getReturnValue(); ad_groups adGroup = (ad_groups) ((tempVar2 instanceof ad_groups) ? tempVar2 : null); if (adGroup != null) { VmPoolToAdGroupParameters tempVar3 = new VmPoolToAdGroupParameters(map.getvm_pool_id(), adGroup, true); tempVar3.setShouldBeLogged(true); // old time-lease pools implementation // returnValue = // CommandsFactory.CreateCommand(VdcActionType.AttachVmPoolToAdGroup, // tempVar3); } } } } } else { if (map.gettype() == 0) { returnValue = new DetachUserFromTimeLeasedPoolCommand(new DetachUserFromTimeLeasedPoolParameters( map.getvm_pool_id(), map.getid(), true)); } else { returnValue = new DetachAdGroupFromTimeLeasedPoolCommand(new DetachAdGroupFromTimeLeasedPoolParameters( map.getid(), map.getvm_pool_id(), true)); } } return returnValue; } /** * Action types. Action - operation on queue. */ private enum Actions { Add, Update, Remove; public int getValue() { return this.ordinal(); } public static Actions forValue(int value) { return values()[value]; } } /** * asyncronious actions handling. Action not handle directly on request - * instead there is actions queue where all actions stored and one time in 3 * seconds all actions run in row. Actions timer on only where there is * actions in queue */ @OnTimerMethodAnnotation("OnActionsTimer") public void OnActionsTimer() { // lock (sync) // { try { while (_actions.size() > 0) { TimeLeasedAction currentAction = _actions.poll(); switch (currentAction._action) { case Add: { ConcreteAddAction(currentAction._map, true); break; } case Remove: { ConcreteRemoveAction(currentAction._map); break; } case Update: { ConcreteUpdateAction(currentAction._map); break; } } ActionDescription(currentAction); } } catch (java.lang.Exception e) { } // } } /** * This command run when some user/group should be added to pool. Its called * from two places: 1. during Vdc initialization. In this case no need to * update timers since there are many actions added and in the end of * initialization timers initialized. 2. When user/group added to pool from * Gui. * * @param map * @param proceedTimers * true if command called from gui, otherwise false */ private void ConcreteAddAction(time_lease_vm_pool_map map, boolean proceedTimers) { /** * if actions time in the past - generate detach command and run it */ if (proceedTimers) { log.infoFormat("ConcreteAddAction entered with: {0}", map); PrintQueues(); } if (map.getend_time().compareTo(new java.util.Date()) < 0) { CommandBase command = createCommand(map, false); command.ExecuteAction(); return; } else if (map.getstart_time().compareTo(new java.util.Date()) < 0) { /** * if actios's start time in the past - run it */ CommandBase command = createCommand(map, true); if (command != null) { command.ExecuteAction(); } } else { /** * Add new action to start queue. If action must be scheduled before * first scheduled action - recalculate timer's timout */ List<time_lease_vm_pool_map> list = _startQueue.get(map.getstart_time()); if (list == null) { list = new java.util.ArrayList<time_lease_vm_pool_map>(); _startQueue.put(map.getstart_time(), list); } list.add(map); } /** * Add new action to end queue. If action must be scheduled before first * scheduled action - recalculate timer's timout */ List<time_lease_vm_pool_map> endlist = _endQueue.get(map.getend_time()); if (endlist == null) { endlist = new java.util.ArrayList<time_lease_vm_pool_map>(); _endQueue.put(map.getend_time(), endlist); } endlist.add(map); } /** * This action called when user detach user/group from time leased pool * manually or then user updated times. * * @param map */ private void ConcreteRemoveAction(time_lease_vm_pool_map map) { RemoveAction(_startQueue, map, map.getstart_time()); RemoveAction(_endQueue, map, map.getend_time()); } private void RemoveAction(SortedMap<java.util.Date, List<time_lease_vm_pool_map>> queue, time_lease_vm_pool_map map, java.util.Date time) { List<time_lease_vm_pool_map> list = queue.get(time); if (list != null) { time_lease_vm_pool_map toRemove = null; for (time_lease_vm_pool_map currMap : list) { if (currMap.getvm_pool_id().equals(map.getvm_pool_id()) && currMap.getid().equals(map.getid())) { toRemove = currMap; break; } } if (toRemove != null) { list.remove(toRemove); if (list.isEmpty()) { queue.remove(time); } } } } public void AddAction(time_lease_vm_pool_map map) { synchronized (_actions) { TimeLeasedAction action = new TimeLeasedAction(Actions.Add, map); InternalAddAction(action); } } public void RemoveAction(time_lease_vm_pool_map map) { synchronized (_actions) { TimeLeasedAction action = new TimeLeasedAction(Actions.Remove, map); InternalAddAction(action); } } private void InternalAddAction(TimeLeasedAction action) { _actions.offer(action); } public void UpdateAction(time_lease_vm_pool_map map) { synchronized (_actions) { TimeLeasedAction action = new TimeLeasedAction(Actions.Update, map); InternalAddAction(action); } } private void ConcreteUpdateAction(time_lease_vm_pool_map map) { ConcreteRemoveAction(map.oldMap); ConcreteAddAction(map, true); } private static class TimeLeasedAction { public TimeLeasedAction(Actions action, time_lease_vm_pool_map map) { _action = action; _map = map; } public Actions _action = Actions.forValue(0); public time_lease_vm_pool_map _map; } private Queue<TimeLeasedAction> _actions = new LinkedList<TimeLeasedAction>(); private void ActionDescription(TimeLeasedAction action) { log.infoFormat("Time leased manager action: {0} for {1}", action._action.toString(), action._map.toString()); PrintQueues(); } public void PrintQueues() { log.infoFormat("Start queue time leased manager. Next Peektime"); for (List<time_lease_vm_pool_map> list : _startQueue.values()) { for (time_lease_vm_pool_map map : list) { log.infoFormat("{0}", map.toString()); } } log.info("End queue time leased manager:"); for (List<time_lease_vm_pool_map> list : _endQueue.values()) { for (time_lease_vm_pool_map map : list) { log.infoFormat("{0}", map.toString()); } } } }