diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..37cf854
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+target
+work
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a77afbf
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+The MIT License
+
+(c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.jenkins-ci.plugins</groupId>
+		<artifactId>plugin</artifactId>
+		<version>1.580.1</version>
+	</parent>
+	<groupId>com.parallels</groupId>
+	<artifactId>parallels-desktop</artifactId>
+	<version>0.0</version>
+	<packaging>hpi</packaging>
+
+	<name>Parallels Desktop Cloud</name>
+	<url>http://parallels.com</url>
+	<licenses>
+		<license>
+			<name>MIT License</name>
+			<url>http://opensource.org/licenses/MIT</url>
+		</license>
+	</licenses>
+	<!-- Assuming you want to host on @jenkinsci:
+	<scm>
+		<connection>scm:git:git://github.com/jenkinsci/${project.artifactId}-plugin.git</connection>
+		<developerConnection>scm:git:git@github.com:jenkinsci/${project.artifactId}-plugin.git</developerConnection>
+		<url>http://github.com/jenkinsci/${project.artifactId}-plugin</url>
+	</scm>
+	 -->
+	<repositories>
+		<repository>
+			<id>repo.jenkins-ci.org</id>
+			<url>http://repo.jenkins-ci.org/public/</url>
+		</repository>
+	</repositories>
+	<pluginRepositories>
+		<pluginRepository>
+			<id>repo.jenkins-ci.org</id>
+			<url>http://repo.jenkins-ci.org/public/</url>
+		</pluginRepository>
+	</pluginRepositories>
+</project>
diff --git a/src/main/java/com/parallels/desktopcould/ParallelsDesktopCloud.java b/src/main/java/com/parallels/desktopcould/ParallelsDesktopCloud.java
new file mode 100644
index 0000000..10c0d03
--- /dev/null
+++ b/src/main/java/com/parallels/desktopcould/ParallelsDesktopCloud.java
@@ -0,0 +1,162 @@
+/*
+ * The MIT License
+ *
+ * (c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.parallels.desktopcloud;
+
+import hudson.Extension;
+import hudson.model.Hudson;
+import hudson.model.Computer;
+import hudson.model.Descriptor;
+import hudson.model.Label;
+import hudson.model.Node;
+import hudson.slaves.Cloud;
+import hudson.slaves.ComputerLauncher;
+import hudson.slaves.NodeProvisioner;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+
+public final class ParallelsDesktopCloud extends Cloud
+{
+	private static final Logger LOGGER = Logger.getLogger(ParallelsDesktopCloud.class.getName());
+	private static final AtomicInteger counter = new AtomicInteger();
+
+	private final List<ParallelsDesktopVM> vms;
+	private final ComputerLauncher pdLauncher;
+	private final String remoteFS;
+
+	@DataBoundConstructor
+	public ParallelsDesktopCloud(String name, String remoteFS, ComputerLauncher pdLauncher,
+			List<ParallelsDesktopVM> vms)
+	{
+		super(name);
+		this.remoteFS = remoteFS;
+		if (vms == null)
+			this.vms = Collections.emptyList();
+		else
+			this.vms = vms;
+		this.pdLauncher = pdLauncher;
+	}
+
+	@Override
+	public Collection<NodeProvisioner.PlannedNode> provision(Label label, int excessWorkload)
+	{
+		LOGGER.log(Level.SEVERE, "Going to provision " + excessWorkload + " executors");
+		Collection<NodeProvisioner.PlannedNode> result = new ArrayList<NodeProvisioner.PlannedNode>();
+		final ParallelsDesktopConnectorSlaveComputer connector = getConnector();
+		for (int i = 0; (i < vms.size()) && (excessWorkload > 0); i++)
+		{
+			final ParallelsDesktopVM vm = vms.get(i);
+			if (label.matches(Label.parse(vm.getLabels())))
+			{
+				final int count = counter.incrementAndGet();
+				final String vmId = vm.getVmid();
+				final String slaveName = "PD Slave #" + count;
+				vm.setSlaveName(slaveName);
+				--excessWorkload;
+				result.add(new NodeProvisioner.PlannedNode(slaveName,
+					Computer.threadPoolForRemoting.submit(new Callable<Node>()
+					{
+						@Override
+						public Node call() throws Exception 
+						{
+							connector.checkVmExists(vmId);
+							return connector.createSlaveOnVM(vm);
+						}
+					}), 1));
+			}
+		}
+		return result;
+	}
+
+	private ParallelsDesktopConnectorSlaveComputer getConnector()
+	{
+		try
+		{
+			ParallelsDesktopConnectorSlave slave = new ParallelsDesktopConnectorSlave("PDConnectorSlave", remoteFS, pdLauncher);
+			Hudson.getInstance().addNode(slave);
+			return (ParallelsDesktopConnectorSlaveComputer)slave.toComputer();
+		}
+		catch (Descriptor.FormException ex)
+		{
+			LOGGER.log(Level.SEVERE, null, ex);
+		}
+		catch (IOException ex)
+		{
+			LOGGER.log(Level.SEVERE, null, ex);
+		}
+		catch(Exception ex)
+		{
+			LOGGER.log(Level.SEVERE, null, ex);
+		}
+		return null;
+	}
+
+	@Override
+	public boolean canProvision(Label label)
+	{
+		if (label != null)
+		{
+			for (ParallelsDesktopVM vm : vms)
+			{
+				if (label.matches(Label.parse(vm.getLabels())))
+					return true;
+			}
+		}
+		return false;
+	}
+
+	public List<ParallelsDesktopVM> getVms()
+	{
+		return vms;
+	}
+
+	public ComputerLauncher getPdLauncher()
+	{
+		return pdLauncher;
+	}
+
+	public String getRemoteFS()
+	{
+		return remoteFS;
+	}
+
+	@Extension
+	public static final class DescriptorImpl extends Descriptor<Cloud>
+	{
+		@Override
+		public String getDisplayName()
+		{
+			return "Parallels Desktop Cloud";
+		}
+	}
+}
diff --git a/src/main/java/com/parallels/desktopcould/ParallelsDesktopCloudRetentionStrategy.java b/src/main/java/com/parallels/desktopcould/ParallelsDesktopCloudRetentionStrategy.java
new file mode 100644
index 0000000..ca47182
--- /dev/null
+++ b/src/main/java/com/parallels/desktopcould/ParallelsDesktopCloudRetentionStrategy.java
@@ -0,0 +1,95 @@
+/*
+ * The MIT License
+ *
+ * (c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.parallels.desktopcloud;
+
+import hudson.slaves.RetentionStrategy;
+import hudson.model.Hudson;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+
+public class ParallelsDesktopCloudRetentionStrategy extends RetentionStrategy<ParallelsDesktopVMSlaveComputer>
+{
+	private static final Logger LOGGER = Logger.getLogger("PDCloudRetentionStrategy");
+
+	@DataBoundConstructor
+	public ParallelsDesktopCloudRetentionStrategy()
+	{
+		super();
+	}
+
+	@Override
+	public long check(ParallelsDesktopVMSlaveComputer c)
+	{
+		LOGGER.log(Level.SEVERE, "Check VM computer " +  c.getName());
+		if (c.isOffline())
+			return 1;
+		if (c.isIdle())
+		{
+			LOGGER.log(Level.SEVERE, "Stopping node...");
+			c.getNode().stop();
+			try
+			{
+				LOGGER.log(Level.SEVERE, "Disconnecting computer...");
+				c.disconnect(null).get();
+				LOGGER.log(Level.SEVERE, "Removing node...");
+				Hudson.getInstance().removeNode(c.getNode());
+				LOGGER.log(Level.SEVERE, "Node was removed.");
+			}
+			catch (Exception e)
+			{
+				LOGGER.log(Level.SEVERE, e.toString());
+			}
+		}
+		return 1;
+	}
+
+	@Override
+	public void start(ParallelsDesktopVMSlaveComputer c)
+	{
+		LOGGER.log(Level.SEVERE, "Starting VM computer " + c.getName());
+		c.connect(false);
+	}
+
+	@Override
+	public DescriptorImpl getDescriptor()
+	{
+		return DESCRIPTOR;
+	}
+
+	@Restricted(NoExternalUse.class)
+	public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
+	public static final class DescriptorImpl extends hudson.model.Descriptor<RetentionStrategy<?>>
+	{
+		@Override
+		public String getDisplayName()
+		{
+			return "ParallelsDesktop Cloud Retention Strategy";
+		}
+	}
+}
diff --git a/src/main/java/com/parallels/desktopcould/ParallelsDesktopConnectorSlave.java b/src/main/java/com/parallels/desktopcould/ParallelsDesktopConnectorSlave.java
new file mode 100644
index 0000000..7f6affd
--- /dev/null
+++ b/src/main/java/com/parallels/desktopcould/ParallelsDesktopConnectorSlave.java
@@ -0,0 +1,72 @@
+/*
+ * The MIT License
+ *
+ * (c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.parallels.desktopcloud;
+
+import hudson.model.Computer;
+import hudson.model.Descriptor;
+import hudson.model.Slave;
+import hudson.model.Node.Mode;
+import hudson.slaves.ComputerLauncher;
+import hudson.slaves.NodeProperty;
+import hudson.slaves.RetentionStrategy;
+import hudson.Extension;
+import java.io.IOException;
+import java.util.ArrayList;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+
+public class ParallelsDesktopConnectorSlave extends Slave
+{
+	@DataBoundConstructor
+	public ParallelsDesktopConnectorSlave(String name, String remoteFS, ComputerLauncher launcher)
+			throws IOException, Descriptor.FormException
+	{
+		super(name, "", remoteFS, 1, Mode.NORMAL, "", launcher,
+				new RetentionStrategy.Demand(1, 1), new ArrayList<NodeProperty<?>>());
+	}
+
+	@Override
+	public Computer createComputer()
+	{
+		return new ParallelsDesktopConnectorSlaveComputer(this);
+	}
+
+	@Override
+	public String getRemoteFS()
+	{
+		String res = super.getRemoteFS();
+		return res;
+	}
+
+	@Extension
+	public static final class DescriptorImpl extends SlaveDescriptor
+	{
+		@Override
+		public String getDisplayName()
+		{
+			return "Parallels Desktop connector slave";
+		}
+	}
+}
diff --git a/src/main/java/com/parallels/desktopcould/ParallelsDesktopConnectorSlaveComputer.java b/src/main/java/com/parallels/desktopcould/ParallelsDesktopConnectorSlaveComputer.java
new file mode 100644
index 0000000..4f3d40e
--- /dev/null
+++ b/src/main/java/com/parallels/desktopcould/ParallelsDesktopConnectorSlaveComputer.java
@@ -0,0 +1,196 @@
+/*
+ * The MIT License
+ *
+ * (c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.parallels.desktopcloud;
+
+import java.lang.reflect.Field;
+
+import hudson.model.Node;
+import hudson.model.Slave;
+import hudson.remoting.Channel;
+import hudson.slaves.SlaveComputer;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.concurrent.ExecutionException;
+import java.lang.Thread;
+import jenkins.security.MasterToSlaveCallable;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+import net.sf.json.JSONSerializer;
+
+
+public class ParallelsDesktopConnectorSlaveComputer extends SlaveComputer
+{
+	private static final Logger LOGGER = Logger.getLogger("PDConnectorSlaveComputer");
+
+	public ParallelsDesktopConnectorSlaveComputer(Slave slave)
+	{
+		super(slave);
+	}
+
+	public boolean checkVmExists(String vmId)
+	{
+		try
+		{
+			RunVmCallable command = new RunVmCallable("list", "-i", "--json");
+			Channel channel = getChannel();
+			if (channel == null)
+			{
+				LOGGER.log(Level.SEVERE, "Connecting to node");
+				connect(false).get();
+			}
+			channel = getChannel();
+			String callResult = channel.call(command);
+			JSONArray vms = (JSONArray)JSONSerializer.toJSON(callResult);
+			for (int i = 0; i < vms.size(); i++)
+			{
+				JSONObject vmInfo = vms.getJSONObject(i);
+				if (vmId.equals(vmInfo.getString("ID")) || vmId.equals(vmInfo.getString("Name")))
+					return true;
+			}
+			return true;
+		}
+		catch (IOException ex)
+		{
+			LOGGER.log(Level.SEVERE, ex.toString());
+		}
+		catch (InterruptedException ex)
+		{
+			LOGGER.log(Level.SEVERE, ex.toString());
+		}
+		catch (ExecutionException ex)
+		{
+			LOGGER.log(Level.SEVERE, ex.toString());
+		}
+		return false;
+	}
+
+	private String getVmIPAddress(String vmId) throws Exception
+	{
+		int TIMEOUT = 60;
+		for (int i = 0; i < TIMEOUT; ++i)
+		{
+			RunVmCallable command = new RunVmCallable("list", "-f", "--json", vmId);
+			String callResult = getChannel().call(command);
+			LOGGER.log(Level.SEVERE, " - (" + i + "/" + TIMEOUT + ") calling for IP");
+			LOGGER.log(Level.SEVERE, callResult);
+			JSONArray vms = (JSONArray)JSONSerializer.toJSON(callResult);
+			JSONObject vmInfo = vms.getJSONObject(0);
+			String ip = vmInfo.getString("ip_configured");
+			if (!ip.equals("-"))
+				return ip;
+			Thread.sleep(1000);
+		}
+		throw new Exception("Failed to get IP for VM '" + vmId + "'");
+	}
+
+	public Node createSlaveOnVM(ParallelsDesktopVM vm) throws Exception
+	{
+		String vmId = vm.getVmid();
+		String slaveName = vm.getSlaveName();
+		LOGGER.log(Level.SEVERE, "Starting slave '" + slaveName+ "'");
+		LOGGER.log(Level.SEVERE, "Starting virtual machine '" + vmId + "'");
+		RunVmCallable command = new RunVmCallable("start", vmId);
+		String callResult = getChannel().call(command);
+		LOGGER.log(Level.SEVERE, "Waiting for IP...");
+		String ip = getVmIPAddress(vmId);
+		LOGGER.log(Level.SEVERE, "Got IP address for VM " + vmId + ": " + ip);
+		try
+		{
+			Class<?> c = vm.getLauncher().getClass();
+			Field f = c.getDeclaredField("host");
+			f.setAccessible(true);
+			f.set(vm.getLauncher(), ip);
+			f.setAccessible(false);
+		}
+		catch (NoSuchFieldException x)
+		{
+			LOGGER.log(Level.SEVERE, "No 'host' field in launcher of " + slaveName);
+		}
+		return new ParallelsDesktopVMSlave(vm, this);
+	}
+
+	public void stopVM(String vmId)
+	{
+		try
+		{
+			LOGGER.log(Level.SEVERE, "Suspending...");
+			RunVmCallable command = new RunVmCallable("suspend", vmId);
+			String res = getChannel().call(command);
+			LOGGER.log(Level.SEVERE, res);
+		}
+		catch (Exception ex)
+		{
+			LOGGER.log(Level.SEVERE, ex.toString());
+		}
+	}
+
+	private static final class RunVmCallable extends MasterToSlaveCallable<String, IOException>
+	{
+		private static final String cmd = "/usr/local/bin/prlctl";
+		private final String[] params;
+		
+		public RunVmCallable(String... params)
+		{
+			this.params = params;
+		}
+
+		@Override
+		public String call() throws IOException
+		{
+			List<String> cmds = new ArrayList<String>();
+			cmds.add(cmd);
+			cmds.addAll(Arrays.asList(this.params));
+			
+			LOGGER.log(Level.SEVERE, "Running command:");
+			for (String s: cmds)
+				LOGGER.log(Level.SEVERE, " [" + s + "]");
+			ProcessBuilder pb = new ProcessBuilder(cmds);
+			pb.redirectErrorStream(true);
+			Process pr = pb.start();
+			BufferedReader in = new BufferedReader(new InputStreamReader(pr.getInputStream()));
+			String line;
+			String result = "";
+			while ((line = in.readLine()) != null) 
+			{
+				result += line;
+			}
+			try
+			{
+				pr.waitFor();
+			}
+			catch (InterruptedException ex)
+			{
+				LOGGER.log(Level.SEVERE, ex.toString());
+			}
+			return result;
+		}
+	}
+}
diff --git a/src/main/java/com/parallels/desktopcould/ParallelsDesktopVM.java b/src/main/java/com/parallels/desktopcould/ParallelsDesktopVM.java
new file mode 100644
index 0000000..c253593
--- /dev/null
+++ b/src/main/java/com/parallels/desktopcould/ParallelsDesktopVM.java
@@ -0,0 +1,97 @@
+/*
+ * The MIT License
+ *
+ * (c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.parallels.desktopcloud;
+
+import hudson.Extension;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+import hudson.slaves.ComputerLauncher;
+import jenkins.model.Jenkins;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+
+public class ParallelsDesktopVM implements Describable<ParallelsDesktopVM>
+{
+	private String vmid;
+	private String labels;
+	private String remoteFS;
+	private String slaveName;
+	private ComputerLauncher launcher;
+
+	@DataBoundConstructor
+	public ParallelsDesktopVM(String vmid, String labels, String remoteFS, ComputerLauncher launcher)
+	{
+		this.vmid = vmid;
+		this.labels = labels;
+		this.remoteFS = remoteFS;
+		this.launcher = launcher;
+	}
+
+	public String getVmid()
+	{
+		return vmid;
+	}
+	
+	public String getLabels()
+	{
+		return labels;
+	}
+
+	public String getRemoteFS()
+	{
+		return remoteFS;
+	}
+
+	public ComputerLauncher getLauncher()
+	{
+		return launcher;
+	}
+
+	public void setSlaveName(String slaveName)
+	{
+		this.slaveName = slaveName;
+	}
+
+	public String getSlaveName()
+	{
+		return slaveName;
+	}
+
+	@Override
+	public Descriptor<ParallelsDesktopVM> getDescriptor()
+	{
+		return Jenkins.getInstance().getDescriptor(getClass());
+	}
+
+	@Extension
+	public static final class DescriptorImpl extends Descriptor<ParallelsDesktopVM>
+	{
+		@Override
+		public String getDisplayName()
+		{
+			return "Parallels Desktop virtual machine";
+		}
+	}
+}
diff --git a/src/main/java/com/parallels/desktopcould/ParallelsDesktopVMSlave.java b/src/main/java/com/parallels/desktopcould/ParallelsDesktopVMSlave.java
new file mode 100644
index 0000000..ba40250
--- /dev/null
+++ b/src/main/java/com/parallels/desktopcould/ParallelsDesktopVMSlave.java
@@ -0,0 +1,77 @@
+/*
+ * The MIT License
+ *
+ * (c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.parallels.desktopcloud;
+
+import hudson.model.Descriptor;
+import hudson.model.Slave;
+import hudson.model.Node.Mode;
+import hudson.model.Computer;
+import hudson.slaves.NodeProperty;
+import hudson.Extension;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+
+public class ParallelsDesktopVMSlave extends Slave
+{
+	private static final Logger LOGGER = Logger.getLogger("PDVMSlave");
+	private final transient ParallelsDesktopConnectorSlaveComputer connector;
+	private final ParallelsDesktopVM vm;
+
+	@DataBoundConstructor
+	public ParallelsDesktopVMSlave(ParallelsDesktopVM vm, ParallelsDesktopConnectorSlaveComputer connector)
+			throws IOException, Descriptor.FormException
+	{
+		super(vm.getSlaveName(), "", vm.getRemoteFS(), 1, Mode.NORMAL, vm.getLabels(), vm.getLauncher(),
+				new ParallelsDesktopCloudRetentionStrategy(), new ArrayList<NodeProperty<?>>());
+		this.connector = connector;
+		this.vm = vm;
+	}
+
+	@Override
+	public Computer createComputer()
+	{
+		return new ParallelsDesktopVMSlaveComputer(this);
+	}
+
+	public void stop()
+	{
+		LOGGER.log(Level.SEVERE, "!!! Stop node '" + getNodeName() + "', id '" + vm.getVmid() + "'");
+		connector.stopVM(vm.getVmid());
+	}
+
+	@Extension
+	public static final class DescriptorImpl extends SlaveDescriptor
+	{
+		@Override
+		public String getDisplayName()
+		{
+			return "Parallels Desktop VM slave";
+		}
+	}
+}
diff --git a/src/main/java/com/parallels/desktopcould/ParallelsDesktopVMSlaveComputer.java b/src/main/java/com/parallels/desktopcould/ParallelsDesktopVMSlaveComputer.java
new file mode 100644
index 0000000..8901702
--- /dev/null
+++ b/src/main/java/com/parallels/desktopcould/ParallelsDesktopVMSlaveComputer.java
@@ -0,0 +1,51 @@
+/*
+ * The MIT License
+ *
+ * (c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.parallels.desktopcloud;
+
+import hudson.model.Slave;
+import hudson.slaves.SlaveComputer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+public class ParallelsDesktopVMSlaveComputer extends SlaveComputer
+{
+	private static final Logger LOGGER = Logger.getLogger("PDVMSlaveComputer");
+
+	public ParallelsDesktopVMSlaveComputer(Slave slave)
+	{
+		super(slave);
+	}
+
+	public ParallelsDesktopVMSlave getNode()
+	{
+		return (ParallelsDesktopVMSlave)super.getNode();
+	}
+
+	protected void onRemoved()
+	{
+		LOGGER.log(Level.SEVERE, "!!!!!! ON REMOVED");
+	}
+}
diff --git a/src/main/resources/com/parallels/desktopcloud/ParallelsDesktopCloud/config.jelly b/src/main/resources/com/parallels/desktopcloud/ParallelsDesktopCloud/config.jelly
new file mode 100644
index 0000000..f9acbd5
--- /dev/null
+++ b/src/main/resources/com/parallels/desktopcloud/ParallelsDesktopCloud/config.jelly
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+The MIT License
+
+(c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
+		xmlns:t="/lib/hudson" xmlns:f="/lib/form">
+	<f:entry field="name" title="Name">
+		<f:textbox default="Parallels Desktop Cloud"/>
+	</f:entry>
+	<f:entry title="${%Remote FS root}" field="remoteFS">
+		<f:textbox/>
+	</f:entry>
+
+	<f:dropdownList name="pdLauncher" title="${%Secondary launch method}"
+			help="${descriptor.getHelpFile('launcher')}">
+		<j:forEach var="d" items="${h.getComputerLauncherDescriptors()}" varStatus="loop">
+			<f:dropdownListBlock value="${d.clazz.name}" name="${d.displayName}"
+					selected="${it.delegateLauncher.descriptor==d}"
+					title="${d.displayName}">
+				<j:set var="descriptor" value="${d}"/>
+				<j:set var="instance"
+					value="${it.delegateLauncher.descriptor==d ? it.delegateLauncher : null}"/>
+					<tr>
+						<td>
+							<input type="hidden" name="stapler-class" value="${d.clazz.name}"/>
+						</td>
+					</tr>
+				<st:include from="${d}" page="${d.configPage}" optional="true"/>
+			</f:dropdownListBlock>
+		</j:forEach>
+	</f:dropdownList>
+
+	<f:entry title="${%Virtual Machines}" description="${%List of virtual machines to be launched as slaves}">
+		<f:repeatable field="vms">
+			<div>
+				<table width="100%">
+					<st:include page="config.jelly" from="${descriptor}"
+						class="com.parallels.desktopcloud.ParallelsDesktopVM" />
+				</table>
+			</div>
+			<div align="right">
+				<f:repeatableDeleteButton/>
+			</div>
+		</f:repeatable>
+	</f:entry>
+</j:jelly>
diff --git a/src/main/resources/com/parallels/desktopcloud/ParallelsDesktopSlave/configure-entries.jelly b/src/main/resources/com/parallels/desktopcloud/ParallelsDesktopSlave/configure-entries.jelly
new file mode 100644
index 0000000..7a0912a
--- /dev/null
+++ b/src/main/resources/com/parallels/desktopcloud/ParallelsDesktopSlave/configure-entries.jelly
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+The MIT License
+
+(c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
+	xmlns:t="/lib/hudson" xmlns:f="/lib/form">
+
+	<f:entry title="${%Description}" help="/help/system-config/master-slave/description.html">
+		<f:textbox field="nodeDescription"/>
+	</f:entry>
+
+	<f:entry title="${%Remote FS root}" field="remoteFS">
+		<f:textbox/>
+	</f:entry>
+
+	<f:dropdownList name="slave.delegateLauncher" title="${%Secondary launch method}"
+		help="${descriptor.getHelpFile('launcher')}">
+		<j:forEach var="d" items="${h.getComputerLauncherDescriptors()}" varStatus="loop">
+			<f:dropdownListBlock value="${d.clazz.name}" name="${d.displayName}"
+					selected="${it.delegateLauncher.descriptor==d}"
+					title="${d.displayName}">
+				<j:set var="descriptor" value="${d}"/>
+				<j:set var="instance"
+					value="${it.delegateLauncher.descriptor==d ? it.delegateLauncher : null}"/>
+				<tr>
+					<td>
+						<input type="hidden" name="stapler-class" value="${d.clazz.name}"/>
+					</td>
+				</tr>
+				<st:include from="${d}" page="${d.configPage}" optional="true"/>
+			</f:dropdownListBlock>
+		</j:forEach>
+	</f:dropdownList>
+
+	<!-- pointless to show this if there's only one option, which is the default -->
+	<j:if test="${h.getRetentionStrategyDescriptors().size() gt 1}">
+		<f:dropdownList name="slave.retentionStrategy" title="${%Availability}"
+				help="/help/system-config/master-slave/availability.html">
+			<j:forEach var="d" items="${h.getRetentionStrategyDescriptors()}">
+				<j:if test="${d != null}">
+					<f:dropdownListBlock value="${d.clazz.name}" name="${d.displayName}"
+							selected="${it.retentionStrategy.descriptor==d}"
+							title="${d.displayName}">
+						<j:set var="descriptor" value="${d}"/>
+						<j:set var="instance"
+							value="${it.retentionStrategy.descriptor==d ? it.retentionStrategy : null}"/>
+						<tr>
+							<td>
+								<input type="hidden" name="stapler-class" value="${d.clazz.name}"/>
+							</td>
+						</tr>
+						<st:include from="${d}" page="${d.configPage}" optional="true"/>
+					</f:dropdownListBlock>
+				</j:if>
+			</j:forEach>
+		</f:dropdownList>
+	</j:if>
+
+	<f:entry title="${%Virtual Machines}" description="${%List of virtual machines to be launched as slaves}">
+		<f:repeatable field="vms">
+			<div>
+				<table width="100%">
+					<st:include page="config.jelly" from="${descriptor}"
+						class="com.parallels.desktopcloud.ParallelsDesktopVM" />
+				</table>
+			</div>
+			<div align="right"><f:repeatableDeleteButton/></div>
+		</f:repeatable>
+	</f:entry>
+
+	<f:descriptorList title="${%Node Properties}" descriptors="${h.getNodePropertyDescriptors(descriptor.clazz)}"
+		field="nodeProperties"/>
+</j:jelly>
diff --git a/src/main/resources/com/parallels/desktopcloud/ParallelsDesktopVM/config.jelly b/src/main/resources/com/parallels/desktopcloud/ParallelsDesktopVM/config.jelly
new file mode 100644
index 0000000..68a4ebb
--- /dev/null
+++ b/src/main/resources/com/parallels/desktopcloud/ParallelsDesktopVM/config.jelly
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+The MIT License
+
+(c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
+	xmlns:t="/lib/hudson" xmlns:f="/lib/form" >
+
+	<f:entry title="${%Virtual Machine ID}" field="vmid">
+		<f:textbox />
+	</f:entry>
+
+	<f:entry title="${%Labels}" field="labels">
+		<f:textbox />
+	</f:entry>
+
+	<f:entry title="${%Remote FS root}" field="remoteFS">
+		<f:textbox/>
+	</f:entry>
+
+	<f:dropdownList name="launcher" title="${%Secondary launch method}"
+			help="${descriptor.getHelpFile('launcher')}">
+		<j:forEach var="d" items="${h.getComputerLauncherDescriptors()}" varStatus="loop">
+			<f:dropdownListBlock value="${d.clazz.name}" name="${d.displayName}"
+					selected="${it.delegateLauncher.descriptor==d}"
+					title="${d.displayName}">
+				<j:set var="descriptor" value="${d}"/>
+				<j:set var="instance"
+					value="${it.delegateLauncher.descriptor==d ? it.delegateLauncher : null}"/>
+				<tr>
+					<td>
+						<input type="hidden" name="stapler-class" value="${d.clazz.name}"/>
+					</td>
+				</tr>
+				<st:include from="${d}" page="${d.configPage}" optional="true"/>
+			</f:dropdownListBlock>
+		</j:forEach>
+	</f:dropdownList>
+</j:jelly>
diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly
new file mode 100644
index 0000000..67fe208
--- /dev/null
+++ b/src/main/resources/index.jelly
@@ -0,0 +1,27 @@
+<!--
+The MIT License
+
+(c) 2004-2015. Parallels IP Holdings GmbH. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<div>
+	Parallels Desktop Cloud plugin
+</div>