Hello all,

Attached is a patch that lets you play around with symlinks through the config management interface (rhbz#428190). It's a bit of a hack in the sense that it asks you to put the target of the symlink in the file contents. Also, a few things are unfinished or don't do what is expected with this patch.

Things to do/fix:
- asking for a diff dereferences the symlink. move the code from transaction.py to file_utils.py/process? - integrate with deploy/rollback. pretend symlinks are files in transaction.py? necessary with symlinks?
- hide the owner:group/mode fields?  always root:root/0777?
- when you click on "Symbolic link" in the web interface, hide the "File Contents" box and have a "Destination" input field appear? - on the Channel overview page, the incorrect count of files appears. where is this number calculated from? (how does it get into ConfigChannelDto.java?)

Anyone have some advice on the last one? Or any suggestions on the other ones, too.

Thanks,

Joshua Roys
>From d9db3bf39c890025234b8e1a121084c2b6ac0861 Mon Sep 17 00:00:00 2001
From: Joshua Roys <[email protected]>
Date: Tue, 4 Aug 2009 14:38:54 -0400
Subject: Add symlink capability to config management

---
 client/tools/rhncfg/config_common/transactions.py  |   21 ++++++++++++++++
 .../redhat/rhn/domain/config/ConfigFileType.java   |   13 ++++++++++
 .../redhat/rhn/domain/config/ConfigRevision.java   |    8 ++++++
 .../frontend/strings/jsp/StringResource_en_US.xml  |   16 +++++++++++-
 .../rhn/manager/configuration/ChannelSummary.java  |   18 +++++++++++++-
 .../configuration/ConfigurationManager.java        |   22 ++++++++++++++++-
 .../fragments/configuration/channel/create.jspf    |    3 ++
 .../fragments/configuration/channel/summary.jspf   |   25 ++++++++++++--------
 .../configuration/sdc/configfile_rows.jspf         |    8 ++++-
 .../pages/configuration/files/comparecopy.jsp      |    6 ++++-
 .../pages/configuration/overview/overview.jsp      |    7 ++++-
 .../WEB-INF/pages/configuration/ssm/difffiles.jsp  |    6 ++++-
 schema/spacewalk/common/data/rhnConfigFileType.sql |    1 +
 .../150-rhnConfigFileType_symlinks.sql             |    6 ++++
 14 files changed, 141 insertions(+), 19 deletions(-)
 create mode 100644 
schema/spacewalk/upgrade/spacewalk-schema-0.5-to-spacewalk-schema-0.6/150-rhnConfigFileType_symlinks.sql

diff --git a/client/tools/rhncfg/config_common/transactions.py 
b/client/tools/rhncfg/config_common/transactions.py
index 9b00e07..4a047e0 100644
--- a/client/tools/rhncfg/config_common/transactions.py
+++ b/client/tools/rhncfg/config_common/transactions.py
@@ -22,6 +22,7 @@ import errno
 import shutil
 import traceback
 import string
+import base64
 
 from config_common import file_utils, utils, cfg_exceptions
 from config_common.rhn_log import log_debug
@@ -163,6 +164,22 @@ class DeployTransaction:
             path = utils.normalize_path(self.transaction_root + os.sep + path)
         return path
 
+    def _do_symlink(self, file_info):
+       if os.path.exists(file_info.get('path')):
+               if os.path.islink(file_info.get('path')):
+                       os.unlink(file_info.get('path'))
+               else:
+                       raise Exception("Path exists and is not a symlink")
+
+       contents = file_info.get('file_contents')
+       encoding = file_info.get('encoding')
+
+       if contents and (encoding == 'base64'):
+               contents = base64.decodestring(contents)
+
+       # no return, but can throw an exception
+       os.symlink(contents, file_info.get('path'))
+
     def add_preprocessed(self, dest_path, processed_file_path, file_info, 
dirs_created, strict_ownership=1):
        """preprocess the file if needed, and add the entry to the correct 
list"""
         dest_path = self._normalize_path_to_root(dest_path)
@@ -177,6 +194,8 @@ class DeployTransaction:
         # assume file
        if file_info.get('filetype') == 'directory':
                self.dirs.append(file_info)
+       elif file_info.get('filetype') == 'symlink':
+               self._do_symlink(file_info)
        else:
                self._chown_chmod_chcon(processed_file_path, dest_path, 
file_info, strict_ownership=strict_ownership)
 
@@ -197,6 +216,8 @@ class DeployTransaction:
         # assume file
        if file_info.get('filetype') == 'directory':
            self.dirs.append(file_info)
+       elif file_info.get('filetype') == 'symlink':
+           self._do_symlink(file_info)
        else:
             self.files.append(file_info)
 
diff --git a/java/code/src/com/redhat/rhn/domain/config/ConfigFileType.java 
b/java/code/src/com/redhat/rhn/domain/config/ConfigFileType.java
index be3eba5..87668d1 100644
--- a/java/code/src/com/redhat/rhn/domain/config/ConfigFileType.java
+++ b/java/code/src/com/redhat/rhn/domain/config/ConfigFileType.java
@@ -50,8 +50,17 @@ public class ConfigFileType implements Serializable {
 
     public static final String FILE = "file";
     public static final String DIR = "directory";
+    public static final String SYMLINK = "symlink";
     private static final Map POSSIBLE_TYPES = new TreeMap(String.
                                                     CASE_INSENSITIVE_ORDER);
+
+    /**
+     * @return symlink config file type
+     */
+    public static ConfigFileType symlink() {
+        return lookup(SYMLINK);
+    }
+
     /**
      * 
      * @return dir config file type
@@ -80,11 +89,15 @@ public class ConfigFileType implements Serializable {
                                     lookupConfigFileTypeByLabel(FILE);
             ConfigFileType dir = ConfigurationFactory.
                             lookupConfigFileTypeByLabel(DIR);
+            ConfigFileType symlink = ConfigurationFactory.
+                            lookupConfigFileTypeByLabel(SYMLINK);
             POSSIBLE_TYPES.put(DIR, dir);
             POSSIBLE_TYPES.put("dir", dir);
             POSSIBLE_TYPES.put("folder", dir);
 
             POSSIBLE_TYPES.put(FILE, file);
+
+            POSSIBLE_TYPES.put(SYMLINK, symlink);
         }
         
         if (!POSSIBLE_TYPES.containsKey(type)) {
diff --git a/java/code/src/com/redhat/rhn/domain/config/ConfigRevision.java 
b/java/code/src/com/redhat/rhn/domain/config/ConfigRevision.java
index b0dd550..f47ffeb 100644
--- a/java/code/src/com/redhat/rhn/domain/config/ConfigRevision.java
+++ b/java/code/src/com/redhat/rhn/domain/config/ConfigRevision.java
@@ -217,4 +217,12 @@ public class ConfigRevision extends BaseDomainHelper {
     public boolean isDirectory() {
         return (configFileType != null && 
configFileType.getLabel().equals("directory"));
     }
+
+    /**
+     * Is this revision a symlink?
+     * @return true if file-type is 'symlink'
+     */
+    public boolean isSymlink() {
+        return (configFileType != null && 
configFileType.getLabel().equals("symlink"));
+    }
 }
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 f0b3c7b..6cfc742 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
@@ -12734,10 +12734,10 @@ the &lt;strong&gt;Red Hat Enterprise Linux System 
Administration Guide.&lt;/stro
      </trans-unit>
 
       <trans-unit id="channelOverview.jsp.numfiles">
-<source>{0} files, {1} directories</source>
+<source>{0} files, {1} directories, {2} symlinks</source>
         <context-group name="ctx">
           <context 
context-type="sourcefile">/rhn/configuration/ChannelOverview.do</context>
-                 <context context-type="paramnotes">"36 files, 2 
directories"</context>
+                 <context context-type="paramnotes">"36 files, 2 directories, 
0 symlinks"</context>
         </context-group>
      </trans-unit>
       <trans-unit id="channelOverview.jsp.numfiles-total">
@@ -13328,6 +13328,12 @@ but are not subscribed to 
_@@PRODUCT_NAME@@Config_.</context>
           <context 
context-type="sourcefile">/rhn/configuration/ChannelAddFiles.do</context>
         </context-group>
      </trans-unit>
+       <trans-unit id="addfiles.jsp.type.symlink">
+<source>Symbolic link</source>
+        <context-group name="ctx">
+          <context 
context-type="sourcefile">/rhn/configuration/ChannelAddFiles.do</context>
+        </context-group>
+     </trans-unit>
 
        <!-- channel deploy -->
       <trans-unit id="deployconfirm.jsp.confirmbutton">
@@ -13934,6 +13940,12 @@ centrally-managed configuration channel, {2}; below is 
a list of systems you may
                <context 
context-type="sourcefile">/rhn/configuration/FileDetails.do</context>
        </context-group>
        </trans-unit>
+       <trans-unit id="filedetails.jsp.tip.symlink">
+<source>&lt;strong&gt;Tip:&lt;/strong&gt; Enter the target of the symlink as 
the file contents</source>
+       <context-group name="ctx">
+               <context 
context-type="sourcefile">/rhn/configuration/FileDetails.do</context>
+       </context-group>
+       </trans-unit>
                <!-- form error messages -->
        <trans-unit id="config-file-form.error.user-invalid">
 <source>User must be a valid Linux user name or user id, of 32 characters or 
less.</source>
diff --git 
a/java/code/src/com/redhat/rhn/manager/configuration/ChannelSummary.java 
b/java/code/src/com/redhat/rhn/manager/configuration/ChannelSummary.java
index 69e4da3..509c065 100644
--- a/java/code/src/com/redhat/rhn/manager/configuration/ChannelSummary.java
+++ b/java/code/src/com/redhat/rhn/manager/configuration/ChannelSummary.java
@@ -32,6 +32,7 @@ public class ChannelSummary {
     
     private int numFiles;
     private int numDirs;
+    private int numSymlinks;
     private int numSystems;
     private ConfigRevision mostRecentMod;
     private String recentFileDate;
@@ -126,7 +127,22 @@ public class ChannelSummary {
         this.numFiles = files;
     }
 
-    
+    /**
+     * How many symlinks are contained in this channel?
+     * @return number of symlinks contained in this channel
+     */
+    public int getNumSymlinks() {
+        return numSymlinks;
+    }
+
+    /**
+     * Set num-symlinks contained in this channel
+     * @param symlinks how many symlinks are contained in this channel
+     */
+    public void setNumSymlinks(int symlinks) {
+        this.numSymlinks = symlinks;
+    }
+
     /**
      * How many systems are subscribed to this channel?
      * @return num of systems subscribed
diff --git 
a/java/code/src/com/redhat/rhn/manager/configuration/ConfigurationManager.java 
b/java/code/src/com/redhat/rhn/manager/configuration/ConfigurationManager.java
index 9d8647b..48cbcfd 100644
--- 
a/java/code/src/com/redhat/rhn/manager/configuration/ConfigurationManager.java
+++ 
b/java/code/src/com/redhat/rhn/manager/configuration/ConfigurationManager.java
@@ -868,7 +868,26 @@ public class ConfigurationManager extends BaseManager {
         Long count = (Long)row.get("total_file_size");
         return count.intValue();
     }
-    
+
+    /**
+     * Return the number of Symlinks in this config-channel
+     * @param user user making the request
+     * @param channel channel of interest
+     * @return number of symlinks in this channel
+     */
+    public int getSymlinkCount(User user, ConfigChannel channel) {
+        if (!accessToChannel(user.getId(), channel.getId())) {
+            throw new IllegalArgumentException(
+                "User [" + user.getId() +
+                "] has no access to channel [" + channel.getId() + "]");
+        }
+        Map params = new HashMap();
+        params.put("ccid", channel.getId());
+        params.put("filetype", "symlink");
+        params.put("user_id", user.getId());
+        return doCountFiles(params);
+    }
+
     /**
      * Return the number of Directories in this config-channel
      * @param user user making the request
@@ -1010,6 +1029,7 @@ public class ConfigurationManager extends BaseManager {
         summary.setNumSystems(getSystemCount(user, channel));
         summary.setNumDirs(getDirCount(user, channel));
         summary.setNumFiles(getFileCount(user, channel));
+        summary.setNumSymlinks(getSymlinkCount(user, channel));
         
         DataResult dr = getFileInfo(user, channel);
         if (dr != null && dr.size() > 0) {
diff --git 
a/java/code/webapp/WEB-INF/pages/common/fragments/configuration/channel/create.jspf
 
b/java/code/webapp/WEB-INF/pages/common/fragments/configuration/channel/create.jspf
index 0bbaad9..66a6fd4 100644
--- 
a/java/code/webapp/WEB-INF/pages/common/fragments/configuration/channel/create.jspf
+++ 
b/java/code/webapp/WEB-INF/pages/common/fragments/configuration/channel/create.jspf
@@ -11,6 +11,9 @@
                        <html:radio
                                property="filetype" value="directory" 
/><bean:message
                                key="addfiles.jsp.type.directory" /><br />
+                       <html:radio property="filetype" value="symlink" 
/><bean:message
+                               key="addfiles.jsp.type.symlink" /><br />
+                       <bean:message key="filedetails.jsp.tip.symlink" />
                        </td>
                </tr>
                <tr>
diff --git 
a/java/code/webapp/WEB-INF/pages/common/fragments/configuration/channel/summary.jspf
 
b/java/code/webapp/WEB-INF/pages/common/fragments/configuration/channel/summary.jspf
index 6736ab4..cf24441 100644
--- 
a/java/code/webapp/WEB-INF/pages/common/fragments/configuration/channel/summary.jspf
+++ 
b/java/code/webapp/WEB-INF/pages/common/fragments/configuration/channel/summary.jspf
@@ -4,13 +4,13 @@
        <tr>
                <th><bean:message key="channelOverview.jsp.numfiles-header" 
/></th>
                <c:choose>
-                       <c:when test="${summary.numFiles + summary.numDirs > 
0}">
+                       <c:when test="${summary.numFiles + summary.numDirs + 
summary.numSymlinks> 0}">
                                <td><bean:message 
key="channelOverview.jsp.numfiles"
-                                       arg0="${summary.numFiles}" 
arg1="${summary.numDirs}" /> 
+                                       arg0="${summary.numFiles}" 
arg1="${summary.numDirs}" arg2="${summary.numSymlinks}" /> 
                                        <br />(<a
                                        
href="/rhn/configuration/ChannelFiles.do?ccid=${channel.id}"><bean:message
                                        key="channelOverview.jsp.numfiles-total"
-                                       arg0="${summary.numFiles + 
summary.numDirs}" /></a>)</td>
+                                       arg0="${summary.numFiles + 
summary.numDirs + summary.numSymlinks}" /></a>)</td>
                        </c:when>
                        <c:otherwise>
                                <td><bean:message 
key="channelOverview.jsp.numfiles-none" /></td>
@@ -61,13 +61,18 @@
        <tr>
                <th><bean:message key="channelOverview.jsp.recentfile-header" 
/></th>
                <td><c:choose>
-                       <c:when test="${summary.numFiles + summary.numDirs > 
0}">
-                               <c:if 
test="${summary.mostRecentMod.configFile.latestConfigRevision.directory}">
-                                 <img alt='<bean:message 
key="config.common.dirAlt" />' src="/img/rhn-listicon-cfg_folder.gif" />
-                               </c:if>
-                               <c:if test="${! 
summary.mostRecentMod.configFile.latestConfigRevision.directory}">
-                                 <img alt='<bean:message 
key="config.common.fileAlt" />' src="/img/rhn-listicon-cfg_file.gif" />
-                               </c:if>
+                       <c:when test="${summary.numFiles + summary.numDirs + 
summary.numSymlinks > 0}">
+                               <c:choose>
+                                       <c:when 
test="${summary.mostRecentMod.configFile.latestConfigRevision.directory}">
+                                               <img alt='<bean:message 
key="config.common.dirAlt" />' src="/img/rhn-listicon-cfg_folder.gif" />
+                                       </c:when>
+                                       <c:when 
test="${summary.mostRecentMod.configFile.latestConfigRevision.symlink}">
+                                               <img alt='<bean:message 
key="config.common.fileAlt" />' src="/img/rhn-listicon-cfg_file.gif" />
+                                       </c:when>
+                                       <c:otherwise>
+                                               <img alt='<bean:message 
key="config.common.fileAlt" />' src="/img/rhn-listicon-cfg_file.gif" />
+                                       </c:otherwise>
+                               </c:choose>
                                <a 
href="/rhn/configuration/file/FileDetails.do?cfid=${summary.mostRecentMod.configFile.id}">
                                  <c:out 
value="${summary.mostRecentMod.configFile.configFileName.path}" />
                                </a><br />
diff --git 
a/java/code/webapp/WEB-INF/pages/common/fragments/configuration/sdc/configfile_rows.jspf
 
b/java/code/webapp/WEB-INF/pages/common/fragments/configuration/sdc/configfile_rows.jspf
index 1c361c7..53a14f7 100644
--- 
a/java/code/webapp/WEB-INF/pages/common/fragments/configuration/sdc/configfile_rows.jspf
+++ 
b/java/code/webapp/WEB-INF/pages/common/fragments/configuration/sdc/configfile_rows.jspf
@@ -8,9 +8,13 @@
                 <img alt='<bean:message key="config.common.fileAlt" />'
                      src="/img/rhn-listicon-cfg_file.gif"> ${current.path}
               </c:when>
-              <c:otherwise>
+              <c:when test="${current.configFileType == 'directory'}">
                 <img alt='<bean:message key="config.common.dirAlt" />'
                      src="/img/rhn-listicon-cfg_folder.gif"> ${current.path}
+              </c:when>
+              <c:otherwise>
+                <img alt='<bean:message key="config.common.fileAlt" />'
+                     src="/img/rhn-listicon-cfg_file.gif"> ${current.path}
               </c:otherwise>
             </c:choose>
        </rhn:column>
@@ -37,4 +41,4 @@
             </c:otherwise>
           </c:choose>
           ${current.channelNameDisplay}
-        </rhn:column>
\ No newline at end of file
+        </rhn:column>
diff --git a/java/code/webapp/WEB-INF/pages/configuration/files/comparecopy.jsp 
b/java/code/webapp/WEB-INF/pages/configuration/files/comparecopy.jsp
index b4521de..8b198e9 100644
--- a/java/code/webapp/WEB-INF/pages/configuration/files/comparecopy.jsp
+++ b/java/code/webapp/WEB-INF/pages/configuration/files/comparecopy.jsp
@@ -37,9 +37,13 @@
             <img alt='<bean:message key="config.common.fileAlt" />'
                  src="/img/rhn-listicon-cfg_file.gif" />
           </c:when>
-          <c:otherwise>
+          <c:when test="${current.configFileType == 'directory'}">
             <img alt='<bean:message key="config.common.dirAlt" />'
                  src="/img/rhn-listicon-cfg_folder.gif" />
+          </c:when>
+          <c:otherwise>
+            <img alt='<bean:message key="config.common.fileAlt" />'
+                 src="/img/rhn-listicon-cfg_file.gif" />
           </c:otherwise>
         </c:choose>
         <bean:message key="comparecopy.jsp.revnum" 
arg0="${current.configRevision}" />
diff --git a/java/code/webapp/WEB-INF/pages/configuration/overview/overview.jsp 
b/java/code/webapp/WEB-INF/pages/configuration/overview/overview.jsp
index 718c7ec..f7647fa 100644
--- a/java/code/webapp/WEB-INF/pages/configuration/overview/overview.jsp
+++ b/java/code/webapp/WEB-INF/pages/configuration/overview/overview.jsp
@@ -48,10 +48,15 @@
                  src="/img/rhn-listicon-cfg_file.gif" />
             ${fn:escapeXml(current.path)}
           </c:when>
-          <c:otherwise>
+          <c:when test="${current.type == 'directory'}">
             <img alt='<bean:message key="config.common.dirAlt" />'
                  src="/img/rhn-listicon-cfg_folder.gif" />
             ${fn:escapeXml(current.path)}
+          </c:when>
+          <c:otherwise>
+            <img alt='<bean:message key="config.common.fileAlt" />'
+                 src="/img/rhn-listicon-cfg_file.gif" />
+            ${fn:escapeXml(current.path)}
           </c:otherwise>
         </c:choose>
       </rhn:column>
diff --git a/java/code/webapp/WEB-INF/pages/configuration/ssm/difffiles.jsp 
b/java/code/webapp/WEB-INF/pages/configuration/ssm/difffiles.jsp
index 25a446a..d51a039 100644
--- a/java/code/webapp/WEB-INF/pages/configuration/ssm/difffiles.jsp
+++ b/java/code/webapp/WEB-INF/pages/configuration/ssm/difffiles.jsp
@@ -34,10 +34,14 @@
                  src="/img/rhn-listicon-cfg_file.gif" />
             <c:out value="${current.path}" />
           </c:when>
-          <c:otherwise>
+          <c:when test="${current.configFileType == 'directory'}">
             <img alt='<bean:message key="config.common.dirAlt" />'
                  src="/img/rhn-listicon-cfg_folder.gif" />
             <c:out value="${current.path}" />
+          </c:when>
+          <c:otherwise>
+            <img alt='<bean:message key="config.common.fileAlt" />'
+                 src="/img/rhn-listicon-cfg_file.gif" />
           </c:otherwise>
         </c:choose>
       </rhn:column>
diff --git a/schema/spacewalk/common/data/rhnConfigFileType.sql 
b/schema/spacewalk/common/data/rhnConfigFileType.sql
index d28a5cd..c0b6620 100644
--- a/schema/spacewalk/common/data/rhnConfigFileType.sql
+++ b/schema/spacewalk/common/data/rhnConfigFileType.sql
@@ -18,6 +18,7 @@
 
 insert into rhnConfigFileType values (1, 'file', 'File', sysdate, sysdate);
 insert into rhnConfigFileType values (2, 'directory', 'Directory', sysdate, 
sysdate);
+insert into rhnConfigFileType values (3, 'symlink', 'Symlink', sysdate, 
sysdate);
 
 --
 --
diff --git 
a/schema/spacewalk/upgrade/spacewalk-schema-0.5-to-spacewalk-schema-0.6/150-rhnConfigFileType_symlinks.sql
 
b/schema/spacewalk/upgrade/spacewalk-schema-0.5-to-spacewalk-schema-0.6/150-rhnConfigFileType_symlinks.sql
new file mode 100644
index 0000000..e48930a
--- /dev/null
+++ 
b/schema/spacewalk/upgrade/spacewalk-schema-0.5-to-spacewalk-schema-0.6/150-rhnConfigFileType_symlinks.sql
@@ -0,0 +1,6 @@
+--
+-- Add in an entry for symlinks
+--
+
+insert into rhnConfigFileType values (3, 'symlink', 'Symlink', sysdate, 
sysdate);
+
-- 
1.6.4

_______________________________________________
Spacewalk-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/spacewalk-devel

Reply via email to