4 commits - backend/server branding/img client/tools java/buildconf java/code java/ivy.xml java/spacewalk-java.spec schema/spacewalk web/html

Miroslav Suchý msuchy at fedoraproject.org
Mon Jun 4 18:04:54 UTC 2012


 backend/server/action/Makefile                                                                                  |    2 
 backend/server/action/image.py                                                                                  |   50 +
 backend/server/action_extra_data/Makefile                                                                       |    2 
 backend/server/action_extra_data/image.py                                                                       |   27 
 branding/img/spinner.gif                                                                                        |binary
 client/tools/rhn-virtualization/actions/image.py                                                                |  273 ++++++++++
 client/tools/rhn-virtualization/rhn-virtualization.spec                                                         |   13 
 client/tools/rhn-virtualization/scripts/Makefile                                                                |    6 
 client/tools/rhn-virtualization/scripts/image.cfg                                                               |    4 
 client/tools/rhn-virtualization/scripts/studio-kvm-template.xml                                                 |   41 +
 client/tools/rhn-virtualization/scripts/studio-xen-template.xml                                                 |   33 +
 client/tools/rhn-virtualization/virtualization/util.py                                                          |    7 
 java/buildconf/build-props.xml                                                                                  |    4 
 java/code/src/com/redhat/rhn/domain/action/ActionFactory.java                                                   |   10 
 java/code/src/com/redhat/rhn/domain/action/Action_legacyUser.hbm.xml                                            |    6 
 java/code/src/com/redhat/rhn/domain/action/image/DeployImageAction.java                                         |   43 +
 java/code/src/com/redhat/rhn/domain/action/image/DeployImageActionDetails.hbm.xml                               |   25 
 java/code/src/com/redhat/rhn/domain/action/image/DeployImageActionDetails.java                                  |  161 +++++
 java/code/src/com/redhat/rhn/domain/credentials/Credentials.hbm.xml                                             |   25 
 java/code/src/com/redhat/rhn/domain/credentials/Credentials.java                                                |  186 ++++++
 java/code/src/com/redhat/rhn/domain/credentials/CredentialsFactory.java                                         |  124 ++++
 java/code/src/com/redhat/rhn/domain/credentials/CredentialsType.hbm.xml                                         |   21 
 java/code/src/com/redhat/rhn/domain/credentials/CredentialsType.java                                            |   93 +++
 java/code/src/com/redhat/rhn/domain/image/Image.java                                                            |  244 ++++++++
 java/code/src/com/redhat/rhn/domain/image/ProxyConfig.java                                                      |   85 +++
 java/code/src/com/redhat/rhn/frontend/action/renderers/ImagesRenderer.java                                      |  148 +++++
 java/code/src/com/redhat/rhn/frontend/action/systems/images/ScheduleImageDeploymentAction.java                  |  156 +++++
 java/code/src/com/redhat/rhn/frontend/action/user/UserCredentialsDeleteAction.java                              |   66 ++
 java/code/src/com/redhat/rhn/frontend/action/user/UserCredentialsEditAction.java                                |   98 +++
 java/code/src/com/redhat/rhn/frontend/strings/java/StringResource_en_US.xml                                     |   19 
 java/code/src/com/redhat/rhn/frontend/strings/jsp/StringResource_en_US.xml                                      |   89 +++
 java/code/src/com/redhat/rhn/frontend/strings/nav/StringResource_en_US.xml                                      |    7 
 java/code/src/com/redhat/rhn/frontend/taglibs/RhnTagFunctions.java                                              |   10 
 java/code/src/com/redhat/rhn/frontend/taglibs/rhn-taglib.tld                                                    |    5 
 java/code/src/com/redhat/rhn/frontend/xmlrpc/activationkey/ActivationKeyHandler.java                            |    6 
 java/code/src/com/redhat/rhn/manager/action/ActionManager.java                                                  |   43 +
 java/code/webapp/WEB-INF/dwr.xml                                                                                |    6 
 java/code/webapp/WEB-INF/nav/sitenav-authenticated.xml                                                          |    4 
 java/code/webapp/WEB-INF/nav/system_detail.xml                                                                  |    1 
 java/code/webapp/WEB-INF/pages/common/fragments/scheduledactions/listdisplay-new.jspf                           |    2 
 java/code/webapp/WEB-INF/pages/common/fragments/yourrhn/scheduledActions.jsp                                    |    2 
 java/code/webapp/WEB-INF/pages/credentials/delete.jsp                                                           |   49 +
 java/code/webapp/WEB-INF/pages/credentials/edit.jsp                                                             |   50 +
 java/code/webapp/WEB-INF/pages/schedule/archivedactions.jsp                                                     |    2 
 java/code/webapp/WEB-INF/pages/schedule/pendingactionsconfirm.jsp                                               |    2 
 java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images-content.jspf                        |   94 +++
 java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images-render-async.jsp                    |   17 
 java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images.jsp                                 |   38 +
 java/code/webapp/WEB-INF/struts-config.xml                                                                      |   50 +
 java/ivy.xml                                                                                                    |    2 
 java/spacewalk-java.spec                                                                                        |    6 
 schema/spacewalk/common/data/rhnActionType.sql                                                                  |    1 
 schema/spacewalk/common/data/suseCredentialsType.sql                                                            |   18 
 schema/spacewalk/common/tables/rhnActionImageDeploy.sql                                                         |   34 +
 schema/spacewalk/common/tables/suseCredentials.sql                                                              |   36 +
 schema/spacewalk/common/tables/suseCredentialsType.sql                                                          |   38 +
 schema/spacewalk/common/tables/tables.deps                                                                      |    2 
 schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/100-rhnActionImageDeploy_create.sql       |   34 +
 schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/101-rhnActionType_image-deploy_insert.sql |   17 
 schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/102-suseCredentialsType_create.sql        |   38 +
 schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/103-suseCredentialsType_insert.sql        |   18 
 schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/104-suseCredentials_create.sql            |   36 +
 web/html/javascript/images.js                                                                                   |   24 
 63 files changed, 2742 insertions(+), 11 deletions(-)

New commits:
commit 3e52965ffd53d33f8b851d8ab93a36f590774172
Author: Johannes Renner <jrenner at suse.de>
Date:   Fri Mar 30 15:58:25 2012 +0200

    Add support for studio image deployments (web UI)

diff --git a/branding/img/spinner.gif b/branding/img/spinner.gif
new file mode 100644
index 0000000..276dd5a
Binary files /dev/null and b/branding/img/spinner.gif differ
diff --git a/java/buildconf/build-props.xml b/java/buildconf/build-props.xml
index 024f387..cce588e 100644
--- a/java/buildconf/build-props.xml
+++ b/java/buildconf/build-props.xml
@@ -117,7 +117,7 @@
              log4j oro redstone-xmlrpc-client redstone-xmlrpc ${struts-jars}
              jcommon stringtree-json postgresql-jdbc
              ${jfreechart} taglibs-core quartz
-             concurrent simple-core" />
+             concurrent simple-core simple-xml susestudio-java-client" />
 
   <property name="dist.jar.dependencies"
       value="antlr ${asm} bcel c3p0 cglib
@@ -128,7 +128,7 @@
              jaf ${jasper-jars} javamail jcommon jdom ${other-jars}
              ${jfreechart} ${tomcat-jars} jpam log4j redstone-xmlrpc-client redstone-xmlrpc
              oro oscache quartz stringtree-json sitemesh ${struts-jars} taglibs-core
-             taglibs-standard tanukiwrapper postgresql-jdbc
+             taglibs-standard tanukiwrapper postgresql-jdbc simple-xml susestudio-java-client
              xalan-j2 xerces-j2 xml-commons-apis simple-core" />
 
   <path id="bootjars">
diff --git a/java/code/src/com/redhat/rhn/domain/action/ActionFactory.java b/java/code/src/com/redhat/rhn/domain/action/ActionFactory.java
index 0d43cf5..950dbb7 100644
--- a/java/code/src/com/redhat/rhn/domain/action/ActionFactory.java
+++ b/java/code/src/com/redhat/rhn/domain/action/ActionFactory.java
@@ -25,6 +25,7 @@ import com.redhat.rhn.domain.action.config.ConfigUploadAction;
 import com.redhat.rhn.domain.action.config.ConfigUploadMtimeAction;
 import com.redhat.rhn.domain.action.config.DaemonConfigAction;
 import com.redhat.rhn.domain.action.errata.ErrataAction;
+import com.redhat.rhn.domain.action.image.DeployImageAction;
 import com.redhat.rhn.domain.action.kickstart.KickstartGuestToolsChannelSubscriptionAction;
 import com.redhat.rhn.domain.action.kickstart.KickstartHostToolsChannelSubscriptionAction;
 import com.redhat.rhn.domain.action.kickstart.KickstartInitiateAction;
@@ -367,6 +368,9 @@ public class ActionFactory extends HibernateFactory {
         else if (typeIn.equals(TYPE_SCAP_XCCDF_EVAL)) {
             retval = new ScapAction();
         }
+        else if (typeIn.equals(TYPE_DEPLOY_IMAGE)) {
+            retval = new DeployImageAction();
+        }
 
         else {
             retval = new Action();
@@ -992,5 +996,11 @@ public class ActionFactory extends HibernateFactory {
     public static final String TXN_OPERATION_INSERT = "insert";
     public static final String TXN_OPERATION_DELETE = "delete";
 
+    /**
+     * The constant representing Image deploy action.  [ID:500]
+     */
+    public static final ActionType TYPE_DEPLOY_IMAGE =
+            lookupActionTypeByLabel("image.deploy");
+
 }
 
diff --git a/java/code/src/com/redhat/rhn/domain/action/Action_legacyUser.hbm.xml b/java/code/src/com/redhat/rhn/domain/action/Action_legacyUser.hbm.xml
index da20e88..27bbeb4 100644
--- a/java/code/src/com/redhat/rhn/domain/action/Action_legacyUser.hbm.xml
+++ b/java/code/src/com/redhat/rhn/domain/action/Action_legacyUser.hbm.xml
@@ -52,6 +52,12 @@ PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 					class="com.redhat.rhn.domain.errata.impl.PublishedErrata" />
 			</set>
 		</subclass>
+        <subclass name="com.redhat.rhn.domain.action.image.DeployImageAction"
+            lazy="true" discriminator-value="500">
+            <one-to-one name="details"
+                class="com.redhat.rhn.domain.action.image.DeployImageActionDetails"
+                outer-join="false" cascade="all" constrained="false" property-ref="parentAction"/>
+        </subclass>
 		<subclass
 				name="com.redhat.rhn.domain.action.Up2DateConfigGetAction"
 				discriminator-value="6" lazy="true">
diff --git a/java/code/src/com/redhat/rhn/domain/action/image/DeployImageAction.java b/java/code/src/com/redhat/rhn/domain/action/image/DeployImageAction.java
new file mode 100644
index 0000000..4e7a1ef
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/action/image/DeployImageAction.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+
+package com.redhat.rhn.domain.action.image;
+
+import com.redhat.rhn.domain.action.Action;
+
+/**
+ * DeployImageAction - Class representation of image deployment action
+ */
+public class DeployImageAction extends Action {
+
+    private static final long serialVersionUID = 1438261396065921002L;
+    private DeployImageActionDetails details;
+
+    /**
+     * Return the details.
+     * @return details
+     */
+    public DeployImageActionDetails getDetails() {
+        return details;
+    }
+
+    /**
+     * Set the details.
+     * @param detailsIn details
+     */
+    public void setDetails(DeployImageActionDetails detailsIn) {
+        this.details = detailsIn;
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/domain/action/image/DeployImageActionDetails.hbm.xml b/java/code/src/com/redhat/rhn/domain/action/image/DeployImageActionDetails.hbm.xml
new file mode 100644
index 0000000..35822e8
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/action/image/DeployImageActionDetails.hbm.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping
+PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping>
+  <class name="com.redhat.rhn.domain.action.image.DeployImageActionDetails"
+         table="rhnActionImageDeploy">
+    <id name="id" type="long" column="id">
+      <meta attribute="scope-set">protected</meta>
+      <generator class="sequence">
+        <param name="sequence">RHN_ACTION_IMAGE_DEPLOY_ID_SEQ</param>
+      </generator>
+    </id>
+    <many-to-one name="parentAction" column="action_id"
+                 class="com.redhat.rhn.domain.action.Action" outer-join="true"
+                 not-null="true" insert="true" update="false" />
+    <property name="memKb" type="long" column="mem_kb" />
+    <property name="vcpus" type="long" column="vcpus" />
+    <property name="bridgeDevice" type="string" column="bridge_device" />
+    <property name="downloadUrl" type="string" column="download_url" />
+    <property name="proxyServer" type="string" column="proxy_server" />
+    <property name="proxyUser" type="string" column="proxy_user" />
+    <property name="proxyPass" type="string" column="proxy_pass" />
+  </class>
+</hibernate-mapping>
diff --git a/java/code/src/com/redhat/rhn/domain/action/image/DeployImageActionDetails.java b/java/code/src/com/redhat/rhn/domain/action/image/DeployImageActionDetails.java
new file mode 100644
index 0000000..7b5945f
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/action/image/DeployImageActionDetails.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright (c) 2011 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+package com.redhat.rhn.domain.action.image;
+
+import com.redhat.rhn.domain.action.ActionChild;
+
+/**
+ * DeployImageActionDetails - Class representation of the table rhnActionImageDeploy.
+ * @version $Rev$
+ */
+public class DeployImageActionDetails extends ActionChild {
+
+    private Long id;
+    private Long vcpus;
+    private Long memKb;
+    private String bridgeDevice;
+    private String downloadUrl;
+    private String proxyServer;
+    private String proxyUser;
+    private String proxyPass;
+
+    /**
+     * Return the ID.
+     * @return id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Set the ID.
+     * @param idIn id
+     */
+    public void setId(Long idIn) {
+        this.id = idIn;
+    }
+
+    /**
+     * Return the number of virtual CPUs.
+     * @return vcpus
+     */
+    public Long getVcpus() {
+        return vcpus;
+    }
+
+    /**
+     * Set the number of virtual CPUs.
+     * @param vcpusIn vcpus
+     */
+    public void setVcpus(Long vcpusIn) {
+        this.vcpus = vcpusIn;
+    }
+
+    /**
+     * Return the amount of memory in KB.
+     * @return memKb
+     */
+    public Long getMemKb() {
+        return memKb;
+    }
+
+    /**
+     * Set the amount of memory in KB.
+     * @param memkb memory in KB
+     */
+    public void setMemKb(Long memkb) {
+        this.memKb = memkb;
+    }
+
+    /**
+     * Return the bridge device.
+     * @return bridgeDevice
+     */
+    public String getBridgeDevice() {
+        return bridgeDevice;
+    }
+
+    /**
+     * Set the bridge device.
+     * @param bridgeDeviceIn bridge device
+     */
+    public void setBridgeDevice(String bridgeDeviceIn) {
+        this.bridgeDevice = bridgeDeviceIn;
+    }
+
+    /**
+     * Set the download URL.
+     * @return downloadUrl
+     */
+    public String getDownloadUrl() {
+        return downloadUrl;
+    }
+
+    /**
+     * Return the download URL.
+     * @param downloadUrlIn download URL
+     */
+    public void setDownloadUrl(String downloadUrlIn) {
+        this.downloadUrl = downloadUrlIn;
+    }
+
+    /**
+     * Return the proxy server.
+     * @return proxyServer
+     */
+    public String getProxyServer() {
+        return proxyServer;
+    }
+
+    /**
+     * Set the proxy server.
+     * @param proxyServerIn proxy server
+     */
+    public void setProxyServer(String proxyServerIn) {
+        this.proxyServer = proxyServerIn;
+    }
+
+    /**
+     * Return the proxy user.s
+     * @return proxyUser
+     */
+    public String getProxyUser() {
+        return proxyUser;
+    }
+
+    /**
+     * Set the proxy user.
+     * @param proxyUserIn proxy user
+     */
+    public void setProxyUser(String proxyUserIn) {
+        this.proxyUser = proxyUserIn;
+    }
+
+    /**
+     * Return the proxy password.
+     * @return proxyPass
+     */
+    public String getProxyPass() {
+        return proxyPass;
+    }
+
+    /**
+     * Set the proxy password.
+     * @param proxyPassIn proxy password
+     */
+    public void setProxyPass(String proxyPassIn) {
+        this.proxyPass = proxyPassIn;
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/domain/credentials/Credentials.hbm.xml b/java/code/src/com/redhat/rhn/domain/credentials/Credentials.hbm.xml
new file mode 100644
index 0000000..6429730
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/credentials/Credentials.hbm.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping
+PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping>
+  <class name="com.redhat.rhn.domain.credentials.Credentials"
+         table="SUSECREDENTIALS">
+    <id name="id" column="id" type="long">
+      <generator class="sequence">
+        <param name="sequence">suse_credentials_id_seq</param>
+      </generator>
+    </id>
+    <many-to-one name="user" column="user_id" class="com.redhat.rhn.domain.user.legacy.UserImpl" />
+    <many-to-one name="type" column="type_id" class="com.redhat.rhn.domain.credentials.CredentialsType" />
+    <property name="url" column="url" type="string" />
+    <property name="username" column="username" type="string" />
+    <property name="encodedPassword" column="password" type="string" />
+    <property name="created" column="created" type="timestamp" insert="false" update="false" />
+    <property name="modified" column="modified" type="timestamp" insert="false" update="false" />
+  </class>
+  <query name="Credentials.findByUserAndTypeLabel">
+    <![CDATA[from com.redhat.rhn.domain.credentials.Credentials as c
+             where c.user = :user and c.type.label = :label]]>
+  </query>
+</hibernate-mapping>
diff --git a/java/code/src/com/redhat/rhn/domain/credentials/Credentials.java b/java/code/src/com/redhat/rhn/domain/credentials/Credentials.java
new file mode 100644
index 0000000..259dbe6
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/credentials/Credentials.java
@@ -0,0 +1,186 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+
+package com.redhat.rhn.domain.credentials;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+
+import com.redhat.rhn.domain.BaseDomainHelper;
+import com.redhat.rhn.domain.user.User;
+
+/**
+ * Credentials - Java representation of the table SUSECREDENTIALS.
+ *
+ * This table contains pairs of credentials used for communicating
+ * with 3rd party systems, e.g. API usernames and keys.
+ */
+public class Credentials extends BaseDomainHelper {
+
+    // Available type labels
+    public static final String TYPE_SUSESTUDIO = "susestudio";
+
+    private Long id;
+    private User user;
+    private CredentialsType type;
+    private String url;
+    private String username;
+    private String encodedPassword;
+
+    /**
+     * Get the ID of this object.
+     * @return id
+     */
+    public Long getId() {
+        return this.id;
+    }
+
+    /**
+     * Set the ID of this object.
+     * @param idIn id
+     */
+    public void setId(Long idIn) {
+        this.id = idIn;
+    }
+
+    /**
+     * Get the associated {@link User}.
+     * @return user
+     */
+    public User getUser() {
+        return this.user;
+    }
+
+    /**
+     * Set the associated {@link User}.
+     * @param userIn user
+     */
+    public void setUser(User userIn) {
+        this.user = userIn;
+    }
+
+    /**
+     * Return the type.
+     * @return type
+     */
+    public CredentialsType getType() {
+        return type;
+    }
+
+    /**
+     * Set the type.
+     * @param typeIn type
+     */
+    public void setType(CredentialsType typeIn) {
+        this.type = typeIn;
+    }
+
+    /**
+     * Return the URL.
+     * @return url
+     */
+    public String getUrl() {
+        return url;
+    }
+
+    /**
+     * Set the url.
+     * @param urlIn url
+     */
+    public void setUrl(String urlIn) {
+        this.url = urlIn;
+    }
+
+    /**
+     * Return the username
+     * @return username
+     */
+    public String getUsername() {
+        return username;
+    }
+
+    /**
+     * Set the username.
+     * @param usernameIn username
+     */
+    public void setUsername(String usernameIn) {
+        this.username = usernameIn;
+    }
+
+    /**
+     * Return the encoded password.
+     * @return the password
+     */
+    public String getEncodedPassword() {
+        return encodedPassword;
+    }
+
+    /**
+     * Set the password.
+     * @param password the password to set
+     */
+    public void setEncodedPassword(String password) {
+        this.encodedPassword = password;
+    }
+
+    /**
+     * Return the decoded password.
+     * @return the password
+     */
+    public String getPassword() {
+        if (this.encodedPassword != null) {
+            return new String(Base64.decodeBase64(this.encodedPassword.getBytes()));
+        }
+        else {
+            return this.encodedPassword;
+        }
+    }
+
+    /**
+     * Set the password after encoding it to Base64.
+     * @param password the password to set
+     */
+    public void setPassword(String password) {
+        if (password != null) {
+            this.encodedPassword = new String(Base64.encodeBase64(password.getBytes()));
+        }
+        else {
+            this.encodedPassword = null;
+        }
+    }
+
+    /**
+     * Credentials are considered as valid as soon as we have a user and a
+     * password.
+     *
+     * @return true if we have a user and a password, else false
+     */
+    public boolean isComplete() {
+        return !StringUtils.isEmpty(username) &&
+                !StringUtils.isEmpty(encodedPassword);
+    }
+
+    /**
+     * Check if these credentials are empty regarding username, password and
+     * url.
+     *
+     * @return true if we have a user and a password, else false
+     */
+    public boolean isEmpty() {
+        return StringUtils.isEmpty(username) &&
+                StringUtils.isEmpty(encodedPassword) &&
+                StringUtils.isEmpty(url);
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/domain/credentials/CredentialsFactory.java b/java/code/src/com/redhat/rhn/domain/credentials/CredentialsFactory.java
new file mode 100644
index 0000000..06c5c6e
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/credentials/CredentialsFactory.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+
+package com.redhat.rhn.domain.credentials;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import com.redhat.rhn.common.hibernate.HibernateFactory;
+import com.redhat.rhn.domain.user.User;
+
+/**
+ * CredentialsFactory
+ */
+public class CredentialsFactory extends HibernateFactory {
+
+    private static CredentialsFactory singleton = new CredentialsFactory();
+    private static Logger log = Logger.getLogger(CredentialsFactory.class);
+
+    private CredentialsFactory() {
+        super();
+    }
+
+    /**
+     * Create new empty {@link Credentials}.
+     * @return new empty credentials
+     */
+    public static Credentials createCredentials() {
+        Credentials creds = new Credentials();
+        return creds;
+    }
+
+    /**
+     * Store {@link Credentials} to the database.
+     * @param creds credentials
+     */
+    public static void storeCredentials(Credentials creds) {
+        creds.setModified(new Date());
+        singleton.saveObject(creds);
+    }
+
+    /**
+     * Delete {@link Credentials} from the database.
+     * @param creds credentials
+     */
+    public static void removeCredentials(Credentials creds) {
+        singleton.removeObject(creds);
+    }
+
+    /**
+     * Load {@link Credentials} for a given {@link User} and type label.
+     * @param user user
+     * @param typeLabel type label
+     * @return credentials or null
+     */
+    public static Credentials lookupByUserAndType(User user, String typeLabel) {
+        if (user == null || typeLabel == null) {
+            return null;
+        }
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("user", user);
+        params.put("label", typeLabel);
+        return (Credentials) singleton.lookupObjectByNamedQuery(
+                "Credentials.findByUserAndTypeLabel", params);
+    }
+
+    /**
+     * Find a {@link CredentialsType} by a given label.
+     * @param label label
+     * @return CredentialsType instance for given label
+     */
+    public static CredentialsType findCredentialsTypeByLabel(String label) {
+        if (label == null) {
+            return null;
+        }
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("label", label);
+        return (CredentialsType) singleton.lookupObjectByNamedQuery(
+                "CredentialsType.findByLabel", params);
+    }
+
+    /**
+     * Helper method for creating new SUSE Studio {@link Credentials} for a
+     * given user.
+     * @param user user to associate with these credentials
+     * @return new credentials for SUSE Studio
+     */
+    public static Credentials createStudioCredentials(User user) {
+        Credentials creds = createCredentials();
+        creds.setUser(user);
+        creds.setType(CredentialsFactory
+                .findCredentialsTypeByLabel(Credentials.TYPE_SUSESTUDIO));
+        return creds;
+    }
+
+    /**
+     * Helper method for looking up SUSE Studio credentials.
+     * @param user user
+     * @return credentials or null
+     */
+    public static Credentials lookupStudioCredentials(User user) {
+        return lookupByUserAndType(user, Credentials.TYPE_SUSESTUDIO);
+    }
+
+    @Override
+    protected Logger getLogger() {
+        return log;
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/domain/credentials/CredentialsType.hbm.xml b/java/code/src/com/redhat/rhn/domain/credentials/CredentialsType.hbm.xml
new file mode 100644
index 0000000..3bcd2c5
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/credentials/CredentialsType.hbm.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping
+PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping>
+    <class name="com.redhat.rhn.domain.credentials.CredentialsType"
+        table="SUSECREDENTIALSTYPE" mutable="false">
+        <cache usage="read-only"/>
+        <id name="id" type="long" column="id">
+            <meta attribute="scope-set">protected</meta>
+            <generator class="assigned" />
+        </id>
+        <property name="name" type="string" column="name" />
+        <property name="label" type="string" column="label" />
+        <property name="created" type="date" column="created" />
+        <property name="modified" type="date" column="modified" />
+    </class>
+    <query name="CredentialsType.findByLabel">
+        <![CDATA[from com.redhat.rhn.domain.credentials.CredentialsType as t where t.label = :label]]>
+    </query>
+</hibernate-mapping>
diff --git a/java/code/src/com/redhat/rhn/domain/credentials/CredentialsType.java b/java/code/src/com/redhat/rhn/domain/credentials/CredentialsType.java
new file mode 100644
index 0000000..724c8c3
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/credentials/CredentialsType.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+package com.redhat.rhn.domain.credentials;
+
+import com.redhat.rhn.domain.BaseDomainHelper;
+
+/**
+ * CredentialsType
+ * @version $Rev$
+ */
+public class CredentialsType extends BaseDomainHelper {
+
+    private Long id;
+    private String label;
+    private String name;
+
+    /**
+     * @return Returns the id.
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * @param i The id to set.
+     */
+    public void setId(Long i) {
+        this.id = i;
+    }
+
+    /**
+     * @return Returns the label.
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * @param l The label to set.
+     */
+    public void setLabel(String l) {
+        this.label = l;
+    }
+
+    /**
+     * @return Returns the name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @param n The name to set.
+     */
+    public void setName(String n) {
+        this.name = n;
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        CredentialsType credsType = (CredentialsType) o;
+
+        if (id != null ? !id.equals(credsType.id) : credsType.id != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return id != null ? id.hashCode() : 0;
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/domain/image/Image.java b/java/code/src/com/redhat/rhn/domain/image/Image.java
new file mode 100644
index 0000000..937f356
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/image/Image.java
@@ -0,0 +1,244 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+package com.redhat.rhn.domain.image;
+
+import com.redhat.rhn.frontend.dto.BaseDto;
+
+/**
+ * Images for deployment to virtual host systems. Currently such image objects
+ * are not being persisted, but they rather exist in memory only.
+ */
+public class Image extends BaseDto implements Comparable<Image> {
+
+    private Long id;
+    private String name;
+    private String version;
+    private String arch;
+    private String imageSize;
+    private String imageType;
+    private String downloadUrl;
+    private String editUrl;
+    private boolean selectable = true;
+
+    /**
+     * Return the ID.
+     * @return id
+     */
+    public Long getId() {
+        return this.id;
+    }
+
+    /**
+     * Set the ID.
+     * @param inId id
+     */
+    public void setId(Long inId) {
+        this.id = inId;
+    }
+
+    /**
+     * Return the name.
+     * @return name
+     */
+    public String getName() {
+        return this.name;
+    }
+
+    /**
+     * Set the name.
+     * @param nameIn name
+     */
+    public void setName(String nameIn) {
+        this.name = nameIn;
+    }
+
+    /**
+     * Return the version.
+     * @return version
+     */
+    public String getVersion() {
+        return this.version;
+    }
+
+    /**
+     * Set the version.
+     * @param versionIn version
+     */
+    public void setVersion(String versionIn) {
+        this.version = versionIn;
+    }
+
+    /**
+     * Return the architecture.
+     * @return architecture
+     */
+    public String getArch() {
+        return this.arch;
+    }
+
+    /**
+     * Set the architecture.
+     * @param archIn architecture
+     */
+    public void setArch(String archIn) {
+        this.arch = archIn;
+    }
+
+    /**
+     * Get the image size.
+     * @return image size
+     */
+    public String getImageSize() {
+        return this.imageSize;
+    }
+
+    /**
+     * Set the image size.
+     * @param imageSizeIn image size
+     */
+    public void setImageSize(String imageSizeIn) {
+        this.imageSize = imageSizeIn;
+    }
+
+    /**
+     * Return the image type.
+     * @return image type
+     */
+    public String getImageType() {
+        return this.imageType;
+    }
+
+    /**
+     * Set the image type.
+     * @param imageTypeIn image type
+     */
+    public void setImageType(String imageTypeIn) {
+        this.imageType = imageTypeIn;
+    }
+
+    /**
+     * Return the download URL.
+     * @return download URL
+     */
+    public String getDownloadUrl() {
+        return this.downloadUrl;
+    }
+
+    /**
+     * Set the download URL.
+     * @param downloadUrlIn download URL
+     */
+    public void setDownloadUrl(String downloadUrlIn) {
+        this.downloadUrl = downloadUrlIn;
+    }
+
+    /**
+     * Return the edit URL.
+     * @return edit URL
+     */
+    public String getEditUrl() {
+        return this.editUrl;
+    }
+
+    /**
+     * Set the edit URL.
+     * @param editUrlIn edit URL
+     */
+    public void setEditUrl(String editUrlIn) {
+        this.editUrl = editUrlIn;
+    }
+
+    /**
+     * Control if this image is selectable.
+     * @param value true to make it selectable, otherwise false.
+     */
+    public void setSelectable(boolean value) {
+        this.selectable = value;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isSelectable() {
+        return selectable;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getSelectionKey() {
+        return String.valueOf(getId());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof Image)) {
+            return false;
+        }
+        Image other = (Image) obj;
+        if (id == null) {
+            if (other.id != null) {
+                return false;
+            }
+        }
+        else if (!id.equals(other.id)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((id == null) ? 0 : id.hashCode());
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int compareTo(Image image) {
+        int ret = 0;
+        if (!this.name.equals(image.getName())) {
+            ret = this.name.compareTo(image.name);
+        }
+        else if (!this.version.equals(image.getVersion())) {
+            ret = this.version.compareTo(image.version);
+        }
+        else if (!this.arch.equals(image.getArch())) {
+            ret = this.arch.compareTo(image.getArch());
+        }
+        else if (!this.imageType.equals(image.getImageType())) {
+            ret = this.imageType.compareTo(image.getImageType());
+        }
+        return ret;
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/domain/image/ProxyConfig.java b/java/code/src/com/redhat/rhn/domain/image/ProxyConfig.java
new file mode 100644
index 0000000..e4c19c4
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/domain/image/ProxyConfig.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+
+package com.redhat.rhn.domain.image;
+
+/**
+ * Simple class representing a proxy configuration.
+ */
+public class ProxyConfig {
+    private String server;
+    private String user;
+    private String pass;
+
+    /**
+     * Constructor for creating a proxy configuration.
+     * @param serverIn server
+     * @param userIn user
+     * @param passIn password
+     */
+    public ProxyConfig(String serverIn, String userIn, String passIn) {
+        this.setServer(serverIn);
+        this.setUser(userIn);
+        this.setPass(passIn);
+    }
+
+    /**
+     * Return the server.
+     * @return server
+     */
+    public String getServer() {
+        return server;
+    }
+
+    /**
+     * Set the server.
+     * @param serverIn server
+     */
+    public void setServer(String serverIn) {
+        this.server = serverIn;
+    }
+
+    /**
+     * Return the user.
+     * @return user
+     */
+    public String getUser() {
+        return user;
+    }
+
+    /**
+     * Set the user.
+     * @param userIn user
+     */
+    public void setUser(String userIn) {
+        this.user = userIn;
+    }
+
+    /**
+     * Return the password.
+     * @return password
+     */
+    public String getPass() {
+        return pass;
+    }
+
+    /**
+     * Set the password.
+     * @param passIn password
+     */
+    public void setPass(String passIn) {
+        this.pass = passIn;
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/frontend/action/renderers/ImagesRenderer.java b/java/code/src/com/redhat/rhn/frontend/action/renderers/ImagesRenderer.java
new file mode 100644
index 0000000..716c4a5
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/frontend/action/renderers/ImagesRenderer.java
@@ -0,0 +1,148 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+
+package com.redhat.rhn.frontend.action.renderers;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.log4j.Logger;
+
+import com.redhat.rhn.domain.credentials.Credentials;
+import com.redhat.rhn.domain.credentials.CredentialsFactory;
+import com.redhat.rhn.domain.image.Image;
+import com.redhat.rhn.domain.user.User;
+import com.redhat.rhn.frontend.listview.PageControl;
+import com.redhat.rhn.frontend.taglibs.list.ListTagHelper;
+import com.suse.studio.client.SUSEStudio;
+import com.suse.studio.client.model.Appliance;
+import com.suse.studio.client.model.Build;
+
+/**
+ * Asynchronously render the page content for image selection and deployment.
+ */
+public class ImagesRenderer extends BaseFragmentRenderer {
+
+    private static Logger logger = Logger.getLogger(ImagesRenderer.class);
+
+    // Attribute keys
+    public static final String ATTRIB_IMAGES_LIST = "imagesList";
+    public static final String ATTRIB_ERROR_MSG = "errorMsg";
+
+    // List of all valid image types
+    private static List<String> validImageTypes = Arrays.asList("vmx", "xen");
+
+    // The URL of the page to render
+    private static final String PAGE_URL =
+            "/WEB-INF/pages/systems/details/virtualization/images/images-render-async.jsp";
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void render(User user, PageControl pc, HttpServletRequest request) {
+        List<Image> images = null;
+        try {
+            // Get the list of images and sort it
+            images = getImages(user, request);
+            Collections.sort(images);
+
+            // Set the "parentUrl" for the form (in rl:listset)
+            request.setAttribute(ListTagHelper.PARENT_URL, "");
+
+            // Store the set of images (if any) to the session
+            if (images != null && !images.isEmpty()) {
+                request.getSession().setAttribute(ATTRIB_IMAGES_LIST, images);
+            }
+        }
+        catch (IOException e) {
+            logger.error(e.getMessage());
+            request.setAttribute(ATTRIB_ERROR_MSG, "images.message.error.connection");
+        }
+    }
+
+    /**
+     * Get a list of appliance builds from SUSE Studio.
+     * @param user
+     * @return list of {@link Image} objects
+     */
+    private List<Image> getImages(User user, HttpServletRequest request)
+            throws IOException {
+        List<Appliance> ret = new ArrayList<Appliance>();
+
+        // Lookup credentials and url
+        Credentials creds = CredentialsFactory.lookupStudioCredentials(user);
+        if (creds != null && creds.isComplete()) {
+            String studioUser = creds.getUsername();
+            String studioKey = creds.getPassword();
+            String studioUrl = creds.getUrl();
+
+            // Get appliance builds from studio
+            SUSEStudio studio = new SUSEStudio(studioUser, studioKey, studioUrl);
+            ret = studio.getAppliances();
+        }
+        else {
+            request.setAttribute(ATTRIB_ERROR_MSG, "images.message.error.nocreds");
+        }
+
+        // Convert to a list of images
+        return convertAppliances(ret);
+    }
+
+    /**
+     * Convert a list of {@link Appliance}s to a list of {@link Image}s.
+     * @param appliances list of appliances
+     * @return list of images
+     */
+    private List<Image> convertAppliances(List<Appliance> appliances) {
+        List<Image> ret = new LinkedList<Image>();
+        for (Appliance appliance : appliances) {
+            // Create one image object for every build
+            for (Build build : appliance.getBuilds()) {
+                // Skip this build if image type is unsupported
+                if (!validImageTypes.contains(build.getImageType())) {
+                    continue;
+                }
+                Image img = new Image();
+                // Appliance attributes
+                img.setArch(appliance.getArch());
+                img.setEditUrl(appliance.getEditUrl());
+                img.setName(appliance.getName());
+                // Build attributes
+                img.setDownloadUrl(build.getDownloadUrl());
+                img.setId(new Long(build.getId()));
+                img.setImageSize(build.getImageSize());
+                img.setImageType(build.getImageType());
+                img.setVersion(build.getVersion());
+                ret.add(img);
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getPageUrl() {
+        return PAGE_URL;
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/frontend/action/systems/images/ScheduleImageDeploymentAction.java b/java/code/src/com/redhat/rhn/frontend/action/systems/images/ScheduleImageDeploymentAction.java
new file mode 100644
index 0000000..117f949
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/frontend/action/systems/images/ScheduleImageDeploymentAction.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+package com.redhat.rhn.frontend.action.systems.images;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.DynaActionForm;
+
+import com.redhat.rhn.domain.action.Action;
+import com.redhat.rhn.domain.image.Image;
+import com.redhat.rhn.domain.image.ProxyConfig;
+import com.redhat.rhn.domain.server.Server;
+import com.redhat.rhn.domain.user.User;
+import com.redhat.rhn.frontend.action.renderers.ImagesRenderer;
+import com.redhat.rhn.frontend.struts.RequestContext;
+import com.redhat.rhn.frontend.struts.RhnAction;
+import com.redhat.rhn.frontend.taglibs.list.ListTagHelper;
+import com.redhat.rhn.manager.action.ActionManager;
+import com.redhat.rhn.manager.system.SystemManager;
+
+/**
+ * This action will present the user with a list of available images
+ * and allow one to be selected for provisioning.
+ */
+public class ScheduleImageDeploymentAction extends RhnAction {
+
+    private static final String SUCCESS_KEY = "images.message.success.scheduled";
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    public ActionForward execute(ActionMapping actionMapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws Exception {
+
+        // Get the current user
+        RequestContext ctx = new RequestContext(request);
+        User user = ctx.getCurrentUser();
+
+        // Put the server object to the request (for system header)
+        Long sid = new Long(request.getParameter(RequestContext.SID));
+        Server server = SystemManager.lookupByIdAndUser(sid, user);
+        request.setAttribute("system", server);
+
+        ActionForward forward;
+        if (request.getParameter(RequestContext.DISPATCH) != null) {
+            // Read the form parameters
+            DynaActionForm form = (DynaActionForm) actionForm;
+            Long vcpus = (Long) form.get("vcpus");
+            Long memkb = (Long) form.get("mem_mb") * 1024;
+            String bridge = (String) form.getString("bridge");
+            String proxyServer = (String) form.getString("proxy_server");
+            String proxyUser = (String) form.getString("proxy_user");
+            String proxyPass = (String) form.getString("proxy_pass");
+
+            // Find the requested image
+            String imageId = request.getParameter("image_id");
+            Image image = findImage(new Long(imageId), request);
+
+            // Set up the proxy configuration
+            ProxyConfig proxy = null;
+            if (StringUtils.isNotEmpty(proxyServer)) {
+                proxy = new ProxyConfig(proxyServer, proxyUser, proxyPass);
+            }
+
+            // Put defaults for deployment parameters
+            if (vcpus <= 0) {
+                vcpus = Long.valueOf(1);
+            }
+            if (memkb <= 0) {
+                memkb = Long.valueOf(524288);
+            }
+
+            // Create the action and store it
+            Action action = ActionManager.createDeployImageAction(user, image,
+                    vcpus, memkb, bridge, proxy);
+            ActionManager.addServerToAction(sid, action);
+            ActionManager.storeAction(action);
+            createSuccessMessage(request, SUCCESS_KEY, image.getName());
+
+            // Forward the sid as a request parameter
+            Map forwardParams = makeParamMap(request);
+            forwardParams.put(RequestContext.SID, sid);
+            forwardParams.put("load_async", false);
+            forward = getStrutsDelegate().forwardParams(
+                    actionMapping.findForward("submitted"), forwardParams);
+        }
+        else {
+            // Load images asynchronously if 'sid' is the only parameter
+            if (loadAsync(request)) {
+                request.setAttribute("loadAsync", true);
+            }
+            else {
+                // The 'parentUrl' is needed for the 'listset' tag
+                request.setAttribute(ListTagHelper.PARENT_URL,
+                        request.getRequestURI());
+            }
+            // Find the default destination
+            forward = actionMapping.findForward("default");
+        }
+        return forward;
+    }
+
+    /**
+     * Get the list of images from the session and find the selected one.
+     * @param imageId
+     * @param request
+     * @return
+     */
+    private Image findImage(Long imageId, HttpServletRequest request) {
+        @SuppressWarnings("unchecked")
+        List<Image> images = (List<Image>) request.getSession().getAttribute(
+                ImagesRenderer.ATTRIB_IMAGES_LIST);
+        Image image = null;
+        for (Image i : images) {
+            if (i.getId().equals(imageId)) {
+                image = i;
+                break;
+            }
+        }
+        return image;
+    }
+
+    /**
+     * Return true if there is only one parameter contained in the request.
+     * @param request
+     * @return true if there is only one parameter, else false
+     */
+    private boolean loadAsync(HttpServletRequest request) {
+        boolean ret = false;
+        Map params = request.getParameterMap();
+        if (params.size() == 1) {
+            ret = true;
+        }
+        return ret;
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/frontend/action/user/UserCredentialsDeleteAction.java b/java/code/src/com/redhat/rhn/frontend/action/user/UserCredentialsDeleteAction.java
new file mode 100644
index 0000000..7dcacef
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/frontend/action/user/UserCredentialsDeleteAction.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+package com.redhat.rhn.frontend.action.user;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+
+import com.redhat.rhn.domain.credentials.Credentials;
+import com.redhat.rhn.domain.credentials.CredentialsFactory;
+import com.redhat.rhn.domain.user.User;
+import com.redhat.rhn.frontend.struts.RequestContext;
+import com.redhat.rhn.frontend.struts.RhnAction;
+
+/**
+ * Delete credentials for external systems or APIs.
+ */
+public class UserCredentialsDeleteAction extends RhnAction {
+
+    private static final String ATTRIB_CREDS = "creds";
+
+    /** {@inheritDoc} */
+    @Override
+    public ActionForward execute(ActionMapping mapping, ActionForm formIn,
+            HttpServletRequest request, HttpServletResponse response)
+            throws Exception {
+        RequestContext ctx = new RequestContext(request);
+
+        // Lookup this user's credentials
+        User user = ctx.getCurrentUser();
+        Credentials creds = CredentialsFactory.lookupStudioCredentials(user);
+        if (creds == null) {
+            // Create new credentials if necessary
+            creds = CredentialsFactory.createStudioCredentials(user);
+        }
+        request.setAttribute(ATTRIB_CREDS, creds);
+
+        if (ctx.wasDispatched("credentials.jsp.delete.dispatch")) {
+            // Delete credentials from DB
+            CredentialsFactory.removeCredentials(creds);
+            ActionMessages messages = new ActionMessages();
+            messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
+                    "credentials.message.deleted"));
+            getStrutsDelegate().saveMessages(request, messages);
+            return mapping.findForward("success");
+        }
+        return mapping.findForward("default");
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/frontend/action/user/UserCredentialsEditAction.java b/java/code/src/com/redhat/rhn/frontend/action/user/UserCredentialsEditAction.java
new file mode 100644
index 0000000..6473985
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/frontend/action/user/UserCredentialsEditAction.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2012 Novell
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+package com.redhat.rhn.frontend.action.user;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+
+import com.redhat.rhn.domain.credentials.Credentials;
+import com.redhat.rhn.domain.credentials.CredentialsFactory;
+import com.redhat.rhn.domain.user.User;
+import com.redhat.rhn.frontend.struts.RequestContext;
+import com.redhat.rhn.frontend.struts.RhnAction;
+
+/**
+ * Create and edit credentials for external systems or APIs.
+ */
+public class UserCredentialsEditAction extends RhnAction {
+
+    private static final String ATTRIB_CREDS = "creds";
+    private static final String PARAM_USER = "studio_user";
+    private static final String PARAM_KEY = "studio_key";
+    private static final String PARAM_URL = "studio_url";
+    private static final String DEFAULT_URL = "http://susestudio.com";
+
+    /** {@inheritDoc} */
+    @Override
+    public ActionForward execute(ActionMapping mapping, ActionForm formIn,
+            HttpServletRequest request, HttpServletResponse response)
+            throws Exception {
+        RequestContext ctx = new RequestContext(request);
+
+        // Lookup this user's credentials
+        User user = ctx.getCurrentUser();
+        Credentials creds = CredentialsFactory.lookupStudioCredentials(user);
+        if (creds == null) {
+            // Create new credentials if necessary
+            creds = CredentialsFactory.createStudioCredentials(user);
+            creds.setUrl(DEFAULT_URL);
+        }
+        request.setAttribute(ATTRIB_CREDS, creds);
+
+        if (ctx.isSubmitted()) {
+            // The form was submitted, create a temporary object
+            Credentials newCreds = CredentialsFactory.createCredentials();
+            newCreds.setUsername(request.getParameter(PARAM_USER).trim());
+            newCreds.setPassword(request.getParameter(PARAM_KEY).trim());
+            newCreds.setUrl(request.getParameter(PARAM_URL).trim());
+
+            // Check for completeness
+            if (newCreds.isEmpty() || newCreds.isComplete()) {
+                if (newCreds.isEmpty()) {
+                    // Delete from DB
+                    CredentialsFactory.removeCredentials(creds);
+                    request.setAttribute(ATTRIB_CREDS, newCreds);
+                }
+                else {
+                    // Store the credentials
+                    creds.setUsername(newCreds.getUsername());
+                    creds.setPassword(newCreds.getPassword());
+                    creds.setUrl(newCreds.getUrl());
+                    CredentialsFactory.storeCredentials(creds);
+                }
+                ActionMessages messages = new ActionMessages();
+                messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
+                        "credentials.message.updated"));
+                getStrutsDelegate().saveMessages(request, messages);
+            }
+            else {
+                // Incomplete credentials, show an error
+                ActionErrors errors = new ActionErrors();
+                errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
+                        "credentials.message.incomplete"));
+                getStrutsDelegate().saveMessages(request, errors);
+                request.setAttribute(ATTRIB_CREDS, newCreds);
+            }
+        }
+        return mapping.findForward("default");
+    }
+}
diff --git a/java/code/src/com/redhat/rhn/frontend/strings/java/StringResource_en_US.xml b/java/code/src/com/redhat/rhn/frontend/strings/java/StringResource_en_US.xml
index e0f656a..338430b 100644
--- a/java/code/src/com/redhat/rhn/frontend/strings/java/StringResource_en_US.xml
+++ b/java/code/src/com/redhat/rhn/frontend/strings/java/StringResource_en_US.xml
@@ -8210,6 +8210,25 @@ tree will not be usable for kickstarting.
      </trans-unit>
 
 
+      <!-- SUSE Studio -->
+      <trans-unit id="credentials.message.updated">
+        <source>Your credentials were successfully updated.</source>
+      </trans-unit>
+      <trans-unit id="credentials.message.deleted">
+        <source>Your credentials were successfully deleted.</source>
+      </trans-unit>
+      <trans-unit id="credentials.message.incomplete">
+        <source>Your credentials are incomplete, at least username and password are needed.</source>
+      </trans-unit>
+      <trans-unit id="images.message.error.nocreds">
+        <source>It looks like you did not enter valid credentials for talking to SUSE Studio, please check &lt;a href="/rhn/account/Credentials.do"&gt;here&lt;/a&gt;.</source>
+      </trans-unit>
+      <trans-unit id="images.message.error.connection">
+        <source>There was a problem connecting to SUSE Studio, you might want to review your configuration &lt;a href="/rhn/account/Credentials.do"&gt;here&lt;/a&gt; or try again later.</source>
+      </trans-unit>
+      <trans-unit id="images.message.success.scheduled">
+        <source>Image deployment scheduled: {0}</source>
+      </trans-unit>
     </body>
   </file>
 </xliff>
diff --git a/java/code/src/com/redhat/rhn/frontend/strings/jsp/StringResource_en_US.xml b/java/code/src/com/redhat/rhn/frontend/strings/jsp/StringResource_en_US.xml
index 2283fc2..d691c85 100644
--- a/java/code/src/com/redhat/rhn/frontend/strings/jsp/StringResource_en_US.xml
+++ b/java/code/src/com/redhat/rhn/frontend/strings/jsp/StringResource_en_US.xml
@@ -21165,6 +21165,95 @@ given channel.</source>
 	 <trans-unit id="channel.jsp.changelog.error">
         <source>There was an error reading the selected package changelog.  Please check that the package physically exists and has the correct permissions.</source>
      </trans-unit>
+
+     <!-- SUSE Studio -->
+     <trans-unit id="image.deploy">
+        <source>Image Deployment</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.select.title">
+        <source>Select an Image</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.select.summary">
+        <source>Please choose from the images below to create a new virtual system.</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.name">
+        <source>Name</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.version">
+        <source>Version</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.arch">
+        <source>Architecture</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.type">
+        <source>Type</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.noimages">
+        <source>No Images</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.deploy.title">
+        <source>Image Deployment: </source>
+     </trans-unit>
+     <trans-unit id="images.jsp.deploy.summary">
+        <source>Please submit the form to schedule the deployment of image &lt;span id="image-string"&gt;&lt;/span&gt;.</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.vmsetup">
+        <source>Virtual Machine Setup</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.vcpus">
+        <source>Number of VCPUs&lt;span class="required-form-field"&gt;*&lt;/span&gt;:</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.memory">
+        <source>Memory (MB)&lt;span class="required-form-field"&gt;*&lt;/span&gt;:</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.bridge">
+        <source>Bridge Device:</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.proxyconfig">
+        <source>Proxy Configuration</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.proxyserver">
+        <source>Proxy Server:</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.proxyuser">
+        <source>Proxy User:</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.proxypass">
+        <source>Proxy Password:</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.cancel">
+        <source>Cancel</source>
+     </trans-unit>
+     <trans-unit id="images.jsp.dispatch">
+        <source>Schedule Deployment</source>
+     </trans-unit>
+     <trans-unit id="credentials.jsp.susestudio">
+        <source>SUSE Studio</source>
+     </trans-unit>
+     <trans-unit id="credentials.jsp.edit.summary">
+        <source>External system or API credentials associated with your @@PRODUCT_NAME@@ account are listed below.</source>
+     </trans-unit>
+     <trans-unit id="credentials.jsp.edit.dispatch">
+        <source>Update Credentials</source>
+     </trans-unit>
+     <trans-unit id="credentials.jsp.delete.summary">
+        <source>These external system or API credentials will be deleted. This action cannot be undone.</source>
+     </trans-unit>
+     <trans-unit id="credentials.jsp.delete.dispatch">
+        <source>Delete Credentials</source>
+     </trans-unit>
+     <trans-unit id="toolbar.delete.credentials">
+        <source>delete credentials</source>
+     </trans-unit>
+     <trans-unit id="credentials.jsp.username">
+        <source>Username</source>
+     </trans-unit>
+     <trans-unit id="credentials.jsp.apikey">
+        <source>API Key</source>
+     </trans-unit>
+     <trans-unit id="credentials.jsp.url">
+        <source>URL</source>
+     </trans-unit>
   </body>
   </file>
 </xliff>
diff --git a/java/code/src/com/redhat/rhn/frontend/strings/nav/StringResource_en_US.xml b/java/code/src/com/redhat/rhn/frontend/strings/nav/StringResource_en_US.xml
index 5400455..e3930ce 100644
--- a/java/code/src/com/redhat/rhn/frontend/strings/nav/StringResource_en_US.xml
+++ b/java/code/src/com/redhat/rhn/frontend/strings/nav/StringResource_en_US.xml
@@ -1110,6 +1110,13 @@ http://www.oasis-open.org/committees/xliff/documents/xliff-core-1.1.xsd"
   </group>
 
 
+      <!-- SUSE Studio -->
+      <trans-unit id="Credentials">
+        <source>Credentials</source>
+      </trans-unit>
+      <trans-unit id="Images">
+        <source>Images</source>
+      </trans-unit>
     </body>
   </file>
 </xliff>
diff --git a/java/code/src/com/redhat/rhn/frontend/taglibs/RhnTagFunctions.java b/java/code/src/com/redhat/rhn/frontend/taglibs/RhnTagFunctions.java
index 8af4368..9da77fe 100644
--- a/java/code/src/com/redhat/rhn/frontend/taglibs/RhnTagFunctions.java
+++ b/java/code/src/com/redhat/rhn/frontend/taglibs/RhnTagFunctions.java
@@ -14,6 +14,7 @@
  */
 package com.redhat.rhn.frontend.taglibs;
 
+import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang.StringUtils;
 
 import com.redhat.rhn.common.conf.Config;
@@ -131,4 +132,13 @@ public class RhnTagFunctions {
         return StringUtil.urlEncode(param);
     }
 
+    /**
+     * Escape the characters in a String using JavaScript String rules.
+     *
+     * @param param to be escaped
+     * @return escaped string
+     */
+    public static String escapeJS(String param) {
+        return StringEscapeUtils.escapeJavaScript(param);
+    }
 }
diff --git a/java/code/src/com/redhat/rhn/frontend/taglibs/rhn-taglib.tld b/java/code/src/com/redhat/rhn/frontend/taglibs/rhn-taglib.tld
index 0f9445b..e423ea0 100644
--- a/java/code/src/com/redhat/rhn/frontend/taglibs/rhn-taglib.tld
+++ b/java/code/src/com/redhat/rhn/frontend/taglibs/rhn-taglib.tld
@@ -704,4 +704,9 @@
         <function-class>com.redhat.rhn.frontend.taglibs.RhnTagFunctions</function-class>
         <function-signature>java.lang.String urlEncode(java.lang.String)</function-signature>
     </function>    
+    <function>
+        <name>escapeJS</name>
+        <function-class>com.redhat.rhn.frontend.taglibs.RhnTagFunctions</function-class>
+        <function-signature>java.lang.String escapeJS(java.lang.String)</function-signature>
+    </function>
 </taglib>
diff --git a/java/code/src/com/redhat/rhn/frontend/xmlrpc/activationkey/ActivationKeyHandler.java b/java/code/src/com/redhat/rhn/frontend/xmlrpc/activationkey/ActivationKeyHandler.java
index d947ca6..1eeb4d0 100644
--- a/java/code/src/com/redhat/rhn/frontend/xmlrpc/activationkey/ActivationKeyHandler.java
+++ b/java/code/src/com/redhat/rhn/frontend/xmlrpc/activationkey/ActivationKeyHandler.java
@@ -111,7 +111,8 @@ public class ActivationKeyHandler extends BaseHandler {
      * default.")
      * @xmlrpc.param #param_desc("int", "usageLimit", "If unlimited usage is desired,
      * use the create API that does not include the parameter.")
-     * @xmlrpc.param #array_desc("string", "Add-on entitlement label to associate with the key.")
+     * @xmlrpc.param #array_desc("string", "Add-on entitlement label to associate with the
+     * key.")
      *   #options()
      *     #item("monitoring_entitled")
      *     #item("provisioning_entitled")
@@ -220,7 +221,8 @@ public class ActivationKeyHandler extends BaseHandler {
      * @xmlrpc.param #param("string", "description")
      * @xmlrpc.param #param_desc("string", "baseChannelLabel", "Leave empty to accept
      * default.")
-     * @xmlrpc.param #array_desc("string", "Add-on entitlement label to associate with the key.")
+     * @xmlrpc.param #array_desc("string", "Add-on entitlement label to associate with the
+     * key.")
      *   #options()
      *     #item("monitoring_entitled")
      *     #item("provisioning_entitled")
diff --git a/java/code/src/com/redhat/rhn/manager/action/ActionManager.java b/java/code/src/com/redhat/rhn/manager/action/ActionManager.java
index f13cf3a..5fbab36 100644
--- a/java/code/src/com/redhat/rhn/manager/action/ActionManager.java
+++ b/java/code/src/com/redhat/rhn/manager/action/ActionManager.java
@@ -27,6 +27,8 @@ import com.redhat.rhn.domain.action.ActionType;
 import com.redhat.rhn.domain.action.config.ConfigAction;
 import com.redhat.rhn.domain.action.config.ConfigUploadAction;
 import com.redhat.rhn.domain.action.errata.ErrataAction;
+import com.redhat.rhn.domain.action.image.DeployImageAction;
+import com.redhat.rhn.domain.action.image.DeployImageActionDetails;
 import com.redhat.rhn.domain.action.kickstart.KickstartAction;
 import com.redhat.rhn.domain.action.kickstart.KickstartActionDetails;
 import com.redhat.rhn.domain.action.kickstart.KickstartGuestAction;
@@ -47,6 +49,8 @@ import com.redhat.rhn.domain.config.ConfigRevision;
 import com.redhat.rhn.domain.config.ConfigurationFactory;
 import com.redhat.rhn.domain.errata.Errata;
 import com.redhat.rhn.domain.errata.impl.PublishedErrata;
+import com.redhat.rhn.domain.image.Image;
+import com.redhat.rhn.domain.image.ProxyConfig;
 import com.redhat.rhn.domain.kickstart.KickstartData;
 import com.redhat.rhn.domain.kickstart.KickstartFactory;
 import com.redhat.rhn.domain.org.Org;
@@ -70,6 +74,7 @@ import com.redhat.rhn.manager.kickstart.cobbler.CobblerVirtualSystemCommand;
 import com.redhat.rhn.manager.kickstart.cobbler.CobblerXMLRPCHelper;
 import com.redhat.rhn.manager.system.SystemManager;
 
+import org.apache.commons.codec.binary.Base64;
 import org.apache.log4j.Logger;
 import org.cobbler.Profile;
 
@@ -615,6 +620,44 @@ public class ActionManager extends BaseManager {
     }
 
     /**
+     * Schedule deployment of an image to a vhost.
+     *
+     * @return The created action
+     * @param user The user scheduling image deployment
+     * @param image The image that will be deployed
+     * @param vcpus number of vcpus
+     * @param memkb memory in Kb
+     * @param bridge device
+     * @param proxy proxy configuration
+     */
+    public static Action createDeployImageAction(User user, Image image,
+            Long vcpus, Long memkb, String bridge, ProxyConfig proxy) {
+        DeployImageAction a = (DeployImageAction) ActionFactory
+                .createAction(ActionFactory.TYPE_DEPLOY_IMAGE);
+        if (user != null) {
+            a.setSchedulerUser(user);
+            a.setOrg(user.getOrg());
+        }
+
+        DeployImageActionDetails details = new DeployImageActionDetails();
+        details.setParentAction(a);
+        details.setVcpus(vcpus);
+        details.setMemKb(memkb);
+        details.setBridgeDevice(bridge);
+        details.setDownloadUrl(image.getDownloadUrl());
+        if (proxy != null) {
+            details.setProxyServer(proxy.getServer());
+            details.setProxyUser(proxy.getUser());
+            details.setProxyPass(new String(Base64.encodeBase64(
+                    proxy.getPass().getBytes())));
+        }
+        a.setDetails(details);
+        a.setName("Image Deployment: " + image.getName() + " - " +
+                image.getVersion());
+        return a;
+    }
+
+    /**
      *
      * @param user   The user scheduling the action
      * @param server The server the action is being scheduled for
diff --git a/java/code/webapp/WEB-INF/dwr.xml b/java/code/webapp/WEB-INF/dwr.xml
index 24f117f..b42b541 100644
--- a/java/code/webapp/WEB-INF/dwr.xml
+++ b/java/code/webapp/WEB-INF/dwr.xml
@@ -43,5 +43,9 @@
 	  	<param name="class" value="com.redhat.rhn.frontend.taglibs.DWRItemSelector" />
 	  	<include method="select" />	  	
 	  </create>	  
+          <create creator="new" javascript="ImagesRenderer">
+                <param name="class" value="com.redhat.rhn.frontend.action.renderers.ImagesRenderer" />
+                <include method="renderAsync" />
+          </create>
 	</allow>
-</dwr>
\ No newline at end of file
+</dwr>
diff --git a/java/code/webapp/WEB-INF/nav/sitenav-authenticated.xml b/java/code/webapp/WEB-INF/nav/sitenav-authenticated.xml
index 6250800..c734eec 100644
--- a/java/code/webapp/WEB-INF/nav/sitenav-authenticated.xml
+++ b/java/code/webapp/WEB-INF/nav/sitenav-authenticated.xml
@@ -9,6 +9,10 @@
         <rhn-tab-url>/rhn/account/EditAddress.do</rhn-tab-url>
       </rhn-tab>
       <rhn-tab name="Change Email" url="/rhn/account/ChangeEmail.do" />
+      <rhn-tab name="Credentials">
+        <rhn-tab-url>/rhn/account/Credentials.do</rhn-tab-url>
+        <rhn-tab-url>/rhn/account/DeleteCredentials.do</rhn-tab-url>
+      </rhn-tab>
       <rhn-tab name="Account Deactivation" url="/rhn/account/AccountDeactivation.do"/>
     </rhn-tab>
     <rhn-tab name="Your Preferences" url="/rhn/account/UserPreferences.do" />
diff --git a/java/code/webapp/WEB-INF/nav/system_detail.xml b/java/code/webapp/WEB-INF/nav/system_detail.xml
index 05d6a24..4b314ec 100644
--- a/java/code/webapp/WEB-INF/nav/system_detail.xml
+++ b/java/code/webapp/WEB-INF/nav/system_detail.xml
@@ -178,6 +178,7 @@
     <rhn-tab-url>/rhn/systems/details/virtualization/VirtualGuestsList.do</rhn-tab-url>
     <rhn-tab name="Details" url="/rhn/systems/details/virtualization/VirtualGuestsList.do"/>
     <rhn-tab name="Provisioning" url="/rhn/systems/details/virtualization/ProvisionVirtualizationWizard.do" acl="system_feature(ftr_kickstart) or system_feature(ftr_snapshotting)"/>
+    <rhn-tab name="Images" url="/rhn/systems/details/virtualization/Images.do"/>
   </rhn-tab>
 
   <rhn-tab name="Audit">
diff --git a/java/code/webapp/WEB-INF/pages/common/fragments/scheduledactions/listdisplay-new.jspf b/java/code/webapp/WEB-INF/pages/common/fragments/scheduledactions/listdisplay-new.jspf
index 0e118bd..0ed39b4 100644
--- a/java/code/webapp/WEB-INF/pages/common/fragments/scheduledactions/listdisplay-new.jspf
+++ b/java/code/webapp/WEB-INF/pages/common/fragments/scheduledactions/listdisplay-new.jspf
@@ -18,7 +18,7 @@
                            sortattr="actionName"
                            filterattr="actionName"
                            styleclass="list-fat-column-50">
-                        	<a href="/rhn/schedule/CompletedSystems.do?aid=${current.id}">${current.actionName}</a>
+                           <a href="/rhn/schedule/CompletedSystems.do?aid=${current.id}"><c:out value="${current.actionName}" /></a>
                 </rl:column>
 
 
diff --git a/java/code/webapp/WEB-INF/pages/common/fragments/yourrhn/scheduledActions.jsp b/java/code/webapp/WEB-INF/pages/common/fragments/yourrhn/scheduledActions.jsp
index 121b560..68d1910 100644
--- a/java/code/webapp/WEB-INF/pages/common/fragments/yourrhn/scheduledActions.jsp
+++ b/java/code/webapp/WEB-INF/pages/common/fragments/yourrhn/scheduledActions.jsp
@@ -29,7 +29,7 @@
     			<img src="/img/icon_checkin.gif" title="<bean:message key="yourrhn.jsp.actions.unknown" />" alt="<bean:message key="yourrhn.jsp.actions.unknown" />"  />
     		</c:otherwise>
     	</c:choose>
-    	<a href="/rhn/schedule/ActionDetails.do?aid=${current.id}">${current.actionName}</a>
+      <a href="/rhn/schedule/ActionDetails.do?aid=${current.id}"><c:out value="${current.actionName}" /></a>
     </rhn:column>
 
         <rhn:column header="yourrhn.jsp.user"
diff --git a/java/code/webapp/WEB-INF/pages/credentials/delete.jsp b/java/code/webapp/WEB-INF/pages/credentials/delete.jsp
new file mode 100644
index 0000000..18ccef7
--- /dev/null
+++ b/java/code/webapp/WEB-INF/pages/credentials/delete.jsp
@@ -0,0 +1,49 @@
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://rhn.redhat.com/rhn" prefix="rhn" %>
+
+<html:xhtml/>
+<html>
+<body>
+
+<rhn:toolbar base="h1" img="/img/rhn-icon-users.gif" imgAlt="users.jsp.imgAlt"
+    helpUrl="/rhn/help/reference/en-US/s1-sm-your-rhn.jsp#s2-sm-your-rhn-account">
+  <bean:message key="credentials.jsp.delete.dispatch" />
+</rhn:toolbar>
+
+<div class="page-summary">
+  <p><bean:message key="credentials.jsp.delete.summary" /></p>
+</div>
+
+<hr />
+
+<h2><bean:message key="credentials.jsp.susestudio" /></h2>
+<table class="details">
+<tr>
+  <th><bean:message key="credentials.jsp.username" /></th>
+  <td><c:out value="${creds.username}" /></td>
+</tr>
+<tr>
+  <th><bean:message key="credentials.jsp.apikey" /></th>
+  <td><c:out value="${creds.password}" /></td>
+</tr>
+<tr>
+  <th><bean:message key="credentials.jsp.url" /></th>
+  <td><c:out value="${creds.url}" /></td>
+</tr>
+</table>
+
+<form method="post" action="/rhn/account/DeleteCredentials.do">
+  <rhn:csrf />
+  <rhn:submitted />
+  <div align="right">
+    <hr />
+    <html:submit property="dispatch">
+      <bean:message key="credentials.jsp.delete.dispatch" />
+    </html:submit>
+  </div>
+</form>
+
+</body>
+</html>
diff --git a/java/code/webapp/WEB-INF/pages/credentials/edit.jsp b/java/code/webapp/WEB-INF/pages/credentials/edit.jsp
new file mode 100644
index 0000000..0221d2e
--- /dev/null
+++ b/java/code/webapp/WEB-INF/pages/credentials/edit.jsp
@@ -0,0 +1,50 @@
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://rhn.redhat.com/rhn" prefix="rhn" %>
+
+<html:xhtml/>
+<html>
+<body>
+
+<rhn:toolbar base="h1" img="/img/rhn-icon-users.gif" imgAlt="users.jsp.imgAlt"
+    helpUrl="/rhn/help/reference/en-US/s1-sm-your-rhn.jsp#s2-sm-your-rhn-account"
+    deletionUrl="/rhn/account/DeleteCredentials.do"
+    deletionType="credentials">
+  <bean:message key="Credentials"/>
+</rhn:toolbar>
+
+<div class="page-summary">
+  <p><bean:message key="credentials.jsp.edit.summary" /></p>
+</div>
+
+<form method="post" action="/rhn/account/Credentials.do">
+  <rhn:csrf />
+  <rhn:submitted />
+
+  <h2><bean:message key="credentials.jsp.susestudio" /></h2>
+  <table class="details">
+  <tr>
+    <th><bean:message key="credentials.jsp.username" /></th>
+    <td><html:text property="studio_user" value="${creds.username}" /></td>
+  </tr>
+  <tr>
+    <th><bean:message key="credentials.jsp.apikey" /></th>
+    <td><html:text property="studio_key" value="${creds.password}" /></td>
+  </tr>
+  <tr>
+    <th><bean:message key="credentials.jsp.url" /></th>
+    <td><html:text property="studio_url" value="${creds.url}" /></td>
+  </tr>
+  </table>
+
+  <div align="right">
+    <hr />
+    <html:submit>
+      <bean:message key="credentials.jsp.edit.dispatch" />
+    </html:submit>
+  </div>
+</form>
+
+</body>
+</html>
diff --git a/java/code/webapp/WEB-INF/pages/schedule/archivedactions.jsp b/java/code/webapp/WEB-INF/pages/schedule/archivedactions.jsp
index 5f6b2d2..db5c513 100644
--- a/java/code/webapp/WEB-INF/pages/schedule/archivedactions.jsp
+++ b/java/code/webapp/WEB-INF/pages/schedule/archivedactions.jsp
@@ -36,7 +36,7 @@
                        sortattr="actionName"
                        styleclass="list-fat-column-50"
                        filterattr="actionName">
-                <a href="/rhn/schedule/CompletedSystems.do?aid=${current.id}">${current.actionName}</a>
+                <a href="/rhn/schedule/CompletedSystems.do?aid=${current.id}"><c:out value="${current.actionName}" /></a>
             </rl:column>
 
             <rl:column sortable="true"
diff --git a/java/code/webapp/WEB-INF/pages/schedule/pendingactionsconfirm.jsp b/java/code/webapp/WEB-INF/pages/schedule/pendingactionsconfirm.jsp
index 84afb64..fd37329 100644
--- a/java/code/webapp/WEB-INF/pages/schedule/pendingactionsconfirm.jsp
+++ b/java/code/webapp/WEB-INF/pages/schedule/pendingactionsconfirm.jsp
@@ -36,7 +36,7 @@
                            defaultsort="asc"
                            filterattr="actionName"
                            styleclass="first-column">
-				<a href="/rhn/schedule/CompletedSystems.do?aid=${current.id}">${current.actionName}</a>
+				<a href="/rhn/schedule/CompletedSystems.do?aid=${current.id}"><c:out value="${current.actionName}" /></a>
                 </rl:column>
 
 
diff --git a/java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images-content.jspf b/java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images-content.jspf
new file mode 100644
index 0000000..6d2cba0
--- /dev/null
+++ b/java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images-content.jspf
@@ -0,0 +1,94 @@
+<%@ taglib uri="http://rhn.redhat.com/rhn" prefix="rhn" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+
+<rl:listset name="groupSet">
+  <rhn:csrf />
+  <html:hidden property="sid" value="${param.sid}" />
+
+  <div id="images-table">
+    <h2><bean:message key="images.jsp.select.title" /></h2>
+    <div class="page-summary">
+      <p>
+        <bean:message key="images.jsp.select.summary" />
+      </p>
+    </div>
+    <rl:list dataset="imagesList" emptykey="images.jsp.noimages">
+      <rl:column headerkey="images.jsp.name" filterattr="name">
+        <c:set var="nameJS" value="${fn:escapeXml(rhn:escapeJS(current.name))}" />
+        <a href="javascript:void();" onclick="showForm('${current.id}','${nameJS}','${current.version}','${current.arch}','${current.imageType}','${current.editUrl}');">
+          <c:out value="${current.name}" />
+        </a>
+      </rl:column>
+      <rl:column headerkey="images.jsp.version">
+        <c:out value="${current.version}" />
+      </rl:column>
+      <rl:column headerkey="images.jsp.arch">
+        <c:out value="${current.arch}" />
+      </rl:column>
+      <rl:column headerkey="images.jsp.type">
+        <c:out value="${current.imageType}" />
+      </rl:column>
+    </rl:list>
+  </div>
+
+  <div id="deployment-form" style="display:none">
+    <h2><bean:message key="images.jsp.deploy.title" /><a id="edit-link" href="" target="_blank"></a></h2>
+    <div class="page-summary">
+      <p><bean:message key="images.jsp.deploy.summary" /></p>
+    </div>
+
+    <table class="details-2-columns" width="100%">
+      <tbody>
+        <tr>
+          <td width="50%">
+            <h2><bean:message key="images.jsp.vmsetup" /></h2>
+            <table class="details">
+              <tr>
+                <th><bean:message key="images.jsp.vcpus" /></th>
+                <td><html:text property="vcpus" size="15" value="1" /></td>
+              </tr>
+              <tr>
+                <th><bean:message key="images.jsp.memory" /></th>
+                <td><html:text property="mem_mb" size="15" value="512" /></td>
+              </tr>
+              <tr>
+                <th><bean:message key="images.jsp.bridge" /></th>
+                <td><html:text property="bridge" size="15" value="br0" /></td>
+              </tr>
+            </table>
+          </td>
+          <td width="50%">
+            <h2><bean:message key="images.jsp.proxyconfig" /></h2>
+            <table class="details">
+              <tr>
+                <th><bean:message key="images.jsp.proxyserver" /></th>
+                <td><html:text property="proxy_server" size="30" value="" /></td>
+              </tr>
+              <tr>
+                <th><bean:message key="images.jsp.proxyuser" /></th>
+                <td><html:text property="proxy_user" size="15" value="" /></td>
+              </tr>
+              <tr>
+                <th><bean:message key="images.jsp.proxypass" /></th>
+                <td><html:password property="proxy_pass" size="15" value="" /></td>
+              </tr>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+
+    <div align="right">
+      <rhn:submitted />
+      <html:hidden styleId="image-id" property="image_id" value="" />
+      <hr />
+      <input type="button"
+             onclick="showImages()"
+             value="${rhn:localize('images.jsp.cancel')}" />
+      <html:submit property="dispatch"
+                   disabled="${empty sessionScope.imagesList}">
+        <bean:message key="images.jsp.dispatch" />
+      </html:submit>
+    </div>
+  </div>
+</rl:listset>
diff --git a/java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images-render-async.jsp b/java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images-render-async.jsp
new file mode 100644
index 0000000..0ae275c
--- /dev/null
+++ b/java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images-render-async.jsp
@@ -0,0 +1,17 @@
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://rhn.redhat.com/rhn" prefix="rhn" %>
+<%@ taglib uri="http://rhn.redhat.com/tags/list" prefix="rl" %>
+
+<c:choose>
+  <c:when test="${requestScope.errorMsg != null}">
+    <div class="page-summary">
+      <p><bean:message key="${requestScope.errorMsg}" /></p>
+    </div>
+  </c:when>
+
+  <c:otherwise>
+    <%@ include file="/WEB-INF/pages/systems/details/virtualization/images/images-content.jspf" %>
+  </c:otherwise>
+</c:choose>
diff --git a/java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images.jsp b/java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images.jsp
new file mode 100644
index 0000000..d4d83d9
--- /dev/null
+++ b/java/code/webapp/WEB-INF/pages/systems/details/virtualization/images/images.jsp
@@ -0,0 +1,38 @@
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
+<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
+<%@ taglib uri="http://rhn.redhat.com/rhn" prefix="rhn" %>
+<%@ taglib uri="http://rhn.redhat.com/tags/list" prefix="rl" %>
+
+<html:xhtml/>
+<html>
+
+<head>
+  <script type="text/javascript" src="/rhn/dwr/interface/ImagesRenderer.js"></script>
+  <script type="text/javascript" src="/rhn/dwr/engine.js"></script>
+  <script type="text/javascript" src="/javascript/scriptaculous.js"></script>
+  <script type="text/javascript" src="/javascript/render.js"></script>
+  <script type="text/javascript" src="/javascript/images.js"></script>
+</head>
+
+<body>
+  <%@ include file="/WEB-INF/pages/common/fragments/systems/system-header.jspf" %>
+
+  <c:choose>
+    <c:when test="${requestScope.loadAsync == true}">
+      <div id="images-content">
+        <div style="padding: 1em;">
+          <img src="/img/spinner.gif" style="vertical-align: middle;" />
+          <span style="padding-left: 0.3em;">Loading ...</span>
+        </div>
+        <script type="text/javascript">
+          ImagesRenderer.renderAsync(makeAjaxCallback("images-content", false));
+        </script>
+      </div>
+    </c:when>
+    <c:otherwise>
+      <%@ include file="/WEB-INF/pages/systems/details/virtualization/images/images-content.jspf" %>
+    </c:otherwise>
+  </c:choose>
+</body>
+</html>
diff --git a/java/code/webapp/WEB-INF/struts-config.xml b/java/code/webapp/WEB-INF/struts-config.xml
index 0565d64..ac1e0fa 100644
--- a/java/code/webapp/WEB-INF/struts-config.xml
+++ b/java/code/webapp/WEB-INF/struts-config.xml
@@ -1178,6 +1178,16 @@
       <form-property name="submitted" type="java.lang.Boolean" />
     </form-bean>
 
+    <form-bean name="deployImageForm"
+               type="com.redhat.rhn.frontend.struts.ScrubbingDynaActionForm">
+      <form-property name="vcpus" type="java.lang.Long" />
+      <form-property name="mem_mb" type="java.lang.Long" />
+      <form-property name="bridge" type="java.lang.String" />
+      <form-property name="proxy_server" type="java.lang.String" />
+      <form-property name="proxy_user" type="java.lang.String" />
+      <form-property name="proxy_pass" type="java.lang.String" />
+    </form-bean>
+
   </form-beans>
 
   <global-exceptions>
@@ -7730,6 +7740,46 @@
                  path="/WEB-INF/pages/admin/bunchDetail.jsp" />
      </action>
 
+    <!-- Image Deployment -->
+    <action path="/systems/details/virtualization/Images"
+            scope="request"
+            name="deployImageForm"
+            parameter="dispatch"
+            type="com.redhat.rhn.frontend.action.systems.images.ScheduleImageDeploymentAction"
+            className="com.redhat.rhn.frontend.struts.RhnActionMapping">
+      <set-property property="postRequiredIfSubmitted" value="true" />
+      <set-property property="acls" value="not system_is_virtual(); system_has_management_entitlement()" />
+      <forward name="default"
+               path="/WEB-INF/pages/systems/details/virtualization/images/images.jsp" />
+      <forward name="submitted"
+               redirect="true"
+               path="/systems/details/virtualization/Images.do" />
+    </action>
+
+    <action path="/account/Credentials"
+            scope="request"
+            input="/WEB-INF/pages/credentials/edit.jsp"
+            type="com.redhat.rhn.frontend.action.user.UserCredentialsEditAction"
+            className="com.redhat.rhn.frontend.struts.RhnActionMapping">
+      <set-property property="postRequiredIfSubmitted" value="true" />
+      <forward name="default"
+               path="/WEB-INF/pages/credentials/edit.jsp" />
+    </action>
+
+    <action path="/account/DeleteCredentials"
+            scope="request"
+            input="/WEB-INF/pages/credentials/delete.jsp"
+            type="com.redhat.rhn.frontend.action.user.UserCredentialsDeleteAction"
+            className="com.redhat.rhn.frontend.struts.RhnActionMapping"
+            parameter="dispatch">
+      <set-property property="postRequiredIfSubmitted" value="true" />
+      <forward name="default"
+               path="/WEB-INF/pages/credentials/delete.jsp" />
+      <forward name="success"
+               redirect="true"
+               path="/account/Credentials.do" />
+    </action>
+
   </action-mappings>
 
   <!-- ===================================== -->
diff --git a/java/ivy.xml b/java/ivy.xml
index 159f947..6c38eb8 100644
--- a/java/ivy.xml
+++ b/java/ivy.xml
@@ -59,10 +59,12 @@
         <dependency org="redhat" name="servletapi5" rev="5.0.18" />
         <dependency org="redhat" name="sitemesh" rev="2.1" />
         <dependency org="redhat" name="simple-core" rev="3.1.3" />
+        <dependency org="redhat" name="simple-xml" rev="2.6.2" />
         <dependency org="redhat" name="smtp" rev="1.3.1" />
         <dependency org="redhat" name="stringtree-json" rev="2.0.9" />
         <dependency org="redhat" name="struts" rev="1.2.9" />
         <dependency org="redhat" name="strutstest" rev="2.1.3" />
+        <dependency org="redhat" name="susestudio-java-client" rev="0.1.2" />
         <dependency org="redhat" name="taglibs-core" rev="1.1.0" />
         <dependency org="redhat" name="taglibs-standard" rev="1.1.0" />
         <dependency org="redhat" name="tanukiwrapper" rev="3.1.2" />
diff --git a/java/spacewalk-java.spec b/java/spacewalk-java.spec
index d884638..595c9a2 100644
--- a/java/spacewalk-java.spec
+++ b/java/spacewalk-java.spec
@@ -81,8 +81,10 @@ Requires: struts-taglib >= 0:1.3.0
 Requires: xalan-j2 >= 0:2.6.0
 Requires: xerces-j2
 Requires: simple-core
+Requires: simple-xml
 Requires: sitemesh
 Requires: stringtree-json
+Requires: susestudio-java-client
 Requires: spacewalk-java-config
 Requires: spacewalk-java-lib
 Requires: spacewalk-java-jdbc
@@ -137,7 +139,9 @@ BuildRequires: redstone-xmlrpc
 BuildRequires: oscache
 BuildRequires: quartz
 BuildRequires: simple-core
+BuildRequires: simple-xml
 BuildRequires: stringtree-json
+BuildRequires: susestudio-java-client
 # EL5 = Struts 1.2 and Tomcat 5, EL6+/recent Fedoras = 1.3 and Tomcat 6
 %if 0%{?rhel} && 0%{?rhel} < 6
 BuildRequires: struts >= 0:1.2.9
@@ -520,8 +524,10 @@ fi
 %{jardir}/redstone-xmlrpc.jar
 %{jardir}/rhn.jar
 %{jardir}/simple-core.jar
+%{jardir}/simple-xml.jar
 %{jardir}/sitemesh.jar
 %{jardir}/stringtree-json.jar
+%{jardir}/susestudio-java-client.jar
 %{jardir}/taglibs-core.jar
 %{jardir}/taglibs-standard.jar
 %{jardir}/tanukiwrapper.jar
diff --git a/web/html/javascript/images.js b/web/html/javascript/images.js
new file mode 100644
index 0000000..fe0a4ee
--- /dev/null
+++ b/web/html/javascript/images.js
@@ -0,0 +1,24 @@
+// These functions make use of prototype.js:
+function showForm(id, name, version, arch, type, editUrl) {
+    // Setup link for editing image
+    var nameEsc = name.escapeHTML();
+    $('edit-link').update(nameEsc);
+    $('edit-link').setAttribute('href', editUrl);
+    // Create string representation
+    var imgString = "\"" + nameEsc + "\", Version " + version + " (" + arch + ", " + type + ")";
+    $('image-string').update(imgString);
+    $('image-id').setValue(id);
+    // Show form fields and hide table
+    $('deployment-form').show();
+    $('images-table').hide();
+}
+// Show table and hide form fields
+function showImages() {
+    $('images-table').show();
+    $('deployment-form').hide();
+    // Reset elements
+    $('image-id').setValue('');
+    $('image-string').update();
+    $('edit-link').setAttribute('href', '');
+    $('edit-link').update();
+}


commit 2bcdf2a4a6fbf1f37640f5e1a31b769899bbdc21
Author: Johannes Renner <jrenner at suse.de>
Date:   Fri Mar 30 15:13:38 2012 +0200

    Add support for studio image deployments (client)

diff --git a/client/tools/rhn-virtualization/actions/image.py b/client/tools/rhn-virtualization/actions/image.py
new file mode 100644
index 0000000..4a81707
--- /dev/null
+++ b/client/tools/rhn-virtualization/actions/image.py
@@ -0,0 +1,273 @@
+#!/usr/bin/env python
+import base64
+import ConfigParser
+import hashlib
+import itertools
+import os
+import pycurl
+import re
+import sys
+
+sys.path.append("/usr/share/rhn/")
+import virtualization.support as virt_support
+from virtualization.util import generate_uuid
+from up2date_client import up2dateLog
+
+log = up2dateLog.initLog()
+
+config = ConfigParser.ConfigParser({
+    "IMAGE_BASE_PATH"      : "/var/lib/libvirt/images/",
+    "IMAGE_CFG_TEMPLATE"   : "/etc/sysconfig/rhn/studio-kvm-template.xml",
+    "PRE_SCRIPT"           : "",
+})
+config.read('/etc/sysconfig/rhn/image.cfg')
+IMAGE_BASE_PATH    = config.get("global", "IMAGE_BASE_PATH")
+IMAGE_CFG_TEMPLATE = config.get("global", "IMAGE_CFG_TEMPLATE")
+PRE_SCRIPT         = config.get("global", "PRE_SCRIPT")
+
+# mark this module as acceptable
+__rhnexport__ = [
+    'deploy'
+]
+
+# download and extract tar.gz file with image
+def _downloadFile(imageName,serverUrl,proxySetting):
+    log.log_debug("downloading %s" % serverUrl)
+
+    # get the file via pycurl
+    c = pycurl.Curl()
+    c.setopt(pycurl.URL, serverUrl)
+
+    # proxySetting : { proxyURL  : http://myproxy.site:3128
+    #                  proxyUser : user
+    #                  proxyPass : s3cr3t }
+    # proxyUser and proxyPass are optional
+    #
+    if proxySetting.has_key("proxyURL") and proxySetting["proxyURL"] != None and proxySetting["proxyURL"] != "":
+        server = proxySetting["proxyURL"]
+        c.setopt(pycurl.PROXY, server )
+        if proxySetting.has_key("proxyUser") and proxySetting["proxyUser"] != None and proxySetting["proxyUser"] != "":
+            user     = proxySetting["proxyUser"]
+            password = base64.b64decode( proxySetting["proxyPass"] )
+            c.setopt(pycurl.PROXYUSERPWD, "%s:%s" % (user,password) )
+    # default IMAGE_BASE_PATH is /var/lib/libvirt/images
+    filePath = "/%s/%s" % (IMAGE_BASE_PATH, imageName)
+    f = open(filePath, 'w')
+    c.setopt(pycurl.WRITEFUNCTION, f.write)
+    c.setopt(pycurl.SSL_VERIFYPEER, 0)
+    c.perform()
+    log.log_debug("curl got HTTP code: %s" % c.getinfo(pycurl.HTTP_CODE))
+    f.close()
+    return c.getinfo(pycurl.HTTP_CODE)
+
+def _connect_to_hypervisor():
+    """
+    Connects to the hypervisor.
+    """
+    # First, attempt to import libvirt.  If we don't have that, we can't do
+    # much else.
+    try:
+        import libvirt
+    except ImportError, ie:
+        raise VirtLibNotFoundException, \
+              "Unable to locate libvirt: %s" % str(ie)
+
+    # Attempt to connect to the hypervisor.
+    try:
+        connection = libvirt.open(None)
+    except Exception, e:
+        raise VirtualizationKickstartException, \
+              "Could not connect to hypervisor: %s" % str(e)
+
+    return connection
+
+#
+# this is not nice but tarfile.py does not support
+# sparse file writing :(
+#
+def _extractImage( source, dest ):
+    param = "xf"
+    if not os.path.exists( source ):
+        log.log_debug("source file not found: %s" % source)
+        raise Exception("source file not found: %s" % source)
+
+    if not os.path.exists( dest ):
+        log.log_debug("target path not found: %s" % dest)
+        raise Exception("target path not found: %s" % dest)
+
+    if( source.endswith("gz") ):
+        param = param + "z"
+    elif( source.endswith("bz2") ):
+        param = param + "j"
+
+    # skip the root directory in the tar - extract only the image files
+    cmd = "tar %s %s -C %s --strip-components=1" % ( param, source, dest )
+    log.log_debug(cmd)
+    if os.system( cmd ) != 0:
+        log.log_debug( "%s failed" % cmd )
+        raise Exception("%s failed" % cmd)
+
+    return 0
+
+def _md5(path):
+    f = open(path, "rb")
+    sum = hashlib.md5()
+    while 1:
+        block = f.read(128)
+        if not block:
+            break
+        sum.update(block)
+    f.close()
+    return sum.hexdigest()
+
+
+def _fileExists(name, md5Sum):
+    return os.path.exists( name ) and md5Sum == _md5( name )
+
+def _domainExists( dom, connection ):
+    try:
+        connection.lookupByName(dom)
+    except Exception, e:
+        log.log_debug("domain %s does not exist" % dom)
+        return False
+    log.log_debug("domain %s exists" % dom)
+    return True
+
+# create a new or reuse an existing directory
+def _createTargetDir( wantedDir ):
+    new_dir_name = wantedDir
+    for i in itertools.count(1):
+        if not os.path.exists(new_dir_name):
+            try:
+                os.makedirs( new_dir_name )
+            except OSError, exc:
+                return (1, "creating directory %s failed" % new_dir_name)
+            return new_dir_name
+        elif len(os.listdir(new_dir_name)) <= 1:
+            # if the directory exists with zero or only one file, we'll use it
+            # to extract the image into it
+            return new_dir_name
+        new_dir_name = "%s-%i" % ( wantedDir, i )
+
+# fillout the variables in the XML template file
+def _generateXML( params ):
+    if PRE_SCRIPT != "":
+        log.log_debug("running image pre-script %s" % PRE_SCRIPT)
+        os.system( PRE_SCRIPT )
+
+    if os.path.isfile(IMAGE_CFG_TEMPLATE):
+        f = open(IMAGE_CFG_TEMPLATE, 'r')
+        CREATE_TEMPLATE = f.read()
+        f.close()
+
+    created_xml = CREATE_TEMPLATE % params
+    log.log_debug("libvirt XML: %s" % created_xml)
+    return created_xml
+
+# download/extract and start a new image
+# imageName = myImage.x86_64.
+#
+# downloadURL  : http://susestudio.com/download/f98.../my_image.i686-0.0.3.vmx.tar.gz
+# proxySetting : { proxyURL  : http://myproxy.site:3128
+#                  proxyUser : user
+#                  proxyPass : s3cr3t }
+# memKB      : 524288
+# vCPUs      : 1
+# domainName : virt_test_machine
+# virtBridge : br0
+#
+def deploy(params, extraParams="",cache_only=None):
+    """start and connect a local image with SUSE Manager"""
+
+    urlParts  = params["downloadURL"].split('/')
+    studioArchiveFileName  = urlParts[-1]
+    checksum  = urlParts[-2]
+
+    # studioArchiveFileName = workshop_test_sles11sp1.i686-0.0.1.vmx.tar.gz
+    # studioArchiveFileName = Just_enough_OS_openSUSE_12.1.x86_64-0.0.1.xen.tar.gz
+    m = re.search( '(.*)\.(x86_64|i\d86)-(\d+\.\d+\.\d+)\.(xen|vmx)', studioArchiveFileName )
+
+    imageName    = m.group(1)
+    imageArch    = m.group(2)
+    imageVersion = m.group(3)
+    imageType    = m.group(4)
+    studioImageDiskFileName = imageName+"."+imageArch+"-"+imageVersion
+
+    connection = _connect_to_hypervisor()
+
+    # if we got an explicit name, we'll use it
+    if params.has_key("domainName") and params["domainName"] != "":
+        imageName = params["domainName"]
+    # if not, we'll try to find a free name
+    elif( _domainExists(imageName, connection) ):
+        for i in itertools.count(1):
+            newImageName = ("%s-%i" % (imageName,i))
+            if not _domainExists(newImageName, connection):
+                log.log_debug("free domain found")
+                imageName = newImageName
+                break
+    log.log_debug( "name=%s arch=%s ver=%s type=%s" % (imageName,imageArch,imageVersion,imageType) )
+
+    if len(imageName) < 1 or len(imageArch) < 1:
+        log.log_debug("invalid image name or arch")
+        return (1, "invalid image name or arch: name=%s arch=%s ver=%s type=%s" % (imageName,imageArch,imageVersion,imageType), {})
+
+    httpResponseCode = -1
+    if not _fileExists("%s/%s" % (IMAGE_BASE_PATH,studioArchiveFileName), checksum):
+        try:
+            httpResponseCode = _downloadFile(studioArchiveFileName,params["downloadURL"],params["proxySettings"])
+            if not _fileExists("%s/%s" % (IMAGE_BASE_PATH,studioArchiveFileName), checksum):
+                log.log_debug("downloading image file failed. HTTP Code is: %s" % httpResponseCode)
+                return (1, "downloading image file failed: %s/%s (%s)" % (IMAGE_BASE_PATH, studioArchiveFileName,httpResponseCode), {})
+        except Exception, e:
+            return ( 1, "getting the image failed with: %s" % e )
+    if cache_only:
+        return (0, "image fetched and cached for later deployment", {})
+    try:
+        targetDir = _createTargetDir( "%s/%s" % (IMAGE_BASE_PATH, imageName) )
+        _extractImage( "%s/%s" % (IMAGE_BASE_PATH,studioArchiveFileName), targetDir )
+    except Exception, e:
+        return (1, "extracting the image tarball failed with: %s" % e, {})
+
+    # image exists in $IMAGE_BASE_PATH/$imageName now
+
+    uuid = generate_uuid()
+    # FIXME: check for the extensions. There might be more
+    studioFileExtension = "vmdk"
+    if imageType == "xen":
+        studioFileExtension = "raw"
+    extractedImagePath = "%s/%s.%s" % (targetDir,studioImageDiskFileName,studioFileExtension)
+    log.log_debug("working on image in %s" % extractedImagePath)
+    if not os.path.exists( extractedImagePath ):
+        return (1, "extracted image not found at %s" % extractedImagePath, {})
+    if imageArch in ( 'i386', 'i486', 'i568' ):
+        imageArch = 'i686'
+
+    create_params = { 'name'           : imageName,
+                      'arch'           : imageArch,
+                      'extra'          : extraParams,
+                      'mem_kb'         : params["memKB"],
+                      'vcpus'          : params["vCPUs"],
+                      'uuid'           : uuid,
+                      'disk'           : extractedImagePath,
+                      'imageType'      : imageType,
+                      'virtBridge'     : params["virtBridge"],
+                    }
+    create_xml = _generateXML( create_params )
+    domain = None
+    try:
+        domain = connection.defineXML(create_xml)
+    except Exception, e:
+        return (1, "failed to pass XML to libvirt: %s" % e, {})
+
+    domain.create()
+    virt_support.refresh()
+
+    return (0, "image '%s' deployed and started" % imageName, {})
+
+# just for testing
+if __name__ == "__main__":
+    # test code
+    log.log_debug("actions/image.py called")
+    print "You can not run this module by itself"
+    sys.exit(-1)
diff --git a/client/tools/rhn-virtualization/rhn-virtualization.spec b/client/tools/rhn-virtualization/rhn-virtualization.spec
index f97407c..4976540 100644
--- a/client/tools/rhn-virtualization/rhn-virtualization.spec
+++ b/client/tools/rhn-virtualization/rhn-virtualization.spec
@@ -48,8 +48,10 @@ Requires: libvirt-python
 Requires: rhn-virtualization-common = %{version}-%{release}
 %if 0%{?suse_version}
 Requires: cron
+Requires: python-curl
 %else
 Requires: /usr/sbin/crond
+Requires: python-pycurl
 %endif
 %if 0%{?rhel} && 0%{?rhel} < 6
 # in RHEL5 we need libvirt, but in RHEV at RHEL5 there should not be libvirt
@@ -90,6 +92,11 @@ rm -rf $RPM_BUILD_ROOT
 %else
 /sbin/service crond condrestart
 %endif
+if [ -d /proc/xen ]; then
+    # xen kernel is running
+    # change the default template to the xen version
+    sed -i 's@^IMAGE_CFG_TEMPLATE=/etc/sysconfig/rhn/studio-kvm-template.xml at IMAGE_CFG_TEMPLATE=/etc/sysconfig/rhn/studio-xen-template.xml@' /etc/sysconfig/rhn/image.cfg
+fi
 
 %preun host
 if [ $1 = 0 ]; then
@@ -114,6 +121,9 @@ fi
 %doc LICENSE
 
 %files host
+%if 0%{?suse_version}
+%dir %{rhn_conf_dir}
+%endif
 %dir %{rhn_conf_dir}/virt
 %dir %{rhn_conf_dir}/virt/auto
 %{_initrddir}/rhn-virtualization-host
@@ -130,7 +140,10 @@ fi
 %{rhn_dir}/virtualization/state.py*
 %{rhn_dir}/virtualization/support.py*
 %{rhn_dir}/actions/virt.py*
+%{rhn_dir}/actions/image.py*
 %{rhn_dir}/virtualization/localvdsm.py*
+%{rhn_conf_dir}/studio-*-template.xml
+%config(noreplace) %{rhn_conf_dir}/image.cfg
 %doc LICENSE
 
 %changelog
diff --git a/client/tools/rhn-virtualization/scripts/Makefile b/client/tools/rhn-virtualization/scripts/Makefile
index 0bf7d23..cb8f587 100644
--- a/client/tools/rhn-virtualization/scripts/Makefile
+++ b/client/tools/rhn-virtualization/scripts/Makefile
@@ -1,10 +1,12 @@
 PKGDIR0 = /etc/rc.d/init.d
 PKGDIR1 = /etc/cron.d
+SYSCONF_DIR = /etc/sysconfig/rhn
 CONF_DIR = /etc/sysconfig/rhn/virt
 CONF_DIR_AUTO = /etc/sysconfig/rhn/virt/auto
 
 SCRIPTS = rhn-virtualization-host
 OTHER = rhn-virtualization.cron
+CONF = studio-kvm-template.xml studio-xen-template.xml image.cfg
 
 all: 
 	echo "Nothing to do"
@@ -17,9 +19,13 @@ install:
 	mkdir -p $(DESTDIR)/$(PKGDIR1)
 	mkdir -p $(DESTDIR)/$(CONF_DIR)
 	mkdir -p $(DESTDIR)/$(CONF_DIR_AUTO)
+	mkdir -p $(DESTDIR)/$(SYSCONF_DIR)
 	for p in $(SCRIPTS) ; do \
 		install -m 755 $$p $(DESTDIR)/$(PKGDIR0)/$$p; \
 	done
 	for q in $(OTHER) ; do \
 		install -m 755 $$q $(DESTDIR)/$(PKGDIR1)/$$q; \
 	done
+	for r in $(CONF) ; do \
+		install -m 644 $$r $(DESTDIR)/$(SYSCONF_DIR)/$$r; \
+	done
diff --git a/client/tools/rhn-virtualization/scripts/image.cfg b/client/tools/rhn-virtualization/scripts/image.cfg
new file mode 100644
index 0000000..ee573e2
--- /dev/null
+++ b/client/tools/rhn-virtualization/scripts/image.cfg
@@ -0,0 +1,4 @@
+[global]
+IMAGE_BASE_PATH=/var/lib/libvirt/images/
+IMAGE_CFG_TEMPLATE=/etc/sysconfig/rhn/studio-kvm-template.xml
+PRE_SCRIPT=
diff --git a/client/tools/rhn-virtualization/scripts/studio-kvm-template.xml b/client/tools/rhn-virtualization/scripts/studio-kvm-template.xml
new file mode 100644
index 0000000..42ac7dd
--- /dev/null
+++ b/client/tools/rhn-virtualization/scripts/studio-kvm-template.xml
@@ -0,0 +1,41 @@
+<domain type='kvm'>
+  <name>%(name)s</name>
+  <uuid>%(uuid)s</uuid>
+  <memory>%(mem_kb)s</memory>
+  <vcpu>%(vcpus)s</vcpu>
+  <os>
+    <type arch='%(arch)s' machine='pc-0.12'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>restart</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-kvm</emulator>
+    <disk type='file' device='disk'>
+      <driver name='qemu' type='vmdk'/>
+      <source file='%(disk)s'/>
+      <target dev='hda' bus='ide'/>
+      <address type='drive' controller='0' bus='0' unit='0'/>
+    </disk>
+    <controller type='ide' index='0'/>
+
+    <!-- if you don't want a bridge, remove this interface -->
+    <interface type='bridge'>
+      <source bridge='%(virtBridge)s'/>
+    </interface>
+
+    <serial type='pty'>
+      <target port='0'/>
+    </serial>
+    <console type='pty'>
+      <target port='0'/>
+    </console>
+    <input type='mouse' bus='ps2'/>
+    <graphics type='vnc' port='-1' autoport='yes' keymap='en-us'/>
+    <video>
+      <model type='cirrus' vram='9216' heads='1'/>
+    </video>
+  </devices>
+</domain>
diff --git a/client/tools/rhn-virtualization/scripts/studio-xen-template.xml b/client/tools/rhn-virtualization/scripts/studio-xen-template.xml
new file mode 100644
index 0000000..0894e7c
--- /dev/null
+++ b/client/tools/rhn-virtualization/scripts/studio-xen-template.xml
@@ -0,0 +1,33 @@
+<domain type='xen'>
+  <name>%(name)s</name>
+  <uuid>%(uuid)s</uuid>
+  <memory>%(mem_kb)s</memory>
+  <vcpu>%(vcpus)s</vcpu>
+  <bootloader>/usr/bin/pygrub</bootloader>
+  <os>
+    <type arch='%(arch)s' machine='xenpv'>linux</type>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>restart</on_crash>
+  <devices>
+    <disk type='file' device='disk'>
+      <driver name='tap' type='aio'/>
+      <source file='%(disk)s'/>
+      <target dev='xvda' bus='xen'/>
+      <address type='drive' controller='0' bus='0' unit='0'/>
+    </disk>
+
+    <!-- if you don't want a bridge, remove this interface -->
+    <interface type='bridge'>
+      <source bridge='%(virtBridge)s'/>
+    </interface>
+
+    <console type='pty'>
+      <target port='0'/>
+    </console>
+    <input type='mouse' bus='xen'/>
+    <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'/>
+  </devices>
+</domain>
diff --git a/client/tools/rhn-virtualization/virtualization/util.py b/client/tools/rhn-virtualization/virtualization/util.py
index 79f7a80..c19aa0c 100644
--- a/client/tools/rhn-virtualization/virtualization/util.py
+++ b/client/tools/rhn-virtualization/virtualization/util.py
@@ -13,8 +13,15 @@
 # in this software or its documentation. 
 #
 
+import random
 from virtualization.errors import UUIDError
 
+def generate_uuid():
+    """Generate a random UUID and return it."""
+
+    uuid_list = [ random.randint(0, 255) for _ in range(0, 16) ]
+    return ("%02x" * 16) % tuple(uuid_list)
+
 def hyphenize_uuid(uuid):
     # Determine whether the string is already hyphenized.
     if len(uuid) == 36 and len(uuid.replace('-', '')) == 32:


commit 5bbb0a5578afdc244f6b483eee1689d91616f903
Author: Johannes Renner <jrenner at suse.de>
Date:   Thu Mar 15 15:19:05 2012 +0100

    Add support for studio image deployments (backend)

diff --git a/backend/server/action/Makefile b/backend/server/action/Makefile
index 764d73c..bb8d7e4 100644
--- a/backend/server/action/Makefile
+++ b/backend/server/action/Makefile
@@ -7,7 +7,7 @@ SUBDIR	= server/action
 
 SPACEWALK_FILES	= __init__ configfiles kickstart kickstart_guest packages errata hardware reboot \
           rollback activation solarispkgs script rhnsd rhn_applet virt kickstart_host \
-          utils scap
+          utils scap image
 
 include $(TOP)/Makefile.defs
 
diff --git a/backend/server/action/image.py b/backend/server/action/image.py
new file mode 100644
index 0000000..40f6542
--- /dev/null
+++ b/backend/server/action/image.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2011 Novell
+#
+# This software is licensed to you under the GNU General Public License,
+# version 2 (GPLv2). There is NO WARRANTY for this software, express or
+# implied, including the implied warranties of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+# along with this software; if not, see
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+#
+
+from spacewalk.common.rhnLog import log_debug
+from spacewalk.server import rhnSQL
+from spacewalk.server.rhnLib import InvalidAction
+
+# the "exposed" functions
+__rhnexport__ = ['deploy']
+
+# returns the values for deploying a virtual machine with an image
+#
+# file_name, checksum, mem_kb, vcpus, imageType
+#
+def deploy(serverId, actionId, dry_run=0):
+    log_debug(3)
+    statement = """
+        select aid.mem_kb, aid.vcpus, aid.bridge_device,aid.download_url,
+               aid.proxy_server, aid.proxy_user, aid.proxy_pass
+          from rhnActionImageDeploy aid
+	   where aid.action_id = :action_id"""
+    h = rhnSQL.prepare(statement)
+    h.execute(action_id = actionId)
+    row = h.fetchone_dict()
+    if not row:
+        # No image for this action
+        raise InvalidAction("image.deploy: No image found for action id "
+            "%s and server %s" % (actionId, serverId))
+
+    for key in [ 'download_url', 'proxy_server', 'proxy_user', 'proxy_pass', 'bridge_device' ]:
+        if row[key] == None:
+            row[key] = ""
+
+    params = {
+        "downloadURL"   : row['download_url'],
+        "proxySettings" : { "proxyURL" : row['proxy_server'], "proxyUser" : row['proxy_user'], "proxyPass" : row['proxy_pass'] },
+        "memKB"         : row['mem_kb'],
+        "vCPUs"         : row['vcpus'],
+        "domainName"    : "",
+        "virtBridge"    : row['bridge_device'] }
+    return (params)
diff --git a/backend/server/action_extra_data/Makefile b/backend/server/action_extra_data/Makefile
index d64e2a3..ec0631f 100644
--- a/backend/server/action_extra_data/Makefile
+++ b/backend/server/action_extra_data/Makefile
@@ -6,7 +6,7 @@ TOP	= ../..
 SUBDIR	= server/action_extra_data
 
 SPACEWALK_FILES	= __init__ configfiles kickstart kickstart_guest packages errata hardware reboot \
-          rollback up2date_config solarispkgs script rhnsd rhn_applet virt kickstart_host scap
+          rollback up2date_config solarispkgs script rhnsd rhn_applet virt kickstart_host scap image
 
 include $(TOP)/Makefile.defs
 
diff --git a/backend/server/action_extra_data/image.py b/backend/server/action_extra_data/image.py
new file mode 100644
index 0000000..ca3969d
--- /dev/null
+++ b/backend/server/action_extra_data/image.py
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2008--2010 Red Hat, Inc.
+#
+# This software is licensed to you under the GNU General Public License,
+# version 2 (GPLv2). There is NO WARRANTY for this software, express or
+# implied, including the implied warranties of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+# along with this software; if not, see
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+#
+# Red Hat trademarks are not licensed under GPLv2. No permission is
+# granted to use or replicate Red Hat trademarks that are incorporated
+# in this software or its documentation.
+#
+# errata-related error handling functions
+#
+
+from spacewalk.common.rhnLog import log_debug
+
+# the "exposed" functions
+__rhnexport__ = ['deploy']
+
+def deploy(server_id, action_id, data={}):
+    if not data:
+        return
+    log_debug("action_error.image.deploy: Should do something "
+        "useful with this data", server_id, action_id, data)


commit 43f410f04f42888c2105f09f8aae8ab2b72143fa
Author: Johannes Renner <jrenner at suse.de>
Date:   Fri Mar 30 14:23:08 2012 +0200

    Add support for studio image deployments (DB files)

diff --git a/schema/spacewalk/common/data/rhnActionType.sql b/schema/spacewalk/common/data/rhnActionType.sql
index a99c85a..f07aa89 100644
--- a/schema/spacewalk/common/data/rhnActionType.sql
+++ b/schema/spacewalk/common/data/rhnActionType.sql
@@ -70,6 +70,7 @@ insert into rhnActionType values (48, 'virt.setVCPUs', 'Sets the Vcpu usage for
 commit;
 insert into rhnActionType values (49, 'proxy.deactivate', 'Deactivate Proxy', 'N', 'N');
 insert into rhnActionType values (50, 'scap.xccdf_eval', 'OpenSCAP xccdf scanning', 'N', 'Y');
+insert into rhnActionType values (500, 'image.deploy', 'Deploy an image to a virtual host.', 'N', 'N');
 --
 --
 -- Revision 1.25  2004/10/29 05:07:52  pjones
diff --git a/schema/spacewalk/common/data/suseCredentialsType.sql b/schema/spacewalk/common/data/suseCredentialsType.sql
new file mode 100644
index 0000000..892eded
--- /dev/null
+++ b/schema/spacewalk/common/data/suseCredentialsType.sql
@@ -0,0 +1,18 @@
+--
+-- Copyright (c) 2012 Novell
+--
+-- This software is licensed to you under the GNU General Public License,
+-- version 2 (GPLv2). There is NO WARRANTY for this software, express or
+-- implied, including the implied warranties of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+-- along with this software; if not, see
+-- http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+--
+-- Red Hat trademarks are not licensed under GPLv2. No permission is
+-- granted to use or replicate Red Hat trademarks that are incorporated
+-- in this software or its documentation.
+--
+
+insert into suseCredentialsType (id, label, name) values
+	(sequence_nextval('suse_credtype_id_seq'), 'susestudio', 'SUSE Studio');
+commit;
diff --git a/schema/spacewalk/common/tables/rhnActionImageDeploy.sql b/schema/spacewalk/common/tables/rhnActionImageDeploy.sql
new file mode 100644
index 0000000..c7daaa9
--- /dev/null
+++ b/schema/spacewalk/common/tables/rhnActionImageDeploy.sql
@@ -0,0 +1,34 @@
+--
+-- Copyright (c) 2012 Novell
+--
+-- This software is licensed to you under the GNU General Public License,
+-- version 2 (GPLv2). There is NO WARRANTY for this software, express or
+-- implied, including the implied warranties of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+-- along with this software; if not, see
+-- http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+--
+-- Red Hat trademarks are not licensed under GPLv2. No permission is
+-- granted to use or replicate Red Hat trademarks that are incorporated
+-- in this software or its documentation.
+--
+
+CREATE TABLE rhnActionImageDeploy
+(
+    id            NUMBER NOT NULL PRIMARY KEY,
+    action_id     NUMBER NOT NULL
+                      CONSTRAINT rhn_act_idp_act_fk
+                      REFERENCES rhnAction (id)
+                      ON DELETE CASCADE,
+    vcpus         NUMBER NOT NULL,
+    mem_kb        NUMBER NOT NULL,
+    bridge_device VARCHAR2(32),
+    download_url  VARCHAR2(256) NOT NULL,
+    proxy_server  VARCHAR2(64),
+    proxy_user    VARCHAR2(32),
+    proxy_pass    VARCHAR2(64)
+)
+ENABLE ROW MOVEMENT
+;
+
+CREATE SEQUENCE rhn_action_image_deploy_id_seq;
diff --git a/schema/spacewalk/common/tables/suseCredentials.sql b/schema/spacewalk/common/tables/suseCredentials.sql
new file mode 100644
index 0000000..9cad567
--- /dev/null
+++ b/schema/spacewalk/common/tables/suseCredentials.sql
@@ -0,0 +1,36 @@
+--
+-- Copyright (c) 2012 Novell
+--
+-- This software is licensed to you under the GNU General Public License,
+-- version 2 (GPLv2). There is NO WARRANTY for this software, express or
+-- implied, including the implied warranties of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+-- along with this software; if not, see
+-- http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+--
+-- Red Hat trademarks are not licensed under GPLv2. No permission is
+-- granted to use or replicate Red Hat trademarks that are incorporated
+-- in this software or its documentation.
+--
+
+CREATE TABLE suseCredentials
+(
+    id       NUMBER NOT NULL
+                 CONSTRAINT suse_credentials_pk PRIMARY KEY,
+    user_id  NUMBER NOT NULL
+                 CONSTRAINT suse_credentials_user_fk
+                 REFERENCES web_contact (id)
+                 ON DELETE CASCADE,
+    type_id  NUMBER NOT NULL
+                 CONSTRAINT suse_credentials_type_fk
+                 REFERENCES suseCredentialsType (id),
+    url      VARCHAR2(256),
+    username VARCHAR2(64) NOT NULL,
+    password VARCHAR2(64) NOT NULL,
+    created  DATE DEFAULT (sysdate) NOT NULL,
+    modified DATE DEFAULT (sysdate) NOT NULL
+)
+ENABLE ROW MOVEMENT
+;
+
+CREATE SEQUENCE suse_credentials_id_seq;
diff --git a/schema/spacewalk/common/tables/suseCredentialsType.sql b/schema/spacewalk/common/tables/suseCredentialsType.sql
new file mode 100644
index 0000000..dc9cae4
--- /dev/null
+++ b/schema/spacewalk/common/tables/suseCredentialsType.sql
@@ -0,0 +1,38 @@
+--
+-- Copyright (c) 2012 Novell
+--
+-- This software is licensed to you under the GNU General Public License,
+-- version 2 (GPLv2). There is NO WARRANTY for this software, express or
+-- implied, including the implied warranties of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+-- along with this software; if not, see
+-- http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+--
+-- Red Hat trademarks are not licensed under GPLv2. No permission is
+-- granted to use or replicate Red Hat trademarks that are incorporated
+-- in this software or its documentation.
+--
+
+CREATE TABLE suseCredentialsType
+(
+    id        NUMBER NOT NULL
+                  CONSTRAINT suse_credtype_id_pk PRIMARY KEY
+                  USING INDEX TABLESPACE [[64k_tbs]],
+    label     VARCHAR2(64) NOT NULL,
+    name      VARCHAR2(128) NOT NULL,
+    created   DATE
+                  DEFAULT (sysdate) NOT NULL,
+    modified  DATE
+                  DEFAULT (sysdate) NOT NULL
+)
+ENABLE ROW MOVEMENT
+;
+
+CREATE INDEX suse_credtype_label_id_idx
+    ON suseCredentialsType (label, id)
+    TABLESPACE [[64k_tbs]];
+
+CREATE SEQUENCE suse_credtype_id_seq;
+
+ALTER TABLE suseCredentialsType
+    ADD CONSTRAINT suse_credtype_label_uq UNIQUE (label);
diff --git a/schema/spacewalk/common/tables/tables.deps b/schema/spacewalk/common/tables/tables.deps
index 9d83eea..c0c1c3c 100644
--- a/schema/spacewalk/common/tables/tables.deps
+++ b/schema/spacewalk/common/tables/tables.deps
@@ -27,6 +27,7 @@ rhnActionConfigRevision        :: rhnAction rhnServer rhnConfigRevision rhnConfi
 rhnActionConfigRevisionResult  :: rhnActionConfigRevision
 rhnActionDaemonConfig          :: rhnAction
 rhnActionErrataUpdate          :: rhnAction rhnErrata
+rhnActionImageDeploy           :: rhnAction
 rhnActionKickstart             :: rhnAction rhnKickstartableTree
 rhnActionKickstartFileList     :: rhnActionKickstart rhnFileList
 rhnActionKickstartGuest        :: rhnActionKickstart rhnKickstartSession rhnKickstartableTree 
@@ -250,6 +251,7 @@ rhn_snmp_alert                 :: rhn_sat_cluster
 rhn_units                      :: rhn_quanta
 rhn_url_probe                  :: rhn_probe 
 rhn_url_probe_step             :: rhn_url_probe 
+suseCredentials                :: suseCredentialsType web_contact
 time_series_data               :: time_series_purge
 time_series_purge              :: rhn_probe
 web_contact                    :: web_customer
diff --git a/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/100-rhnActionImageDeploy_create.sql b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/100-rhnActionImageDeploy_create.sql
new file mode 100644
index 0000000..c7daaa9
--- /dev/null
+++ b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/100-rhnActionImageDeploy_create.sql
@@ -0,0 +1,34 @@
+--
+-- Copyright (c) 2012 Novell
+--
+-- This software is licensed to you under the GNU General Public License,
+-- version 2 (GPLv2). There is NO WARRANTY for this software, express or
+-- implied, including the implied warranties of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+-- along with this software; if not, see
+-- http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+--
+-- Red Hat trademarks are not licensed under GPLv2. No permission is
+-- granted to use or replicate Red Hat trademarks that are incorporated
+-- in this software or its documentation.
+--
+
+CREATE TABLE rhnActionImageDeploy
+(
+    id            NUMBER NOT NULL PRIMARY KEY,
+    action_id     NUMBER NOT NULL
+                      CONSTRAINT rhn_act_idp_act_fk
+                      REFERENCES rhnAction (id)
+                      ON DELETE CASCADE,
+    vcpus         NUMBER NOT NULL,
+    mem_kb        NUMBER NOT NULL,
+    bridge_device VARCHAR2(32),
+    download_url  VARCHAR2(256) NOT NULL,
+    proxy_server  VARCHAR2(64),
+    proxy_user    VARCHAR2(32),
+    proxy_pass    VARCHAR2(64)
+)
+ENABLE ROW MOVEMENT
+;
+
+CREATE SEQUENCE rhn_action_image_deploy_id_seq;
diff --git a/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/101-rhnActionType_image-deploy_insert.sql b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/101-rhnActionType_image-deploy_insert.sql
new file mode 100644
index 0000000..34e7ffa
--- /dev/null
+++ b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/101-rhnActionType_image-deploy_insert.sql
@@ -0,0 +1,17 @@
+--
+-- Copyright (c) 2012 Novell
+--
+-- This software is licensed to you under the GNU General Public License,
+-- version 2 (GPLv2). There is NO WARRANTY for this software, express or
+-- implied, including the implied warranties of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+-- along with this software; if not, see
+-- http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+--
+-- Red Hat trademarks are not licensed under GPLv2. No permission is
+-- granted to use or replicate Red Hat trademarks that are incorporated
+-- in this software or its documentation.
+--
+
+insert into rhnActionType values (500, 'image.deploy', 'Deploy an image to a virtual host.', 'N', 'N');
+commit;
diff --git a/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/102-suseCredentialsType_create.sql b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/102-suseCredentialsType_create.sql
new file mode 100644
index 0000000..dc9cae4
--- /dev/null
+++ b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/102-suseCredentialsType_create.sql
@@ -0,0 +1,38 @@
+--
+-- Copyright (c) 2012 Novell
+--
+-- This software is licensed to you under the GNU General Public License,
+-- version 2 (GPLv2). There is NO WARRANTY for this software, express or
+-- implied, including the implied warranties of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+-- along with this software; if not, see
+-- http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+--
+-- Red Hat trademarks are not licensed under GPLv2. No permission is
+-- granted to use or replicate Red Hat trademarks that are incorporated
+-- in this software or its documentation.
+--
+
+CREATE TABLE suseCredentialsType
+(
+    id        NUMBER NOT NULL
+                  CONSTRAINT suse_credtype_id_pk PRIMARY KEY
+                  USING INDEX TABLESPACE [[64k_tbs]],
+    label     VARCHAR2(64) NOT NULL,
+    name      VARCHAR2(128) NOT NULL,
+    created   DATE
+                  DEFAULT (sysdate) NOT NULL,
+    modified  DATE
+                  DEFAULT (sysdate) NOT NULL
+)
+ENABLE ROW MOVEMENT
+;
+
+CREATE INDEX suse_credtype_label_id_idx
+    ON suseCredentialsType (label, id)
+    TABLESPACE [[64k_tbs]];
+
+CREATE SEQUENCE suse_credtype_id_seq;
+
+ALTER TABLE suseCredentialsType
+    ADD CONSTRAINT suse_credtype_label_uq UNIQUE (label);
diff --git a/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/103-suseCredentialsType_insert.sql b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/103-suseCredentialsType_insert.sql
new file mode 100644
index 0000000..892eded
--- /dev/null
+++ b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/103-suseCredentialsType_insert.sql
@@ -0,0 +1,18 @@
+--
+-- Copyright (c) 2012 Novell
+--
+-- This software is licensed to you under the GNU General Public License,
+-- version 2 (GPLv2). There is NO WARRANTY for this software, express or
+-- implied, including the implied warranties of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+-- along with this software; if not, see
+-- http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+--
+-- Red Hat trademarks are not licensed under GPLv2. No permission is
+-- granted to use or replicate Red Hat trademarks that are incorporated
+-- in this software or its documentation.
+--
+
+insert into suseCredentialsType (id, label, name) values
+	(sequence_nextval('suse_credtype_id_seq'), 'susestudio', 'SUSE Studio');
+commit;
diff --git a/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/104-suseCredentials_create.sql b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/104-suseCredentials_create.sql
new file mode 100644
index 0000000..9cad567
--- /dev/null
+++ b/schema/spacewalk/upgrade/spacewalk-schema-1.7-to-spacewalk-schema-1.8/104-suseCredentials_create.sql
@@ -0,0 +1,36 @@
+--
+-- Copyright (c) 2012 Novell
+--
+-- This software is licensed to you under the GNU General Public License,
+-- version 2 (GPLv2). There is NO WARRANTY for this software, express or
+-- implied, including the implied warranties of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+-- along with this software; if not, see
+-- http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+--
+-- Red Hat trademarks are not licensed under GPLv2. No permission is
+-- granted to use or replicate Red Hat trademarks that are incorporated
+-- in this software or its documentation.
+--
+
+CREATE TABLE suseCredentials
+(
+    id       NUMBER NOT NULL
+                 CONSTRAINT suse_credentials_pk PRIMARY KEY,
+    user_id  NUMBER NOT NULL
+                 CONSTRAINT suse_credentials_user_fk
+                 REFERENCES web_contact (id)
+                 ON DELETE CASCADE,
+    type_id  NUMBER NOT NULL
+                 CONSTRAINT suse_credentials_type_fk
+                 REFERENCES suseCredentialsType (id),
+    url      VARCHAR2(256),
+    username VARCHAR2(64) NOT NULL,
+    password VARCHAR2(64) NOT NULL,
+    created  DATE DEFAULT (sysdate) NOT NULL,
+    modified DATE DEFAULT (sysdate) NOT NULL
+)
+ENABLE ROW MOVEMENT
+;
+
+CREATE SEQUENCE suse_credentials_id_seq;




More information about the spacewalk-commits mailing list