[asterisk-scf-commits] asterisk-scf/integration/configurator.git branch "master" created.
Commits to the Asterisk SCF project code repositories
asterisk-scf-commits at lists.digium.com
Fri Feb 18 07:01:51 CST 2011
branch "master" has been created
at 1e20913bf7364a4f91beadc7e94934472a55204a (commit)
- Log -----------------------------------------------------------------
commit 1e20913bf7364a4f91beadc7e94934472a55204a
Author: Joshua Colp <jcolp at digium.com>
Date: Fri Feb 18 09:01:30 2011 -0400
Add Configurator python module with example.
diff --git a/Configurator.py b/Configurator.py
new file mode 100755
index 0000000..04061bf
--- /dev/null
+++ b/Configurator.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+
+# Asterisk SCF Configurator Module
+
+import ConfigParser, os, Ice, getopt
+
+# Load and make the configuration interface available, we require it
+Ice.loadSlice('-I. --all ConfigurationIf.ice')
+import AsteriskSCF.System.Configuration.V1
+
+# Exception class used within the configurator application
+class ConfiguratorError(Exception):
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+# Common section visitor pattern implementation
+class SectionVisitors():
+ def __init__(self):
+ """Generic class meant to be inherited from for section visitors"""
+ self.groups = []
+
+ def visit(self, config, section):
+ """Execute the visit_ function corresponding to the section name"""
+ try:
+ method = getattr(self, 'visit_' + section)
+ except AttributeError:
+ self.visit_unsupported(config, section)
+ else:
+ method(config, section);
+
+ def visit_unsupported(self, config, section):
+ """Handle an unsupported configuration section"""
+ print "Unsupported configuration section " + section
+
+# Common option to item mapper implementation
+class OptionMapper():
+ options = { }
+
+ def __init(self):
+ """Generic class meant to perform option to item mapping"""
+ self.options = { }
+
+ def map(self, option, item, item_name, method, default = None):
+ item = default
+ self.options[option] = [ item, item_name, method, default ]
+
+ def execute(self, group, section, option):
+ """Map options to configuration items based on options set"""
+ item = self.options[option][0]
+ item_name = self.options[option][1]
+ method = self.options[option][2]
+ default = self.options[option][3]
+
+ try:
+ item = method(section, option)
+
+ except ValueError:
+ # This is not a fatal error since we just use the default value.
+ if default == None:
+ print "The specified value for option '" + option + "' is not valid and no default value exists."
+ else:
+ print "The specified value for option '" + option + "' is not valid. Using default value."
+ item = default
+
+ # This has the potential to overwrite an existing item but this is done on purpose so that
+ # configuration options that all map to a single item can happily exist
+ if item != None:
+ group.configurationItems[item_name] = item
+
+ def finish(self, group):
+ """Finish mapping options by finding ones that should have a value but do not"""
+ for option in self.options:
+ item = self.options[option][0]
+ default = self.options[option][3]
+ if default == None:
+ if item == None:
+ print "Option '" + option + "' requires a value to be set and no default value exists."
+
+# Common configurator application logic
+class ConfiguratorApp(Ice.Application):
+ def __init__(self, defaultConfigFile, visitor, configurationService = None):
+ """Common configuration utility class"""
+
+ if defaultConfigFile == '':
+ raise ConfiguratorError('Configuration utility implementation issue - No default configuration filename specified.')
+
+ self.defaultConfigFile = defaultConfigFile
+ self.visitor = visitor
+ self.configurationService = configurationService
+
+ def usage(self):
+ """Print usage information for the configuration utility"""
+
+ print "Usage: " + self.appName() + " [--config=filename] [--proxy=stringified configuration service proxy] [--wipe]"
+ print "Push a configuration to a component."
+ print ""
+ print "Mandatory arguments to long options are mandatory for short options too."
+ print "-h, --help display help information"
+ print "-c, --config=FILENAME use specified configuration file instead of " + self.defaultConfigFile
+ print "-p, --proxy=PROXY use specified proxy instead of locating component"
+ print "-w, --wipe remove existing configuration before applying new one"
+
+ def run(self, args):
+ """Parse options, read configuration file, produce configuration data, and submit it"""
+
+ try:
+ opts, arguments = getopt.getopt(args[1:], "hc:p:w", ["help", "config=", "proxy=", "wipe"])
+ except getopt.GetoptError, err:
+ print str(err)
+ self.usage()
+ return 1
+
+ configFile = self.defaultConfigFile
+ configurationService = self.configurationService
+ configurationWipe = False
+
+ for o, a in opts:
+ if o in ("-h", "--help"):
+ self.usage()
+ return 1
+
+ elif o in ("-c", "--config"):
+ configFile = a
+
+ elif o in ("-p", "--proxy"):
+ configurationService = AsteriskSCF.System.Configuration.V1.ConfigurationServicePrx.uncheckedCast(self.communicator().stringToProxy(a))
+
+ elif o in ("-w", "--wipe"):
+ configurationWipe = True
+
+ if configurationService == None:
+ print "No configuration service to configure."
+ return 0
+
+ print "Reading configuration from file " + configFile
+ config = ConfigParser.ConfigParser()
+
+ # This purposely uses open and readfp so that the file is required to be present
+ try:
+ config.readfp(open(configFile))
+ except IOError:
+ print "Specified configuration file " + configFile + " could not be loaded."
+ return 0
+
+ print "Building configuration changes to send to component"
+ for section in config.sections():
+ self.visitor.visit(config, section)
+
+ if self.visitor.groups:
+ if configurationWipe == True:
+ print "Wiping existing configuration before applying changes"
+ # There is a race condition here where another component could modify the groups present, but if two
+ # components are altering the configuration we are best effort to begin with
+ try:
+ groups = configurationService.getConfigurationGroups()
+ configurationService.removeConfigurationGroups(groups)
+ except:
+ print "Configuration could not be wiped - Failed to contact component"
+ return 0
+
+ print "Applying configuration"
+
+ try:
+ configurationService.setConfiguration(self.visitor.groups)
+ print "Configuration applied"
+ except:
+ print "Configuration could not be applied - Failed to contact component"
+
+ else:
+ print "No configuration to apply"
+
+ return 0
diff --git a/Example.config b/Example.config
new file mode 100644
index 0000000..9ca996e
--- /dev/null
+++ b/Example.config
@@ -0,0 +1,9 @@
+[general]
+enable=yes
+instances=10
+
+[network]
+ipv6=never ever
+ipv4=yes
+
+[bob]
diff --git a/ExampleConfigurationIf.ice b/ExampleConfigurationIf.ice
new file mode 100644
index 0000000..422deba
--- /dev/null
+++ b/ExampleConfigurationIf.ice
@@ -0,0 +1,71 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#pragma once
+
+#include "ConfigurationIf.ice"
+
+module AsteriskSCF
+{
+
+module System
+{
+
+module Configuration
+{
+
+module Example
+{
+
+["suppress"]
+module V1
+{
+
+ /* General configuration group for items that do not belong in any specific group */
+ class GeneralGroup extends AsteriskSCF::System::Configuration::V1::ConfigurationGroup
+ {
+ };
+
+ /* Network configuration group for network specific items */
+ class NetworkGroup extends AsteriskSCF::System::Configuration::V1::ConfigurationGroup
+ {
+ };
+
+ /* Simple enable and disable item */
+ class EnableItem extends AsteriskSCF::System::Configuration::V1::ConfigurationItem
+ {
+ bool enabled;
+ };
+
+ /* Instance information item */
+ class InstanceInfoItem extends AsteriskSCF::System::Configuration::V1::ConfigurationItem
+ {
+ /* Name of the instances */
+ string name;
+
+ /* How many there should be */
+ int num;
+ };
+
+}; /* End of namespace V1 */
+
+}; /* End of namespace Example */
+
+}; /* End of namespace Configuration */
+
+}; /* End of namespace System */
+
+}; /* End of namespace AsteriskSCF */
diff --git a/ExampleConfigurator.py b/ExampleConfigurator.py
new file mode 100755
index 0000000..bf53cdb
--- /dev/null
+++ b/ExampleConfigurator.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+# Example configurator
+
+# Bring in the common configuration infrastructure
+import Ice, Configurator, sys
+
+# Load our component specific configuration definitions
+Ice.loadSlice('-I. --all ExampleConfigurationIf.ice')
+import AsteriskSCF.System.Configuration.Example.V1
+
+# Add our own visitor implementations for the sections we support
+class ExampleSectionVisitors(Configurator.SectionVisitors):
+ def visit_general(self, config, section):
+ # Create the general group where items from this section will go
+ group = AsteriskSCF.System.Configuration.Example.V1.GeneralGroup()
+ group.configurationItems = { }
+
+ # Create an option mapper and make it aware of the options and their mappings
+ mapper = Configurator.OptionMapper()
+
+ # Enable is easy
+ mapper.map('enable', AsteriskSCF.System.Configuration.Example.V1.EnableItem().enabled, 'enabled', config.getboolean, False)
+
+ # What will be the name and how many of them?
+ item = AsteriskSCF.System.Configuration.Example.V1.InstanceInfoItem()
+ mapper.map('name', item.name, 'instance_info', config.get, None)
+ mapper.map('instances', item.num, 'instance_info', config.getint, 1)
+
+ # Map all the options to the corresponding items and stuff them in the group!
+ for option in config.options(section):
+ mapper.execute(group, section, option)
+ mapper.finish(group)
+
+ # Add the general group to the groups that will be pushed to the component
+ self.groups.append(group)
+
+ def visit_network(self, config, section):
+ # Create the network group where items from this section will go
+ group = AsteriskSCF.System.Configuration.Example.V1.NetworkGroup()
+ group.configurationItems = { }
+
+ # Create an option mapper, put our options in, and map them
+ mapper = Configurator.OptionMapper()
+ mapper.map('ipv4', AsteriskSCF.System.Configuration.Example.V1.EnableItem().enabled, 'ipv4_enabled', config.getboolean, False)
+ mapper.map('ipv6', AsteriskSCF.System.Configuration.Example.V1.EnableItem().enabled, 'ipv6_enabled', config.getboolean, False)
+ for option in config.options(section):
+ mapper.execute(group, section, option)
+
+ mapper.finish(group)
+
+ # Add the network group to the groups that will be pushed to the component
+ self.groups.append(group)
+
+ def visit_unsupported(self, config, section):
+ # If this function is not defined then the configurator will simply print an unsupported option message
+ print "Have unsupported option, should I create some sort of endpoint?"
+
+# Make a configurator application and let it run
+app = Configurator.ConfiguratorApp('Example.config', ExampleSectionVisitors())
+sys.exit(app.main(sys.argv))
-----------------------------------------------------------------------
--
asterisk-scf/integration/configurator.git
More information about the asterisk-scf-commits
mailing list