Package org.globus.workspace.scheduler.defaults.pilot

Source Code of org.globus.workspace.scheduler.defaults.pilot.PilotSlotManagement

/*
* Copyright 1999-2008 University of Chicago
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package org.globus.workspace.scheduler.defaults.pilot;

import commonj.timers.TimerManager;
import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.globus.workspace.groupauthz.GroupAuthz;
import org.globus.workspace.scheduler.NodeExistsException;
import org.globus.workspace.scheduler.NodeInUseException;
import org.globus.workspace.scheduler.NodeManagement;
import org.globus.workspace.scheduler.NodeManagementDisabled;
import org.globus.workspace.scheduler.NodeNotFoundException;
import org.globus.workspace.scheduler.defaults.ResourcepoolEntry;
import org.globus.workspace.service.binding.authorization.CreationAuthorizationCallout;
import org.nimbus.authz.AuthzDBAdapter;
import org.nimbus.authz.UserAlias;
import org.nimbustools.api.services.rm.DoesNotExistException;
import org.nimbustools.api.services.rm.ResourceRequestDeniedException;
import org.nimbustools.api.services.rm.ManageException;
import org.globus.workspace.Lager;
import org.globus.workspace.ReturnException;
import org.globus.workspace.WorkspaceConstants;
import org.globus.workspace.WorkspaceUtil;
import org.globus.workspace.WorkspaceException;
import org.globus.workspace.cmdutils.TorqueUtil;
import org.globus.workspace.persistence.WorkspaceDatabaseException;
import org.globus.workspace.scheduler.Reservation;
import org.globus.workspace.scheduler.Scheduler;
import org.globus.workspace.scheduler.defaults.NodeRequest;
import org.globus.workspace.scheduler.defaults.SlotManagement;
import org.globus.workspace.service.WorkspaceHome;
import org.globus.workspace.service.InstanceResource;
import org.globus.workspace.service.impls.site.HTTPListener;
import org.globus.workspace.service.impls.site.PilotPoll;
import org.globus.workspace.service.impls.site.SlotPollCallback;
import org.globus.workspace.xen.XenUtil;
import org.safehaus.uuid.UUIDGenerator;
import org.springframework.core.io.Resource;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;

public class PilotSlotManagement implements SlotManagement,
                                            SlotPollCallback,
                                            NodeManagement {

    // -------------------------------------------------------------------------
    // STATIC VARIABLES
    // -------------------------------------------------------------------------

    private static final Log logger =
        LogFactory.getLog(PilotSlotManagement.class.getName());

    private static final String SEVERE_PILOT_ISSUE = "There was a severe " +
            "issue with the workspace site scheduler interaction (worksapce " +
            "pilot).  Please contact your administrator with the time of " +
            "this problem and any relevant information";

    private static final String REMOTE_NODE_MGR_DISABLED = "Pilot mode: node management " +
            "is not possible, use the LRM for node management";

    private static final Exception SEVERE_PILOT_FAULT =
            new Exception(SEVERE_PILOT_ISSUE);

    private static final UUIDGenerator uuidGen = UUIDGenerator.getInstance();

    private static final DateFormat localFormat = DateFormat.getDateTimeInstance();


    // -------------------------------------------------------------------------
    // INSTANCE VARIABLES
    // -------------------------------------------------------------------------

    private final Lager lager;
    private final TimerManager timerManager;
    private final DataSource dataSource;
    private final ExecutorService sharedExecutor;
    private final Object groupLock = new Object();

    private WorkspaceHome instHome;
    private Scheduler schedulerAdapter;

    private PilotSlotManagementDB db;
   
    private CursorPersistence cp = null;

    private String sshNotifyString;

    // keep reference for clean shutdown
    private HTTPListener httpListener = null;

    private String httpNotifyString;
   
    private PilotPoll watcher = null;

    private Pilot pilot = null;

    private String logdirPath = null;

    private TorqueUtil torque;

    private AuthzDBAdapter authzDBAdapter;
    private CreationAuthorizationCallout authzCallout;

    // set from config
    private String contactPort;
    private String accountsPath;
    private String sshNotificationInfo;
    private String pilotPath;
    private String pollScript;
    private int grace = -1; // seconds
    private int padding = -1; // seconds
    private long watcherDelay = 200; // ms
    private String LRM;
    private String submitPath;
    private String deletePath;
    private String pilotVersion;
    private int maxMB; // assumes homogenous nodes for now
    private int ppn = -1; // assumes homogenous nodes for now

    private String destination = null; // only one for now
    private String extraProperties = null;
    private String multiJobPrefix = null;
    private String accounting;

    // -------------------------------------------------------------------------
    // CONSTRUCTOR
    // -------------------------------------------------------------------------

    public PilotSlotManagement(WorkspaceHome home,
                               Lager lager,
                               DataSource dataSource,
                               TimerManager timerManager,
                               AuthzDBAdapter authz,
                               CreationAuthorizationCallout authzCall) {

        if (home == null) {
            throw new IllegalArgumentException("home may not be null");
        }
        this.instHome = home;
        this.sharedExecutor = home.getSharedExecutor();

        if (dataSource == null) {
            throw new IllegalArgumentException("dataSource may not be null");
        }
        this.dataSource = dataSource;
       
        if (timerManager == null) {
            throw new IllegalArgumentException("timerManager may not be null");
        }
        this.timerManager = timerManager;

        if (lager == null) {
            throw new IllegalArgumentException("lager may not be null");
        }
        this.lager = lager;

        this.authzDBAdapter = authz;
        this.authzCallout = authzCall;
    }


    // -------------------------------------------------------------------------
    // MODULE SET (avoids circular dependency problem)
    // -------------------------------------------------------------------------

    public void setInstHome(WorkspaceHome homeImpl) {
        if (homeImpl == null) {
            throw new IllegalArgumentException("homeImpl may not be null");
        }
        this.instHome = homeImpl;
    }
   
   
    // -------------------------------------------------------------------------
    // SET FROM CONFIG
    // -------------------------------------------------------------------------

    public void setContactPort(String contactPort) {
        this.contactPort = contactPort;
    }

    public void setAccountsResource(Resource accountsResource) throws IOException {
        this.accountsPath = accountsResource.getFile().getAbsolutePath();
    }

    public void setSshNotificationInfo(String info) {
        if (info != null && info.trim().length() != 0) {
            this.sshNotificationInfo = info;
        }
    }

    public void setPollScriptResource(Resource pollScriptResource) throws IOException {
        this.pollScript = pollScriptResource.getFile().getAbsolutePath();
    }

    // default is 200ms
    public void setWatcherDelay(long delay) {
        this.watcherDelay = delay;
    }

    public void setPilotPath(String pilotPath) {
        this.pilotPath = pilotPath;
    }

    public void setGrace(int grace) {
        this.grace = grace;
    }

    public void setPadding(int padding) {
        this.padding = padding;
    }

    public void setLRM(String LRM) {
        this.LRM = LRM;
    }

    public void setPpn(int ppn) {
        this.ppn = ppn;
    }

    public void setSubmitPath(String submitPath) {
        this.submitPath = submitPath;
    }

    public void setDeletePath(String deletePath) {
        this.deletePath = deletePath;
    }

    public void setPilotVersion(String pilotVersion) {
        this.pilotVersion = pilotVersion;
    }

    public void setMaxMB(int maxMB) {
        this.maxMB = maxMB;
    }

    public void setDestination(String destination) {
        if (destination != null && destination.trim().length() != 0) {
            this.destination = destination;
        }
    }

    public void setExtraProperties(String extraProperties) {
        if (extraProperties != null && extraProperties.trim().length() != 0) {
            this.extraProperties = extraProperties;
        }
    }

    public void setMultiJobPrefix(String multiJobPrefix) {
        if (multiJobPrefix != null && multiJobPrefix.trim().length() != 0) {
            this.multiJobPrefix = multiJobPrefix;
        }
    }

    public void setLogdirResource(Resource logdirResource) throws IOException {
        this.logdirPath = logdirResource.getFile().getAbsolutePath();
    }

    public AuthzDBAdapter getAuthzDBAdapter() {
        return authzDBAdapter;
    }

    public void setAuthzDBAdapter(AuthzDBAdapter authzDBAdapter) {
        this.authzDBAdapter = authzDBAdapter;
    }

    public void setAccounting(String accounting) {
        if (accounting != null && accounting.trim().length() != 0) {
            this.accounting = accounting;
        }
    }

    // -------------------------------------------------------------------------
    // IoC INIT METHOD
    // -------------------------------------------------------------------------

    public synchronized void validate() throws Exception {

        boolean httpNotificationEnabled = true;
        if (this.contactPort == null) {
            httpNotificationEnabled = false;
            logger.info("pilot http-based notification information " +
                        "is not set therefore it is disabled");
        }

        boolean sshNotificationEnabled = true;
       
        if (this.sshNotificationInfo == null) {
            sshNotificationEnabled = false;
            logger.info("pilot ssh-based (backup) notification information " +
                        "is not set therefore it is disabled");
        }

        if (sshNotificationEnabled) {
            if (this.pollScript != null) {

                final File pollScriptFile = new File(this.pollScript);
                if (!pollScriptFile.exists() || !pollScriptFile.isFile()) {
                    throw new FileNotFoundException(
                            "Not found or not a file: '" + this.pollScript);
                }
            } else {
                throw new Exception("pollScript setting is missing from" +
                        " pilot slot management configuration");
            }

            if (this.watcherDelay < 1) {
                throw new Exception("pilot sweeper delay is less than 1, " +
                                    "invalid");
            }

            if (this.watcherDelay < 50) {
                logger.warn("you should probably not set sweeper delay to " +
                            "less than 50ms");
            }
        }

        if (!httpNotificationEnabled && !sshNotificationEnabled) {
            throw new Exception("no pilot-->container notification " +
                                "mechanism is set");
        }

        if (this.submitPath == null) {
            throw new Exception("pilot LRM submit path is not set");
        }

        if (this.deletePath == null) {
            throw new Exception("pilot LRM delete path is not set");
        }

        if (this.LRM == null) {
            throw new Exception("pilot LRM is not set");
        } else if (!this.LRM.equalsIgnoreCase("torque")) {
            String x = "pilot LRM is not set to torque, the only current impl";
            throw new Exception(x);
        }

        if (this.LRM.equalsIgnoreCase("torque")) {
            this.torque = new TorqueUtil(this.submitPath,
                                         this.deletePath);
        }

        if (this.maxMB <= 0) {
            throw new Exception("Max guest memory is <= 0 MB.  Is the " +
                    "configuration present (maxMB)?");
        }

        if (this.pilotPath == null) {
            throw new Exception("path to pilot on remote nodes is not set");
        } else {
            if (!new File(this.pilotPath).isAbsolute()) {
                throw new Exception("currently expecting path to pilot on " +
                        "remote nodes (pilotPath) to be an absolute path");
            }
        }

        if (this.destination != null) {
            logger.debug("Found destination: " + this.destination);
        } else {
            logger.debug("No destination configured.");
        }

        if (this.extraProperties != null) {
            logger.debug("Found extra properties: " + this.extraProperties);
        } else {
            logger.debug("No extra properties configured.");
        }

        if (this.grace < 0) {
            throw new Exception("grace period is less than zero, invalid. " +
                    "Is the configuration present?");
        }

        if (this.ppn < 0) {
            throw new Exception("processors per node (ppn) is less than zero, " +
                    "invalid.  Is the configuration present?");
        }

        if (this.padding < 0) {
            throw new Exception("padding is less than zero, invalid. " +
                    "Is the configuration present?");
        }

        if (this.pilotVersion == null) {
            throw new Exception("pilot version not set, there is no default");
        }

        // Only 0.2 is supported right now and it is never cased for
        // anywhere else in this class -- will add casing in places it is
        // necessary as the implementations out there diverge.  At some point
        // we may even remove support for old pilot versions if they cause too
        // much casing to happen here or do not enable enough features such
        // that too much casing or lack of important service features would
        // have to happen outside this class (especially if remote client
        // semantics would not be the same for all supported versions of the
        // pilot).
        if (this.pilotVersion.equals("0.2")) {
            this.pilot = new Pilot_0_2();
        } else {
            throw new Exception("pilot version '" + this.pilotVersion +
                                "' is not supported");
        }

        if (sshNotificationEnabled) {
           
            final String[] cmd = {this.pollScript};
            try {
                WorkspaceUtil.runCommand(cmd,
                                         this.lager.eventLog,
                                         this.lager.traceLog);
            } catch (Exception e) {
                final String err = "error testing pilot notification script: ";
                // passing e to error gives very long stacktrace to user
                // logger.error(err, e);
                throw new Exception(err + e.getMessage());
            }

            this.sshNotifyString = this.sshNotificationInfo + this.pollScript;

            logger.debug("tests run of pilot notification script '" +
                                            this.pollScript + "' succeeded");

        }

        if (this.logdirPath != null) {

            final File logdirFile = new File(this.logdirPath);
            if (!logdirFile.exists()) {
                throw new Exception("configured pilot log directory " +
                        "does not exist: " + this.logdirPath);
            }

            if (!logdirFile.isDirectory()) {
                throw new Exception("configured pilot log directory is " +
                        "not a directory: " + this.logdirPath);
            }

            if (!logdirFile.canWrite()) {
                throw new Exception("configured pilot log directory is " +
                        "not a directory that is writeable for this " +
                        "user: " + this.logdirPath);
            }
        } else {
            throw new Exception("logdirPath setting is missing from" +
                        " pilot slot management configuration");
        }

        this.db = new PilotSlotManagementDB(this.dataSource, this.lager);

        if (sshNotificationEnabled) {
            this.cp = new CursorPersistence(this.db, this.timerManager, 5000);

            final String eventsPath = this.pollScript + ".txt";
            logger.debug("Setting events file to '" + eventsPath + "'");

            this.watcher = new PilotPoll(this.timerManager,
                                         this.lager,
                                         this.watcherDelay,
                                         eventsPath,
                                         this.db.currentCursorPosition(),
                                         this);

            this.watcher.scheduleNotificationWatcher();

            // this will consume pilot notifications sent during the time
            // the container was down
        }

        if (httpNotificationEnabled) {
            this.httpListener = new HTTPListener(this.contactPort,
                                                 this.accountsPath,
                                                 this,
                                                 this.sharedExecutor,
                                                 this.lager);
            this.httpNotifyString = this.httpListener.getContactURL();
            this.httpListener.start();
        }


    }

    /* ************************ */
    /* SlotManagement interface */
    /* ************************ */

    /**
     * @param request a single workspace or homogenous group-workspace request
     *
     * @return Reservation res
     * @throws ResourceRequestDeniedException exc
     */
    public Reservation reserveSpace(NodeRequest request, boolean preemptable)
            throws ResourceRequestDeniedException {

        this.reserveSpace(request.getIds(),
                          request.getMemory(),
                          request.getCores(),
                          request.getDuration(),
                          request.getGroupid(),
                          request.getCreatorDN());

        return new Reservation(request.getIds());
    }

    /**
     * @param requests  an array of single workspace or homogenous
     *                  group-workspace requests
     * @param coschedid coscheduling (ensemble) ID
     *
     * @return Reservation res
     * @throws ResourceRequestDeniedException exc
     */
    public Reservation reserveCoscheduledSpace(NodeRequest[] requests,
                                               String coschedid)
            throws ResourceRequestDeniedException {

        if (requests == null || requests.length == 0) {
            throw new IllegalArgumentException("requests null or length 0?");
        }

        // the LRM request will be for the highest memory and duration (the
        // lesser workspaces will not get this extra memory or time -- this is
        // capacity vs. mapping and we will get more sophisticated here later)

        int highestMemory = 0;
        int highestCores = 0;
        int highestDuration = 0;

        final ArrayList idInts = new ArrayList(64);
        final ArrayList allDurations = new ArrayList(64);
       
        for (int i = 0; i < requests.length; i++) {

            final int thisMemory = requests[i].getMemory();

            if (highestMemory < thisMemory) {
                highestMemory = thisMemory;
            }

            final int thisCores = requests[i].getCores();

            if (highestCores < thisCores) {
                highestCores = thisCores;
            }

            final int thisDuration = requests[i].getDuration();

            if (highestDuration < thisDuration) {
                highestDuration = thisDuration;
            }

            final int[] ids = requests[i].getIds();
            if (ids == null) {
                throw new ResourceRequestDeniedException(
                        "Cannot proceed, no ids in NodeRequest parameter (?)");
            }

            for (int j = 0; j < ids.length; j++) {
                idInts.add(new Integer(ids[j]));
                allDurations.add(new Integer(thisDuration));
            }
        }

        final int length = idInts.size();
        final int[] all_ids = new int[length];
        final int[] all_durations = new int[length];

        for (int i = 0; i < length; i++) {
            all_ids[i] = ((Number)idInts.get(i)).intValue();
            all_durations[i] = ((Number)allDurations.get(i)).intValue();
        }

        // Assume that the creator's DN is the same for each node
        final String creatorDN = requests[0].getCreatorDN();

        this.reserveSpace(all_ids, highestMemory, highestCores, highestDuration, coschedid, creatorDN);
        return new Reservation(all_ids, null, all_durations);
    }

    /**
     * Only handling one slot per VM for now, will change in the future
     * (multiple layers).
     *
     * Only handling homogenous requests for now.
     *
     * @param vmids array of IDs.  If array length is greater than one, it is
     *        up to the implementation (and its configuration etc) to decide
     *        if each must map to its own node or not.  In the case where more
     *        than one VM is mapped to the same node, the returned node
     *        assignment array will include duplicates.
     * @param memory megabytes needed
     * @param requestedCores needed
     * @param duration seconds needed
     * @param uuid group ID, can not be null if vmids is length > 1
     * @param creatorDN the DN of the user who requested creation of the VM
     *
     *  @throws ResourceRequestDeniedException can not fulfill request
     */
    private void reserveSpace(final int[] vmids,
                              final int memory,
                              final int requestedCores,
                              final int duration,
                              final String uuid,
                              final String creatorDN)
                  throws ResourceRequestDeniedException {

        if (vmids == null) {
            throw new IllegalArgumentException("no vmids");
        }


        if (memory > this.maxMB) {
            String msg = "Memory request (" + memory + " MB) cannot be " +
                    "fulfilled by any VMM node (maximum: " + this.maxMB +
                    " MB).";
            throw new ResourceRequestDeniedException(msg);
        }

        // When there is no core request, the default is -1,
        // we would actually like one core.
        int cores;
        if (requestedCores <= 0) {
            cores = 1;
        }
        else {
            cores = requestedCores;
        }

        if (vmids.length > 1 && uuid == null) {
            logger.error("cannot make group space request without group ID");
            throw new ResourceRequestDeniedException("internal " +
                    "pilot management error");
        }

        final String slotid;
        if (uuid == null) {
            slotid = uuidGen.generateRandomBasedUUID().toString();
        } else {
            slotid = uuid;
            try {
                for (int i = 0; i < vmids.length; i++) {
                    // add to our own group register, encapsulated from
                    // main service group/coscheduling management
                    this.db.newGroupMember(uuid, vmids[i]);
                }
            } catch (WorkspaceDatabaseException e) {
                logger.error(e.getMessage(), e);
                throw new ResourceRequestDeniedException("internal " +
                        "pilot management error");
            }
        }

        this.reserveSpaceImpl(memory, cores, duration, slotid, vmids, creatorDN);

        // pilot reports hostname when it starts running, not returning an
        // exception to signal successful best effort pending slot
    }

    private void reserveSpaceImpl(final int memory,
                                  final int cores,
                                  final int duration,
                                  final String uuid,
                                  final int[] vmids,
                                  final String creatorDN)
            throws ResourceRequestDeniedException {

        final String outputFile = this.logdirPath + File.separator + uuid;

        final int dur = duration + this.padding;
        final long wallTime = duration + this.padding;


        // If the pbs.ppn option in pilot.conf is 0, we should send
        // the number of CPU cores used by the VM as the ppn string,
        // otherwise, use the defined ppn value
        int ppnRequested;
        if (this.ppn == 0) {
            ppnRequested = cores;
        }
        else {
            ppnRequested = this.ppn;
        }

        String account = getAccountString(creatorDN, this.accounting);

        // we know it's torque for now, no casing
        final ArrayList torquecmd;
        try {
            torquecmd = this.torque.constructQsub(this.destination,
                                                  memory,
                                                  vmids.length,
                                                  ppnRequested,
                                                  wallTime,
                                                  this.extraProperties,
                                                  outputFile,
                                                  false,
                                                  false,
                                                  account);

        } catch (WorkspaceException e) {
            final String msg = "Problem with Torque argument construction";
            if (logger.isDebugEnabled()) {
                logger.error(msg + ": " + e.getMessage(), e);
            } else {
                logger.error(msg + ": " + e.getMessage());
            }
            // scrubbing what client sees
            throw new ResourceRequestDeniedException(msg);
        }

        // no casing necessary yet for pilot, only 0.2 supported now
        final ArrayList pilotCommon;
        try {
            pilotCommon = this.pilot.constructCommon(false, false, true, null);
        } catch (ManageException e) {
            final String msg = "Problem with pilot argument construction";
            if (logger.isDebugEnabled()) {
                logger.error(msg + ": " + e.getMessage(), e);
            } else {
                logger.error(msg + ": " + e.getMessage());
            }
            // scrubbing what client sees
            throw new ResourceRequestDeniedException(msg);
        }

        // same params sent to each pilot in a group job
        final ArrayList pilotReserveSlot;
        try {

            final String notifyString;
            if (this.httpNotifyString != null
                    && this.sshNotifyString != null) {

                notifyString = this.httpNotifyString + "+++" +
                               this.sshNotifyString;
            } else if (this.httpNotifyString != null) {
                notifyString = this.httpNotifyString;
            } else if (this.sshNotifyString != null) {
                notifyString = this.sshNotifyString;
            } else {
                final String msg = "No pilot-->service notification mechanism";
                throw new ResourceRequestDeniedException(msg);
            }

            pilotReserveSlot = this.pilot.constructReserveSlot(memory,
                                                               dur,
                                                               this.grace,
                                                               uuid,
                                                               notifyString);
        } catch (ManageException e) {
            final String msg = "Problem with pilot argument construction";
            if (logger.isDebugEnabled()) {
                logger.error(msg + ": " + e.getMessage(), e);
            } else {
                logger.error(msg + ": " + e.getMessage());
            }
            // scrubbing what client sees
            throw new ResourceRequestDeniedException(msg);
        }

        final StringBuffer pilotcmdbuf = new StringBuffer(256);

        if (this.multiJobPrefix != null && vmids.length > 1) {
            pilotcmdbuf.append(this.multiJobPrefix);
            pilotcmdbuf.append(" ");
        }

        pilotcmdbuf.append(this.pilotPath);
        Iterator iter = pilotCommon.iterator();
        while (iter.hasNext()) {
            pilotcmdbuf.append(" ");
            pilotcmdbuf.append(iter.next());
        }
        iter = pilotReserveSlot.iterator();
        while (iter.hasNext()) {
            pilotcmdbuf.append(" ");
            pilotcmdbuf.append(iter.next());
        }
        final String pilotcmd = pilotcmdbuf.toString();
        logger.info("pilot command = " + pilotcmd);

        final String[] cmd = (String[]) torquecmd.toArray(
                                            new String[torquecmd.size()]);
        String stdout;
        try {
            stdout = WorkspaceUtil.runCommand(cmd, true, pilotcmd,
                                              this.lager.eventLog,
                                              this.lager.traceLog);
        } catch (WorkspaceException e) {
            final String msg = "Problem calling Torque";
            if (logger.isDebugEnabled()) {
                logger.error(msg + ": " + e.getMessage(), e);
            } else {
                logger.error(msg + ": " + e.getMessage());
            }
            // scrubbing what client sees
            throw new ResourceRequestDeniedException(msg);
        } catch (ReturnException e) {
            final String msg = "Problem calling Torque";

            final StringBuffer buf = new StringBuffer(msg);
            buf.append(": return code = ")
               .append(e.retval);

            if (e.stderr != null) {
                buf.append(", stderr = '")
                   .append(e.stderr)
                   .append("'");
            } else {
                buf.append(", no stderr");
            }

            if (e.stdout != null) {
                buf.append(", stdout = '")
                   .append(e.stdout)
                   .append("'");
            } else {
                buf.append(", no stdout");
            }

            logger.error(msg + ": " + buf.toString());

            // scrubbing what client sees
            throw new ResourceRequestDeniedException(msg);
        }

        if (stdout == null || stdout.length() == 0) {
            final String msg = "Inexplicable problem receiving job ID from" +
                    " Torque (return == 0, but no stdout), aborting.";
            logger.error(msg);
            throw new ResourceRequestDeniedException(msg);
        }

        // TODO: analyze stdout here, should have no newlines or spaces
        logger.debug("torque stdout = " + stdout);

        stdout = stdout.trim();
        if (stdout.indexOf('\n') >= 0) {
            logger.warn("torque stdout has a new line");
            // todo: throw exc? strip it?
        }

        try {
            if (vmids.length == 1) {
                this.db.newSlot(uuid, vmids[0], stdout, dur);
            } else {
                this.db.newSlotGroup(uuid, vmids, stdout, dur);
            }
        } catch (WorkspaceDatabaseException e) {
            String msg = "Problem with service database, aborting pilot job.";

            if (logger.isDebugEnabled()) {
                logger.error(msg + ": " + e.getMessage(), e);
            } else {
                logger.error(msg + ": " + e.getMessage());
            }

            // we know it's torque for now, no casing
            try {
                WorkspaceUtil.runCommand(this.torque.constructQdel(stdout),
                                         this.lager.eventLog,
                                         this.lager.traceLog);

            } catch (WorkspaceException e2) {
                String msg2 = " (and problem with Torque qdel)";
                msg += msg2;

                if (logger.isDebugEnabled()) {
                    logger.error(msg2 + ": " + e2.getMessage(), e2);
                } else {
                    logger.error(msg2 + ": " + e2.getMessage());
                }
            } catch (ReturnException e2) {
                String msg2 = " (and problem calling Torque qdel)";
                msg += msg2;

                StringBuffer buf = new StringBuffer(msg2);
                buf.append(": return code = ")
                   .append(e2.retval);

                if (e2.stderr != null) {
                    buf.append(", stderr = '")
                       .append(e2.stderr)
                       .append("'");
                } else {
                    buf.append(", no stderr");
                }

                if (e2.stdout != null) {
                    buf.append(", stdout = '")
                       .append(e2.stdout)
                       .append("'");
                } else {
                    buf.append(", no stdout");
                }

                logger.error(buf.toString());
            }
            throw new ResourceRequestDeniedException(msg);
        }

        if (this.watcher != null) {
            this.watcher.scheduleNotificationWatcher();
        }
    }

    public boolean canCoSchedule() {
        return true;
    }

    public void releaseSpace(int reservationID) throws ManageException {

        if (lager.traceLog) {
            logger.trace("releaseSpace(), id = " + reservationID);
        }
        PilotSlot slot;
        try {
            slot = this.db.getSlot(reservationID);
        } catch (SlotNotFoundException e) {
            // fine in several cases
            logger.debug("slot with vmid " + reservationID + " not found");
            return;
        }

        if (slot.partOfGroup) {
            // todo: move to lock per uuid, LockManager etc.
            synchronized (this.groupLock) {

                // Need to retrieve again after being under lock because others
                // in the group will in many cases be destroyed at once and
                // there can now be situations where okToReleaseBlock can
                // succeed more than once.  For example this will happen in
                // the situation where a pilot job is still pending with the
                // LRM when a group/ensemble is destroyed.

                PilotSlot aslot;
                try {
                    aslot = this.db.getSlot(reservationID);
                } catch (SlotNotFoundException e) {
                    return;
                }


                // one slot in the block has enough information to be used
                // in impl of okToReleaseBlock() and releaseSpaceImpl()
                if (okToReleaseBlock(aslot)) {
                    // force qdel for block release
                    this.releaseSpaceImpl(aslot, false);
                } else {
                    logger.debug("Slot is part of block and it is not " +
                            "OK to release entire block yet");
                }
            }
        } else {
            this.releaseSpaceImpl(slot, slot.terminal);
        }
    }

    public void releaseSpace(NodeRequest nodeRequest,
                      Reservation reservation,
                      boolean preemptable) throws ManageException {
        if (nodeRequest == null) {
            throw new IllegalArgumentException("nodeRequest may not be null");
        }
        if (reservation == null) {
            throw new IllegalArgumentException("reservation may not be null");
        }

        for (int id : reservation.getIds()) {
            this.releaseSpace(id);
        }
    }

    // add release-pending and check if all other VMs in block's pilots
    // have a release-pending or not
    private boolean okToReleaseBlock(PilotSlot slot) throws ManageException {
        try {
            if (slot.nodename != null) {
                this.db.setSlotPendingRemove(slot);
            }
        } catch (SlotNotFoundException e) {
            // this should never happen because of groupLock
            logger.error("inexplcable problem, block slot found but " +
                         "then not updateable");
        }

        int[] vmids = this.db.findVMsInGroup(slot.uuid);
        for (int i = 0; i < vmids.length; i++) {
            try {
                PilotSlot aSlot = this.db.getSlot(vmids[i]);
                if (aSlot.nodename == null) {
                    continue;
                }
                if (!aSlot.pendingRemove) {
                    return false;
                }
            } catch (SlotNotFoundException e) {
                // again, should not happen because of groupLock
                logger.error("slot for vm #" + vmids[i] + " not found?");
            }
        }
        return true;
    }

    private void releaseSpaceImpl(PilotSlot slot,
                                  boolean terminal) throws ManageException {

        // If we've already received word from the pilot that it is in a
        // terminal situation, no need to invoke qdel because the pilot's exit
        // will end the LRM job -- unless the slot is still pending and
        // therefore the pilot has not been run yet
       
        if (!terminal || slot.pending) {
            try {

                WorkspaceUtil.runCommand(
                                this.torque.constructQdel(slot.lrmhandle),
                                this.lager.eventLog,
                                this.lager.traceLog,
                                slot.vmid);
               
            } catch (WorkspaceException e) {
                String msg = "Problem with Torque qdel";

                if (logger.isDebugEnabled()) {
                    logger.error(msg + ": " + e.getMessage(), e);
                } else {
                    logger.error(msg + ": " + e.getMessage());
                }
            } catch (ReturnException e) {
                String msg = "Problem calling Torque qdel";

                StringBuffer buf = new StringBuffer(msg);
                buf.append(": return code = ")
                   .append(e.retval);

                if (e.stderr != null) {
                    buf.append(", stderr = '")
                       .append(e.stderr)
                       .append("'");
                } else {
                    buf.append(", no stderr");
                }

                if (e.stdout != null) {
                    buf.append(", stdout = '")
                       .append(e.stdout)
                       .append("'");
                } else {
                    buf.append(", no stdout");
                }

                logger.error(buf.toString());
            }
        }

        // In most situations we will hear from the pilot again about this
        // slot/block (it won't be here which is expected)

        this.db.removeSlot(slot.uuid);
    }

    public boolean isBestEffort() {
        return true;
    }

    /**
     * Strict evacuation means that the scheduler should not allow any time
     * consuming action on the slot after the running duration expires (actions
     * such as unpropagate).
     *
     * @return true if implementation requires strict evacuation
     */
    public boolean isEvacuationStrict() {
        return true;
    }

    public void setScheduler(Scheduler adapter) {
        this.schedulerAdapter = adapter;
    }

    public boolean isNeededAssociationsSupported() {
        return false;
    }

    /* ********************************** */
    /* NotificationPollCallback interface */
    /* ********************************** */

    public int numPendingNotifications() throws Exception {
        return this.db.numSlotsCached(false);
    }

    public void decreaseNumPending(int n) throws Exception {
        // ignored
    }

    public void cursorPosition(long pos) {
        if (this.cp != null) {
            this.cp.cursorPosition(pos);
        }
    }

    /* ************************** */
    /* SlotPollCallback interface */
    /* ************************** */

    /**
     * The pilot reports the slot has been successfully reserved and what host
     * it's ended up running on.
     *
     * @param slotid   uuid
     * @param hostname slot node
     * @param timestamp time of reservation
     */
    public void reserved(String slotid, String hostname, Calendar timestamp) {
       
        if (this.schedulerAdapter == null) {
            logger.error("Severe problem, slot manager has received word " +
                    "that the slot '" + slotid + "' is reserved (hostname '" +
                    hostname + "') but the manager has " +
                    "not been configured with a way to inform service " +
                    "scheduler to proceed.");
            try {
                PilotSlot slot = this.getSlotAndAssignVM(slotid, hostname);
                // this eventually causes this.releaseSpace() to be called
                // unless there was a race
                this.cancelWorkspace(slot.vmid, SEVERE_PILOT_FAULT);
            } catch (SlotNotFoundException e) {
                logger.error(e.getMessage());
            } catch (WorkspaceDatabaseException e) {
                logger.error(e.getMessage());
            }
            return;
        }

        try {

            final PilotSlot slot = this.getSlotAndAssignVM(slotid, hostname);

            if (hostname == null) {

                logger.error("Pilot '" + slotid + "' sent reserved message " +
                        "without hostname (?). Cancelling vm #" + slot.vmid +
                             " and running trash.");

                // this eventually causes this.releaseSpace() to be called
                // unless there was a race
                this.cancelWorkspace(slot.vmid, SEVERE_PILOT_FAULT);
                return;
            }

            if (timestamp == null) {

                logger.error("Pilot '" + slotid + "' sent reserved message " +
                        "without timestamp (?). Cancelling vm #" + slot.vmid +
                             " and running trash.");

                // this eventually causes this.releaseSpace() to be called
                // unless there was a race
                this.cancelWorkspace(slot.vmid, SEVERE_PILOT_FAULT);
                return;
            }

            final InstanceResource resource;
            try {
                resource = this.instHome.find(slot.vmid);
            } catch (DoesNotExistException e) {
                final String msg = "workspace #" + slot.vmid + " is unknown " +
                        "to the service but the pilot tracker has receieved " +
                        "space for it to run?  pilot ID: '" + slotid + "' " +
                        "There is nothing we can do about this.";
                logger.error(e.getMessage());
                return;
            }

            final int runningTime =
                    resource.getVM().getDeployment().getMinDuration();

            // double-checking assumptions
            if (runningTime > slot.duration - this.padding) {
                logger.error("The running time stored for workspace #" +
                        slot.vmid + " is greater than slot duration (?). " +
                        "Implementation error, backing out.");
                this.cancelWorkspace(slot.vmid, SEVERE_PILOT_FAULT);
                return;
            }

            logger.debug("reserved: running time = " + runningTime +
                         " (slot duration = " + slot.duration + ")");
            Calendar stop = (Calendar) timestamp.clone();
            stop.add(Calendar.SECOND, runningTime);

            Calendar slotstop = (Calendar) timestamp.clone();
            slotstop.add(Calendar.SECOND, slot.duration);

            String msg = Lager.ev(slot.vmid) +
                    "Pilot '" + slot.uuid + "' reserved for VM " +
                    slot.vmid + " @ host '" + hostname + "'.  Started at: " +
                    localFormat.format(timestamp.getTime()) + ".  VM " +
                    "running time ends at: " +
                    localFormat.format(stop.getTime()) + ".  Slot will " +
                    "end itself at approximately: " +
                    localFormat.format(slotstop.getTime());
            if (lager.eventLog) {
                logger.info(msg);
            } else {
                logger.debug(msg);
            }

            this.schedulerAdapter.slotReserved(slot.vmid,
                                               timestamp,
                                               stop,
                                               hostname);
           
        } catch (ManageException e) {
            if (logger.isDebugEnabled()) {
                logger.error(e.getMessage(), e);
            } else {
                logger.error(e.getMessage());
            }
        } catch (SlotNotFoundException e) {

            String msg = "Severe problem, hearing about a slot being " +
                    "reserved but service has no record of it.  Slotid: " +
                    slotid + ", hostname: " + hostname + " (can't qdel or " +
                    "cancel it, we don't know the LRM handle or workspace ID)";
            logger.error(msg);
        }
    }

    /**
     * The pilot reports it started running but the slot was not successfully
     * reserved beacuse of some problem.
     *
     * @param slotid uuid
     * @param hostname hostname
     * @param error  error message
     */
    public void errorReserving(String slotid, String hostname, String error) {
        try {

            PilotSlot slot = this.getSlotAndAssignVM(slotid, hostname);

            String id = "Pilot '" + slotid + "'";
            if (hostname != null) {
                id += " @ host '" + hostname + "'";
            }

            if (slot.terminal) {
                logger.error(id + " had an error " +
                             "reserving: '" + error + "', nothing to do " +
                             "slot was already terminal -- unexpected this " +
                             "would be the case because this is the first " +
                             "we've heard from slot/sub-slot (?)");
            } else {

                logger.error(id + " had an error reserving: '" + error +
                             "' (cancelling vm #" + slot.vmid + " without " +
                             "running trash)");

                // this eventually causes this.releaseSpace() to be called
                // unless there was a race
                this.cancelWorkspaceNoTrash(slot.vmid, new Exception(error));

                this.db.setSlotTerminal(slot);
            }

        } catch (WorkspaceDatabaseException e) {
            logger.error(
                   getDBError("problem reserving",
                              slotid,
                              hostname,
                              e.getMessage()),
                   e);
        } catch (SlotNotFoundException e) {
            logger.error(getNoSlotError("problem reserving", slotid, hostname));
        }
    }

    /**
     *
     * The pilot reports that it has been interrupted and has determined the
     * signal was unexpected.  This can happen in three situations:
     *
     * 1. The LRM or administrator has decided to preempt the pilot for
     * whatever reason.
     *
     * 2. The node has been rebooted or shutdown.
     *
     * 3. The LRM job was cancelled by the slot manager (this class).
     *
     * In each situation the pilot attempts to wait a specific (configurable)
     * ratio of the provided grace period.  In cases #1 and #2 this gives the
     * slot manager time to handle the problem (currently this involves running
     * shutdown-trash on all VMs in the slot).  In case #3 the slot manager can
     * just ignore this notification since it is already done with the slot
     * (which is why it cancelled the LRM job).
     *
     * @param slotid uuid
     * @param hostname hostname
     * @param timestamp the time that pilot sent this (second resolution only)
     *                  used to compute if we should act on it
     */
    public void earlyUnreserving(String slotid,
                                 String hostname,
                                 Calendar timestamp) {
        try {
            // SlotNotFoundException expected if we called qdel
            // (which is the usual situation)
            PilotSlot slot = this.db.getSlot(slotid, hostname);

            StringBuffer buf = new StringBuffer();
            buf.append("Pilot '")
               .append(slotid);

            if (hostname != null) {
                buf.append("' @ host '")
                   .append(hostname);
            }
            buf.append("' is being preempted early. Cancelling vm #")
               .append(slot.vmid);

            // Just before the notification was sent
            long timestampLong = timestamp.getTimeInMillis();

            // Now, which could be at any arbitrary time.
            Calendar now = Calendar.getInstance();
            long nowLong = Calendar.getInstance().getTimeInMillis();

            long difference = nowLong - timestampLong;
            difference = difference / 1000; //convert to seconds

            // Because this is an unusual situation, do a lot of logging
            localFormat.setCalendar(timestamp);
            int timestampGMTOffset = timestamp.getTimeZone().getRawOffset();
            buf.append(" || Notification sent @ ")
               .append(localFormat.format(timestamp.getTime()))
               .append(" -- GMT offset ")
               .append(timestampGMTOffset);

            localFormat.setCalendar(now);
            int nowGMTOffset = now.getTimeZone().getRawOffset();
            buf.append(" || Now: ")
               .append(localFormat.format(now.getTime()))
               .append(" -- GMT offset ")
               .append(nowGMTOffset);

            // This check is mostly here if the service was down and the
            // notification consumer is only now getting to the notifications.
            // We allow for a certain amount of inaccuracy on top of the grace
            // period (e.g. only using second resolution on the timestamp).

            int horizon = this.grace + 2;
            boolean trash = true;
            if (difference > horizon) {
                trash = false;
            }

            buf.append(" || Difference in seconds is ")
               .append(difference)
               .append(" which means we will ");

            if (!trash) {
                buf.append("not ");
            }
            buf.append("run shutdown-trash now (difference ");
            if (trash) {
                buf.append(" < ");
            } else {
                buf.append(" > ");
            }
            buf.append("than now + ")
               .append(horizon)
               .append(" seconds.");

            final String msg = buf.toString();
            if (lager.eventLog) {
                logger.info(Lager.ev(slot.vmid) + msg);
            } else {
                logger.debug(Lager.ev(slot.vmid) + msg);
            }

            final Exception e = new Exception("early LRMS preemption");
            if (trash) {
                this.cancelWorkspace(slot.vmid, e);
            } else {
                this.cancelWorkspaceNoTrash(slot.vmid, e);
            }

            this.db.setSlotTerminal(slot);

        } catch (WorkspaceDatabaseException e) {
            final String msg = getDBError("starting early-unreserving",
                                          slotid,
                                          hostname,
                                          e.getMessage());
            logger.error(msg, e);
        } catch (SlotNotFoundException e) {

            String id = "'" + slotid + "'";
            if (hostname != null) {
                id += " @ host '" + hostname + "'";
            }

            logger.debug("Pilot " + id + " is being preempted and " +
                    "we do not have a record of this slot anymore.  This " +
                    "is the expected situation if we have called qdel (this " +
                    "could also have happened if the service just " +
                    "recovered from being down).");
        }
    }

    /**
     * The pilot reports that there was a problem early unreserving, there is
     * no action to take. An error message will usually accompany this (for
     * logging to service logs).
     *
     * @param slotid uuid
     * @param hostname hostname
     * @param error  error message
     */
    public void errorEarlyUnreserving(String slotid,
                                      String hostname,
                                      String error) {
        logger.error("Pilot '" + slotid + "' had an error " +
                         "early-unreserving: '" + error);
        try {
            // SlotNotFoundException expected
            PilotSlot slot = this.db.getSlot(slotid, hostname);

            String id = "Pilot '" + slotid + "'";
            if (hostname != null) {
                id += " @ host '" + hostname + "'";
            }

            logger.warn(id + " had an error early-unreserving. Antecedent " +
                         "unreserving notification either never have made " +
                         "it or service's clean up crossed paths with the " +
                         "pilot's grace period expiring" +
                         ": (cancelling vm " + slot.vmid + "without running " +
                         "trash, a resource-not-found error is likely)");

            // this eventually causes this.releaseSpace() to be called
            // unless there was a race
            final Exception e = new Exception("LRMS preemption with error");
            this.cancelWorkspaceNoTrash(slot.vmid, e);

            this.db.setSlotTerminal(slot);

        } catch (WorkspaceDatabaseException e) {
            final String msg = getDBError("problem early-unreserving",
                                          slotid,
                                          hostname,
                                          e.getMessage());
            logger.error(msg, e);
        } catch (SlotNotFoundException e) {
            // expected
        }
    }

    /**
     * The pilot reports that it has begun unreserving the slot, there is
     * nothing to be done now, this is the end (whether it passes or fails). If
     * there was something the manager was expected to do, earlyUnreserving
     * would have been called.
     *
     * @param slotid uuid
     * @param hostname hostname
     */
    public void unreserving(String slotid, String hostname) {

        try {
            PilotSlot slot = this.db.getSlot(slotid, hostname);

            String id = "Pilot '" + slotid + "'";
            if (hostname != null) {
                id += " @ host '" + hostname + "'";
            }

            logger.error(id + " is being unreserved: " +
                         "Cancelling vm #" + slot.vmid +
                         " (which should be gone already).");

            // this eventually causes this.releaseSpace() to be called
            // unless there was a race
            this.cancelWorkspaceNoTrash(slot.vmid, null);
           
            this.db.setSlotTerminal(slot);

        } catch (WorkspaceDatabaseException e) {
            String msg = getDBError("starting unreserving",
                                        slotid,
                                        hostname,
                                        e.getMessage());
            logger.error(msg, e);
        } catch (SlotNotFoundException e) {
            String id = "Pilot '" + slotid + "'";
            if (hostname != null) {
                id += " @ host '" + hostname + "'";
            }
            logger.debug(id + " is shutting down and " +
                    "we do not have a record of this slot anymore.  This " +
                    "is the expected situation.");
        }
    }

    /**
     * The pilot reports it has killed VMs.
     *
     * @param slotid uuid
     * @param hostname hostname
     * @param killed array of killed VM IDs
     */
    public void kills(String slotid, String hostname, String[] killed) {

        if (killed == null) {
            logger.error("erroneous notification, killed but null VM list");
            return;
        }
        if (killed.length == 0) {
            logger.error("erroneous notification, killed but empty VM list");
            return;
        }
       
        StringBuffer buf = new StringBuffer();
        buf.append("'")
           .append(killed[0])
           .append("'");
        for (int i = 1; i < killed.length; i++) {
            buf.append(", '")
               .append(killed[i])
               .append("'");
        }
        String id = "Pilot '" + slotid + "'";
        if (hostname != null) {
            id += " @ host '" + hostname + "'";
        }
        logger.error(id + "' had to kill these VMS: " + buf.toString());

        try {
            this.db.setSlotTerminal(this.db.getSlot(slotid, hostname));
        } catch (WorkspaceDatabaseException e) {
            logger.error(e.getMessage(), e);
        } catch (SlotNotFoundException e) {
            logger.error(getNoSlotRaceError("killing", slotid, hostname), e);
        }

        for (int i = 0; i < killed.length; i++) {
            // todo: String -> id implementation should be discovered
            final int vmid = XenUtil.xenNameToId(killed[i]);
            if (vmid < 0) {
                logger.error("We don't know about this killed VM '" +
                             killed[i] + "', nothing to do.");
            } else {
                final Exception e =
                        new Exception("LRMS preemption with error (kills)");
                this.cancelWorkspaceNoTrash(vmid, e);
            }
        }
    }

    /**
     * The pilot reports that it can not successfully unreserve the slot, this
     * is no action to take. An error message will usually accompany this (for
     * logging to service logs).
     *
     * @param slotid uuid
     * @param hostname hostname
     * @param error  error message
     */
    public void errorUnreserving(String slotid, String hostname, String error) {
        logger.error("Pilot '" + slotid + "' had an error " +
                         "unreserving: '" + error);
        try {
            // SlotNotFoundException expected
            PilotSlot slot = this.db.getSlot(slotid, hostname);

            String id = "Pilot '" + slotid + "'";
            if (hostname != null) {
                id += " @ host '" + hostname + "'";
            }

            logger.error(id + " had an error " +
                         "unreserving but the previously expected " +
                         "unreserving notification seems to never have made " +
                         "it: (cancelling vm without running trash: " +
                         slot.vmid + ")");

            // this eventually causes this.releaseSpace() to be called
            // unless there was a race
            this.cancelWorkspaceNoTrash(slot.vmid, new Exception(error));

            this.db.setSlotTerminal(slot);

        } catch (WorkspaceDatabaseException e) {
            String msg = getDBError("problem unreserving",
                                        slotid,
                                        hostname,
                                        e.getMessage());
            logger.error(msg, e);
        } catch (SlotNotFoundException e) {
            // expected
        }
    }

    private static String getDBError(String state,
                                     String slotid,
                                     String hostname,
                                     String err) {
        return "Severe problem, hearing about a pilot " +
                state + " but service has a DB problem. " +
                "Slot: " +  slotid + ", hostname: " + hostname +
                " (would have cancelled workspace" +
                " instance if we knew what it was). DB problem: " + err;
    }

    private static String getNoSlotError(String state,
                                         String slotid,
                                         String hostname) {
        return "Problem, hearing about a pilot " +
                state + " but service has no record of it. " +
                "Slotid: " +  slotid + ", hostname: " + hostname +
                " (would have cancelled workspace" +
                " instance if we knew what it was)";
    }

    private static String getNoSlotRaceError(String state,
                                             String slotid,
                                             String hostname) {
        return "Probably OK race condition because of external, " +
                "asynchronous notifications. Hearing about a pilot " +
                state + " but service has no record of it.  Slotid: " +
                slotid + ", hostname = " + hostname;
    }

    // see reserved() and errorReserving()
    PilotSlot getSlotAndAssignVM(String uuid, String hostname)
            throws WorkspaceDatabaseException, SlotNotFoundException {

        synchronized (this.groupLock) {
            return this.db.getSlotAndAssignVMImpl(uuid, hostname);
        }
    }

    /* ******************** */
    /* Service interactions */
    /* ******************** */

    private void cancelWorkspace(int vmid, Exception e) {

        this.cancelWorkspace(vmid, true, e);
    }

    private void cancelWorkspaceNoTrash(int vmid, Exception e) {
       
        this.cancelWorkspace(vmid, false, e);
    }

    private void cancelWorkspace(int vmid,
                                 boolean trash,
                                 Throwable t) {

        final InstanceResource resource;
        try {
            resource = this.instHome.find(vmid);
        } catch (Exception e) {
            final String msg = "Couldn't find workspace " + Lager.id(vmid) +
                    " to cancel, already gone.";
            logger.error(msg);
            return;
        }

        if (!trash) {
            resource.setVMMaccessOK(false);
        }
        try {
            final int curr = resource.getState();
            // no need to set a new state if it is already corrupted
            // (this is a check then act problem)
            if (curr < WorkspaceConstants.STATE_CORRUPTED) {
                resource.setState(curr + WorkspaceConstants.STATE_CORRUPTED,
                                  null);
            }
        } catch (ManageException e) {
            final String msg = "Couldn't set corrupted state on workspace "
                    + Lager.id(vmid) +
                    ", already gone.";
            if (logger.isDebugEnabled()) {
                logger.error(msg, e);
            } else {
                logger.error(msg);
            }
        }
    }

    public ResourcepoolEntry addNode(String hostname, String pool, String networks, int memory,
                                     boolean active)
            throws NodeExistsException, NodeManagementDisabled {
        throw new NodeManagementDisabled(REMOTE_NODE_MGR_DISABLED);
    }

    public List<ResourcepoolEntry> getNodes() throws NodeManagementDisabled {
        throw new NodeManagementDisabled(REMOTE_NODE_MGR_DISABLED);
    }

    public ResourcepoolEntry getNode(String hostname) throws NodeManagementDisabled {
        throw new NodeManagementDisabled(REMOTE_NODE_MGR_DISABLED);
    }

    public ResourcepoolEntry updateNode(String hostname, String pool, String networks,
                                        Integer memory, Boolean active)
            throws NodeInUseException, NodeNotFoundException, NodeManagementDisabled {
        throw new NodeManagementDisabled(REMOTE_NODE_MGR_DISABLED);
    }

    public boolean removeNode(String hostname)
            throws NodeInUseException, NodeManagementDisabled {
        throw new NodeManagementDisabled(REMOTE_NODE_MGR_DISABLED);
    }

    public String getVMMReport() {
        return "No VMM report when pilot is configured.";
    }

    public String getAccountString(String userDN, String accountingType) {

        String accountString = null;
        if (accountingType == null) {
            accountString = null;
        }
        else if (accountingType.equalsIgnoreCase("dn")) {

            accountString = userDN;
        }
        else if (accountingType.equalsIgnoreCase("displayname")) {

            try {
                String userID = authzDBAdapter.getCanonicalUserIdFromDn(userDN);
                final List<UserAlias> aliasList = authzDBAdapter.getUserAliases(userID);
                for (UserAlias alias : aliasList) {
                    if (alias.getAliasType() == AuthzDBAdapter.ALIAS_TYPE_DN) {

                        accountString = alias.getFriendlyName();
                    }
                }
                logger.error("Can't find display name for '" + userDN + "'. "
                             + "No accounting string will be sent to PBS.");
            }
            catch (Exception e) {
                logger.error("Can't connect to authzdb db. No accounting string will be sent to PBS.");
            }
        }
        else if (accountingType.equalsIgnoreCase("group")) {

            try {
                GroupAuthz groupAuthz = (GroupAuthz)this.authzCallout;
                accountString = groupAuthz.getGroupName(userDN);
            }
            catch (Exception e) {
                logger.error("Problem getting group string. Are you sure you're using Group or SQL authz?");
                logger.debug("full error: " + e);
            }
        }
        else {

            logger.error("'" + accountingType + "' isn't a valid accounting string type. "
                         + "No accounting string will be sent to PBS.");
        }

        return accountString;
    }
}
TOP

Related Classes of org.globus.workspace.scheduler.defaults.pilot.PilotSlotManagement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.