Merge "Bring in ovs_config module/state"
diff --git a/_modules/ovs_config.py b/_modules/ovs_config.py
new file mode 100644
index 0000000..3122f71
--- /dev/null
+++ b/_modules/ovs_config.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+'''
+Support for Open vSwitch database configuration.
+
+'''
+from __future__ import absolute_import
+
+import logging
+import salt.utils
+
+log = logging.getLogger(__name__)
+
+
+def __virtual__():
+    '''
+    Only load the module if Open vSwitch is installed
+    '''
+    if salt.utils.which('ovs-vsctl'):
+        return 'ovs_config'
+    return False
+
+
+def _retcode_to_bool(retcode):
+    '''
+    Evaulates ovs-vsctl command`s retcode value.
+
+    Args:
+        retcode: Value of retcode field from response.
+    '''
+    return True if retcode == 0 else False
+
+
+def set(cfg, value, wait=True):
+    '''
+    Updates a specified configuration entry.
+
+    Args:
+        cfg/value: a config entry to update
+        wait: wait or not for ovs-vswitchd to reconfigure itself before it exits.
+
+    CLI Example:
+    .. code-block:: bash
+
+        salt '*' ovs_config.set other_config:dpdk-init true
+    '''
+    wait = '' if wait else '--no-wait '
+
+    cmd = 'ovs-vsctl {0}set Open_vSwitch . {1}="{2}"'.format(wait, cfg, str(value).lower())
+    result = __salt__['cmd.run_all'](cmd)
+    return _retcode_to_bool(result['retcode'])
+
+
+def remove(cfg):
+    '''
+    Removes a specified configuration entry.
+
+    Args:
+        cfg: a config entry to remove
+
+    CLI Example:
+    .. code-block:: bash
+
+        salt '*' ovs_config.remove other_config
+    '''
+    if ':' in cfg:
+        section, key = cfg.split(':')
+        cmd = 'ovs-vsctl remove Open_vSwitch . {} {}'.format(section, key)
+    else:
+        cmd = 'ovs-vsctl clear Open_vSwitch . ' + cfg
+
+    result = __salt__['cmd.run_all'](cmd)
+    return _retcode_to_bool(result['retcode'])
+
+
+def list():
+    '''
+    Return a current config of Open vSwitch
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' ovs_config.list
+    '''
+    cmd = 'ovs-vsctl list Open_vSwitch .'
+    result = __salt__['cmd.run_all'](cmd)
+
+    if result['retcode'] == 0:
+        config = {}
+        for l in result['stdout'].splitlines():
+            cfg, value = map((lambda x: x.strip()), l.split(' : '))
+            if value.startswith('{') and len(value) > 2:
+                for i in value[1:-1].replace('"', '').split(', '):
+                    _k, _v = i.split('=')
+                    config['{}:{}'.format(cfg,_k)] = _v
+            else:
+                config[cfg] = value
+
+        return config
+    else:
+        return False
diff --git a/_states/ovs_config.py b/_states/ovs_config.py
new file mode 100644
index 0000000..bcc8ad6
--- /dev/null
+++ b/_states/ovs_config.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+'''
+Management of Open vSwitch configuration
+========================================
+
+The OVS config can be managed with the ovs_config state module:
+
+.. code-block:: yaml
+
+    other_config:dpdk-init:
+      ovs_config.present:
+        - value: True
+
+    other_config:dpdk-extra:
+      ovs_config.present:
+        - value: -n 12 --vhost-owner libvirt-qemu:kvm --vhost-perm 0664
+
+    external_ids:
+      ovs_config.absent
+'''
+
+
+def __virtual__():
+    '''
+    Only make these states available if Open vSwitch is installed.
+    '''
+    return 'ovs_config.list' in __salt__
+
+
+def present(name, value, wait=True):
+    '''
+    Ensures that the named config exists, eventually creates it.
+
+    Args:
+        name/value: The name/value of the config entry.
+        wait: Whether wait for ovs-vswitchd to reconfigure itself according to the modified database.
+    '''
+    ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}
+    ovs_config = __salt__['ovs_config.list']()
+
+    if name in ovs_config and ovs_config[name] == str(value).lower():
+        ret['result'] = True
+        ret['comment'] = '{0} is already set to {1}.'.format(name, value)
+    else:
+        config_updated = __salt__['ovs_config.set'](name, value, wait)
+        if config_updated:
+            ret['result'] = True
+            ret['comment'] = '{0} is updated.'.format(name)
+            ret['changes'] = { name: 'Updated to {0}'.format(value) }
+        else:
+            ret['result'] = False
+            ret['comment'] = 'Unable to update config of {0}.'.format(name)
+
+    return ret
+
+
+def absent(name):
+    '''
+    Ensures that the named config does not exist, eventually deletes it.
+
+    Args:
+        name: The name of the config entry.
+
+    '''
+    ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}
+    ovs_config = __salt__['ovs_config.list']()
+
+    if ':' in name and name not in ovs_config:
+        ret['result'] = True
+        ret['comment'] = '{0} does not exist.'.format(name)
+    else:
+        config_removed = __salt__['ovs_config.remove'](name)
+        if config_removed:
+            ret['result'] = True
+            ret['comment'] = '{0} is removed.'.format(name)
+            ret['changes'] = { name: '{0} removed'.format(name) }
+        else:
+            ret['result'] = False
+            ret['comment'] = 'Unable to delete config of {0}.'.format(name)
+
+    return ret