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 a6fef0cecdb5b18e9ee00ca30737bcd00874cef9
Author: Johannes Renner <jrenner(a)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 <a
href="/rhn/account/Credentials.do">here</a>.</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 <a
href="/rhn/account/Credentials.do">here</a> 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
<span id="image-string"></span>.</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<span
class="required-form-field">*</span>:</source>
+ </trans-unit>
+ <trans-unit id="images.jsp.memory">
+ <source>Memory (MB)<span
class="required-form-field">*</span>:</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&q...
</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 ac674fd4bd4dd7458cd51fa9e424fa1df0ab5c8a
Author: Johannes Renner <jrenner(a)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@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@IMAGE_CFG_TEMPLATE=/etc/sysconfig/rhn/studio-xen-template.xml(a)'
/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 a7aee7324195f3b099f51d4d797d161ba9a6f6e7
Author: Johannes Renner <jrenner(a)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 c87783019dedf6989e8a5654bc937bab498b9323
Author: Johannes Renner <jrenner(a)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;