[node-patches] Change in ovirt-node[master]: Add JSONVarFile

rbarry at redhat.com rbarry at redhat.com
Wed Oct 1 16:33:21 UTC 2014


Ryan Barry has uploaded a new change for review.

Change subject: Add JSONVarFile
......................................................................

Add JSONVarFile

Prep a backend for moving away from the current ShellVarFile in
/etc/defaults/ovirt. Facter and hiera both have JSON backends, and
logically structuring like data (OVIRT_IP_* options should all sit
together under {"ip": {...}} makes transitioning to puppet modules
much nicer, since we can inherit all the variables and instantiate
the class in one shot instead of populating each variable
separately.

Keep the option to pass back the old-style "OVIRT_SOME_VAR" dicts
until config.defaults can be migrated (particularly since some of
the keys are using _ as a whitespace separator and need time to be
properly namespaced)

Change-Id: Ifb5cf7c87657816eee62915888515972efbb1e10
Signed-off-by: Ryan Barry <rbarry at redhat.com>
---
M src/ovirt/node/utils/fs/__init__.py
1 file changed, 152 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/76/33676/1

diff --git a/src/ovirt/node/utils/fs/__init__.py b/src/ovirt/node/utils/fs/__init__.py
index fcfa40b..a2d46ac 100644
--- a/src/ovirt/node/utils/fs/__init__.py
+++ b/src/ovirt/node/utils/fs/__init__.py
@@ -26,6 +26,7 @@
 
 import shutil
 import errno
+import json
 import os
 import StringIO
 import re
@@ -743,3 +744,154 @@
         [('A', 'ah'), ('B', 'beh'), ('C', 'ceh'), ('D', 'more=less')]
         """
         return parse_varfile(txt)
+
+
+class JSONVarFile(base.Base):
+    """ShellVarFile writes simple KEY=VALUE (shell-like) configuration file
+
+    >>> cfg = {
+    ... "OVIRT_IP_ADDR": "127.0.0.1",
+    ... "OVIRT_IP_NETMASK": "255.255.255.0",
+    ... "OVIRT_NETCONSOLE": "False",
+    ... }
+    >>> p = JSONVarFile(FakeFs.File("json-file"))
+    >>> p.get_dict()
+    {}
+    >>> p.update(cfg, True)
+    >>> p.get_dict(old_style=False) #doctest: +NORMALIZE_WHITESPACE
+    {u'IP': {u'NETMASK': u'255.255.255.0', u'ADDR': u'127.0.0.1'},
+    u'NETCONSOLE': u'False'}
+    >>> p.get_dict(old_style=True) #doctest: +NORMALIZE_WHITESPACE
+    {u'OVIRT_IP_NETMASK_ADDR': u'127.0.0.1', u'OVIRT_IP_NETMASK':
+    u'255.255.255.0', u'OVIRT_NETCONSOLE': u'False'}
+    """
+    filename = None
+    _fileobj = None
+    create = False
+
+    def __init__(self, filename, create=False):
+        super(JSONVarFile, self).__init__()
+        self.filename = filename
+        self.create = create
+        if File in type(filename).mro():
+            self._fileobj = filename
+        else:
+            self._fileobj = File(self.filename)
+            if not create and not self._fileobj.exists():
+                    raise RuntimeError("File does not exist: %s" %
+                                       self.filename)
+
+    def _read_contents(self):
+        return self._fileobj.read()
+
+    def raw_read(self):
+        return self._read_contents()
+
+    def _write_contents(self, data):
+        self._fileobj.write(data)
+
+    def exists(self):
+        """Return true if this file exists
+        """
+        return self._fileobj.exists()
+
+    def get_dict(self, old_style=True):
+        """Returns a dict of (key, value) pairs
+        """
+        data = self._read_contents()
+        if data:
+            if not old_style:
+                return json.loads(data)
+            else:
+                return self.__flatten_dict(json.loads(data))
+        else:
+            return {}
+
+    def write(self, cfg, remove_empty=True):
+        """Write a dictionary as a key-val file
+        """
+        for key, value in cfg.items():
+            if remove_empty and value is None:
+                del cfg[key]
+        self._write_contents(json.dumps(cfg, sort_keys=True, indent=2))
+
+    def update(self, new_dict, remove_empty):
+        """Update the file using new_dict
+        Keys not present in the dict, but present in the file will be kept in
+        the file.
+
+        Args:
+            new_dict: A dictionary containing the keys to be updated
+            remove_empty: Remove keys from file if their value in new_dict
+                          is None.
+                          If False then the keys will be added to the file
+                          without any value. (Aka flags)
+        """
+        new_dict = self._transform_dict(new_dict)
+        self.logger.debug("Updating defaults: %s" % new_dict)
+        self.logger.debug("Removing empty entries? %s" % remove_empty)
+        cfg = self.get_dict()
+        cfg.update(self.__dict_merge(new_dict, cfg))
+        self.write(cfg, remove_empty)
+
+    def __dict_merge(self, a, b, path=[]):
+        for key in b:
+            if key in a:
+                if isinstance(a[key], dict) and isinstance(b[key], dict):
+                    self.__dict_merge(a[key], b[key], path + [str(key)])
+            else:
+                a[key] = b[key]
+        return a
+
+    def __flatten_dict(self, txt):
+        """Flatten a dictionary down to the old OVIRT_SOME_VAL"""
+        flattened = {}
+
+        def recurse(d, header="OVIRT_"):
+            if isinstance(d, dict):
+                for k, v in d.items():
+                    header = "%s_%s" % (header, k)
+                    flattened.update(recurse(v, header))
+            else:
+                return {header: d}
+
+        for k, v in txt.items():
+            if isinstance(v, dict):
+                header = "OVIRT_%s" % k
+                recurse(v, header)
+            else:
+                id = "OVIRT_%s" % k
+                flattened.update({id: v})
+        return flattened
+
+    def _transform_dict(self, txt):
+        """Parse a simple shell-var-style lines into a dict:
+
+        >>> import StringIO
+        >>> txt = {
+        ...    "OVIRT_IP_ADDR" : "127.0.0.1",
+        ...    "OVIRT_IP_NETMASK" : "255.255.255.0",
+        ...    "OVIRT_KDUMP_TYPE" : "nfs",
+        ...    "OVIRT_NETCONSOLE" : "False"
+        ... }
+        >>> p = JSONVarFile(StringIO.StringIO(), True)
+        >>> sorted(p._transform_dict(txt).items())
+        ...    #doctest: +NORMALIZE_WHITESPACE
+        [('IP', {'NETMASK': '255.255.255.0', 'ADDR': '127.0.0.1'}),
+        ('KDUMP', {'TYPE': 'nfs'}), ('NETCONSOLE', 'False')]
+        """
+        formatted = dict()
+
+        def recurse_values(k, d, v):
+            if len(k) < 2:
+                return {k[0]: v}
+            else:
+                d.update({k[0]: recurse_values(k[1:], d, v)})
+                return d
+
+        for k, v in txt.items():
+            k = re.sub(r'OVIRT_', '', k)
+            formatted.update(self.__dict_merge(recurse_values(
+                k.split('_'), {}, v), formatted))
+
+        return formatted


-- 
To view, visit http://gerrit.ovirt.org/33676
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ifb5cf7c87657816eee62915888515972efbb1e10
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-node
Gerrit-Branch: master
Gerrit-Owner: Ryan Barry <rbarry at redhat.com>



More information about the node-patches mailing list