How much thought goes into what your configuration file will look like when developing an application? That is, does the overall configuration structure have an impact on the quality of your finished product? This question can be asked from more than one perspective. For instance, from the end user's point of view, is the set of potential configuration options cohesive and easy to understand? The number of potential configuration options also plays a role in the overall manageability of the application because more configuration options means more complexity. This same point needs to be considered from the application's point of view. Adding flexibility by means of configurable options has an impact on your application's design. Making any aspect of your application configurable adds variability to something that could have been constant, or hard-coded. Beyond the size and structure of an application configuration, there is meaning behind each configuration option. Thinking about what each configuration option means before adding it to the configuration design is important. Lets take a look at an example of how we might go about designing a configuration schema for a simple system.
The system under development is a simple desktop GUI application data viewer. The application will support two data formats; JSON and XML. The GUI for this application is fairly straightforward in that it will have two tabs. These tabs will offer the end user a different view of the data. The first tab will use a tree widget to display a hierarchical view of the data. The second tab will use a table or grid widget to display the data. In the latter tab, clicking a row will reload the table or grid with that row's data if it can be displayed as a table or grid. Otherwise, a dialog is opened to display the details of the row data. Likewise, if a leaf node in the tree widget of the first tab is selected, the same dialog will appear. The application can load the data from a local file or from a URI on the web.
This intentionally simplistic system hardly justifies the need for having a configuration. It is ideal, however, for our purposes here because we want to elaborate on how the configuration values for the application come into being. Lets start by looking at the static aspects of our sample application. By static, I mean aspects of the application that remain constant throughout it's lifetime. These aspects aren't configurable. It is worth looking at the fundamental features that will not change before designing configurable elements. This way, we can eliminate candidates from our list of potential configuration values. Looking at the sample application under development, there are two supported data formats, JSON and XML. One potential configuration value we could add is a list of supported data formats. Logically, we would want to have the ability to extend our application to support more than just JSON and XML formats. However, in our case, this is something that will remain constant. The system is a JSON/XML reader so we don't want to change the what the application does best.
Now that we've eliminated a potential configuration option, lets think about some configuration categories, or sections. Configuration sections are a group of related configuration options. For instance, in our application, we would probably want a configuration section for GUI settings. The benefit to having configuration options grouped by sections is that the end user can get a better handle on what they're changing. Conceptually, sections, or categories, give the end user a little more confidence when manipulating the configuration. For instance, if a user sees a GUI section in the configuration file, they're going to feel at ease changing these options. This is because the intent of this particular group of options is clearly stated; change these values to alter the look and feel of the GUI. Additionally, from the developer's perspective, configuration sections give us an opportunity to map configuration options to concepts found in our code. GUI configuration options might map to only a handful of classes, or possibly a single base class.
Since our sample JSON/XML reader application is so simplistic, we're limited in the number of configuration sections we can create. Our application is a desktop GUI application so "gui" is a good candidate configuration section. We can also read JSON or XML from the web so "network" might be considered for a configuration section. Finally, we may also want a "core" section for configuration options that don't fit into either of the above sections. The "core" configuration section is intentionally general because we still need to leave room for changes. As our application code evolves and is re-factored, we may discover new concepts to map our configuration options to. The "gui" and "network" concepts, although broad, are clear enough for both users and developers upfront.
Lets take a step back from our sample application for a moment and take a look at some the various types of configuration options. The most basic type of configuration option we can have is a boolean, on/off value. These are the configuration options that either enable or disable some aspect of the system. With boolean configuration options, you typically specify a true value to enable something. For instance, setting "ssh_enabled" to a true value enables SSH. We can also use boolean configuration options to negate a certain aspect of the system. For instance, setting "ssh_disabled" to a true value disables SSH. In the latter case, we've changed the meaning the boolean configuration option to take on a negative meaning. Most of the time, the former approach of true values acting as an enabler works best because the intent is always clear. Configuration options can also specify a literal value such as a string or integer. These types of configuration values are useful for default values used in the system. Configuration options can also specify a list of literal values are also typically used as defaults within the system or as a means to extend the system.
Going back to our JSON/XML example, lets come up with some configuration values. Is there any aspect of the application we may like to disable at some point? Perhaps the obvious choice is to have the ability to disable the JSON format or to disable the XML format. What type of configuration option should we use to accomplish this? Two boolean configuration options, "json_enabled" and "xml_enabled", could be added to the configuration file. By default, our application would probably have these two options set to true since these options make up the core of the system. Next, what configuration section do these configuration options belong to? For now, they go in the "core" section because changing these options has an impact on several concepts in our application. If we were to set "xml_enabled" to false, the XML tab would no longer be displayed. An alternative to using boolean configuration options is a list. For instance, we could define a "data_formats" configuration option that defaults to a list of "JSON" and "XML". With this approach, there is a need for only a single configuration option instead of two. But, the problem with this approach is that we've introduced a notion of extensibility to both developers and the end user when this isn't the case. To disable one of these data formats, we'd have to remove it from the list. Once removed, it is as though the data format never existed.
What about our other two configuration sections, "network" and "gui"? In the network section we have a good opportunity to use a list configuration type. An "allowed_hosts" option would allow us to specify a list of hosts in which the end user is allowed to request data from. The "network" section could also use a "network_enabled" boolean option that allows us to disable networking entirely. Our "gui" configuration section is largely undefined at the moment. This is fine because the last thing we want to do is throw configuration options into sections when they aren't needed. The "gui" section is a good place to put proxy configuration options, if and when they are needed. Our application is going to use a GUI library of some sort and these libraries offer a plethora of parameters to change the look and feel of the application. The configuration options defined in the "gui" section can usually be passed directly through the application and into the GUI library.
What we have seen here with our simple example is that there is some thought involved with how a finished configuration file for an application should look. The options used in your configuration file should map well to the concepts found in your application design. If you're writing an application and something seems unclear about a configuration option, change it. But don't hesitate as the sooner you correct this ambiguity the better. Problems with configuration options may be badly named options or they may have misused values. These issues sometimes aren't trivial to spot but putting yourself in the mindset of someone who has to use your configuration file for productive work will often help with the design.
Showing posts with label configuration. Show all posts
Showing posts with label configuration. Show all posts
Wednesday, June 16, 2010
Friday, October 9, 2009
Python Configuration Manipulation
Configuration management is an important aspect of any application. Configuration is necessary unless the application in question is a monolithic, inflexible piece of code. And, there are applications such as these but they aren't the norm. Many aspects of most applications will need to change, even if only subtly. The users running these applications need an easy way to manage and manipulate these configuration values.
The INI configuration style is probably the best standard to follow. It is easy for both users and applications to understand and to manipulate. Moreover, there exist libraries for handling these files in almost any programming language.
In Python, the ConfigObj package is good at dealing with configuration files. It is good at both reading and manipulating the files which is useful because developers need not depend on one package for reading configuration files and another for writing them. There is a single class that Python applications should be concerned with in the ConfigObj package. The configuration objects are instantiated with the file name containing the configuration.
Perhaps the best feature of ConfigObj instances is that configuration values can be read and written using dictionary style notation. This is much more intuitive for Python developers than using some kind of configuration specific notation.
However, since the configuration values are accessed using dictionary notation, it would be trivially easy to abstract away the configuration values as attributes of some domain specific class. The same configuration file may in fact be read by various domain classes. Each class reading a different section of the configuration.
The INI configuration style is probably the best standard to follow. It is easy for both users and applications to understand and to manipulate. Moreover, there exist libraries for handling these files in almost any programming language.
In Python, the ConfigObj package is good at dealing with configuration files. It is good at both reading and manipulating the files which is useful because developers need not depend on one package for reading configuration files and another for writing them. There is a single class that Python applications should be concerned with in the ConfigObj package. The configuration objects are instantiated with the file name containing the configuration.
Perhaps the best feature of ConfigObj instances is that configuration values can be read and written using dictionary style notation. This is much more intuitive for Python developers than using some kind of configuration specific notation.
However, since the configuration values are accessed using dictionary notation, it would be trivially easy to abstract away the configuration values as attributes of some domain specific class. The same configuration file may in fact be read by various domain classes. Each class reading a different section of the configuration.
Friday, September 25, 2009
Granular Django Cache
Like many other web application frameworks, Django has a built-in caching system. Unlike other web application frameworks, the Django cache system is relatively straightforward to configure and use. Configuring the cache system can be as simple as specifying where the cached items are stored. With the Django cache system, developers have plenty of options. There is even a dummy cache storage that can be used for development purposes. Whichever back-end cache system you decide to use, it can be specified in the CACHE_BACKEND configuration value.
Once the cache storage location has been setup, caching can be implemented at any number of levels from per-site to low-level. The most effective way to implement Django cache, I find, is to implement it on a per-view basis. Using this method to implement cache means that cached items are created for each URL that is requested if the view mapped to the URL is cached. Using the lower level Django cache constructs are nearly impossible to manage for larger, more complex applications. They do exist, however, for niche situations.
The cache_page() function is responsible for creating a page cache. The function takes a view to be cached and a timeout as parameters. Once the timeout has expired, any cached items are no longer valid. Although the cache_page() function can be used as a decorator on the view declaration, it makes more sense to pass the view as a parameter to cache_page() within the URL configuration. This is the more portable way of doing things and is better aligned logically since the URL serves as the cache key, not the view name.
Once the cache storage location has been setup, caching can be implemented at any number of levels from per-site to low-level. The most effective way to implement Django cache, I find, is to implement it on a per-view basis. Using this method to implement cache means that cached items are created for each URL that is requested if the view mapped to the URL is cached. Using the lower level Django cache constructs are nearly impossible to manage for larger, more complex applications. They do exist, however, for niche situations.
The cache_page() function is responsible for creating a page cache. The function takes a view to be cached and a timeout as parameters. Once the timeout has expired, any cached items are no longer valid. Although the cache_page() function can be used as a decorator on the view declaration, it makes more sense to pass the view as a parameter to cache_page() within the URL configuration. This is the more portable way of doing things and is better aligned logically since the URL serves as the cache key, not the view name.
Labels:
cache
,
configuration
,
django
,
page
,
url
,
webapplication
Friday, August 21, 2009
Configuring Trac Ticket Workflow
Trac is a project management system written in Python. Trac has a powerful issue tracking system in which new issues are created as tickets. The system is flexible enough to allow for custom ticket types. This means that Trac tickets not only allow for issue tracking, but any conceivable software development task as well. There are many other customizations that can be made to Trac tickets such as custom fields and custom work-flow. Tickets in Trac go through a series of states during their lifetime. The current state of a given ticket determines the actions available on that ticket. The state of a ticket in Trac terminology is the ticket status. The ticket work-flow that determines the actions available, the permissions required, and the state the ticket must be in in order to transition are all configurable. In other words, if a given ticket is closed, the only available action for the ticket is to reopen it. This is because of the default Trac ticket work-flow configuration.
In the Trac configuration, ticket actions can be configured to allow for certain ticket states to be active in order to execute the action. So, if you wanted to allow more actions other than reopen on closed tickets, this can be specified in the Trac configuration. Multiple current states may be specified as criteria. Only a single target state may be specified. Below is a simplistic example of a ticket work-flow configuration element.
In the Trac configuration, ticket actions can be configured to allow for certain ticket states to be active in order to execute the action. So, if you wanted to allow more actions other than reopen on closed tickets, this can be specified in the Trac configuration. Multiple current states may be specified as criteria. Only a single target state may be specified. Below is a simplistic example of a ticket work-flow configuration element.
[ticket-workflow]
action = current_state1, current_state2, current_state3 -> target_state
action.permission = TICKET_MODIFY
action.operation = ticket_operation_name
Labels:
configuration
,
python
,
ticket
,
ticketworkflow
,
trac
,
workflow
Monday, March 23, 2009
Registering configuration values in ECP
The Enomaly Elastic Computing Platform has an extension module API that allows developers to register new ECP components. These components include new web controllers, new RESTful API controllers, and so on. One component that cannot be registered are configuration values. Extension modules can be viewed as smaller applications that are executed within ECP. Therefore, these smaller extension module applications will need to be configured. There are always going to be values that should be configurable within any application such as storage locations. Currently, extension modules must implement their own settings abstractions. This functionality already exists in the ECP core and the way configuration values are accessed and stored should be consistent and hence the need for the custom settings class. It would make sense for extension modules in ECP to have the ability to register their own configuration values. This way, configuration values would be accessed and stored in the exact same way across the platform. An additional complication arises when trying to use the configuration editor. The configuration editor is tightly-coupled with TurboGears widgets and thus requires that all extension modules be tightly-coupled with TurboGears widgets. Ideally, when configuration values are registered, which currently is not possible, additional meta data suitable for generating a display widget for the configuration value could also be registered.
The current implementation of the ECP Settings class uses managed Python attributes to seamlessly save and load configuration values. Every time a managed Settings attribute is accessed, the Variable class will attempt to load the variable. Likewise, when a managed Settings attribute is altered, the Variable class will attempt to store the configuration value. It is easier to use managed attributes for simple storage and retrieval operations. The alternative is to use the Variable class directly. In fact, earlier implementations of ECP did exactly that. Every time a configuration value was needed, we had to invoke Variable.load() while specifying a default value in case the configuration value didn't exist. The new Settings class was introduced to help alleviate some of this troubled configuration access. A single instance of the Settings class is created in the configuration.py module. This instance can then be used throughout the ECP application, including extension modules. Configuration categories are also incorporated into the Settings class. This is done by using the same concept as the Settings class for each category. This category class is then set as an attribute of Settings. This allows us to access configuration values in the form of settings.kvm.bridge. This syntax offers much more readable code when used in context. However, the problem with this method of managing configuration values was soon after realized. There will always be a need to add new configuration values. Most noteably, extension modules are going to need this capability since developers are going to want to access configuration values in the same way as the rest of ECP. There is a need to be able to register new configuration values. This eliminates the extensibility problem of adding new configuration values. If every time a new configuration value needed by an extension module, or the core application for that matter, needs to be added to the Settings class, it will grow exponentially and become very challenging to maintain. Additionally, the configuration editor is very tightly coupled to TurboGears widgets because extension modules need to display these configuration values in the configuration editor. This is done by the extension module defining a hook that passes in TurboGears widgets used to display the configuration values for the extension module in the configuration editor. This isn't the ideal method since this also couples the extension modules to ECP dependencies (TurboGears). Ideally, the widgets for displaying configuration values should be generated by the configuration editor based on minimal meta-data provided by the extension module at registration time.
The new approach to ECP configuration value management is to have configuration values registered in the Settings class. The same approach of using managed attributes to access and store configuration values is still used. What is different is the ability to register a value and have these managed attributes automatically built for the developer. This is accomplished by introducing a new MetaSettings class. The purpose behind this new class is to dynamically construct new categorization classes and methods that will become attributes of the settings instance. There is also a new settings.register() method that can be used to register new configuration values. The end result of using settings.register() to register a new configuration value is the same syntax as before when using the configuration values. The name of the module passed to settings.register() will become an attribute of the settings instance. There are also meta-data parameters in the settings.register() method that allow developers to specify a title and description of the configuration value. In the current ECP configuration management implementation, this information must be specified in the TurboGears widget. With the new implementation, the managed attribute functionality found in the ECP core no longer needs to be duplicated. There is now a much more uniform interface.
With this new configuration registration functionality in place, there is now an opportunity for great improvements in the configuration editor. We could now potentially eliminate the coupling to TurboGears widgets and have each configuration widget generated automatically. Grouping by extension module is now also possible in the configuration editor.
The current implementation of the ECP Settings class uses managed Python attributes to seamlessly save and load configuration values. Every time a managed Settings attribute is accessed, the Variable class will attempt to load the variable. Likewise, when a managed Settings attribute is altered, the Variable class will attempt to store the configuration value. It is easier to use managed attributes for simple storage and retrieval operations. The alternative is to use the Variable class directly. In fact, earlier implementations of ECP did exactly that. Every time a configuration value was needed, we had to invoke Variable.load() while specifying a default value in case the configuration value didn't exist. The new Settings class was introduced to help alleviate some of this troubled configuration access. A single instance of the Settings class is created in the configuration.py module. This instance can then be used throughout the ECP application, including extension modules. Configuration categories are also incorporated into the Settings class. This is done by using the same concept as the Settings class for each category. This category class is then set as an attribute of Settings. This allows us to access configuration values in the form of settings.kvm.bridge. This syntax offers much more readable code when used in context. However, the problem with this method of managing configuration values was soon after realized. There will always be a need to add new configuration values. Most noteably, extension modules are going to need this capability since developers are going to want to access configuration values in the same way as the rest of ECP. There is a need to be able to register new configuration values. This eliminates the extensibility problem of adding new configuration values. If every time a new configuration value needed by an extension module, or the core application for that matter, needs to be added to the Settings class, it will grow exponentially and become very challenging to maintain. Additionally, the configuration editor is very tightly coupled to TurboGears widgets because extension modules need to display these configuration values in the configuration editor. This is done by the extension module defining a hook that passes in TurboGears widgets used to display the configuration values for the extension module in the configuration editor. This isn't the ideal method since this also couples the extension modules to ECP dependencies (TurboGears). Ideally, the widgets for displaying configuration values should be generated by the configuration editor based on minimal meta-data provided by the extension module at registration time.
The new approach to ECP configuration value management is to have configuration values registered in the Settings class. The same approach of using managed attributes to access and store configuration values is still used. What is different is the ability to register a value and have these managed attributes automatically built for the developer. This is accomplished by introducing a new MetaSettings class. The purpose behind this new class is to dynamically construct new categorization classes and methods that will become attributes of the settings instance. There is also a new settings.register() method that can be used to register new configuration values. The end result of using settings.register() to register a new configuration value is the same syntax as before when using the configuration values. The name of the module passed to settings.register() will become an attribute of the settings instance. There are also meta-data parameters in the settings.register() method that allow developers to specify a title and description of the configuration value. In the current ECP configuration management implementation, this information must be specified in the TurboGears widget. With the new implementation, the managed attribute functionality found in the ECP core no longer needs to be duplicated. There is now a much more uniform interface.
With this new configuration registration functionality in place, there is now an opportunity for great improvements in the configuration editor. We could now potentially eliminate the coupling to TurboGears widgets and have each configuration widget generated automatically. Grouping by extension module is now also possible in the configuration editor.
Labels:
configuration
,
ecp
,
enomaly
,
metaclass
,
python
,
settings
,
turbogears
Friday, March 13, 2009
How Pylons connects to the ORM
The Pylons Python web application framework manages database connections for any given application written in the framework. It does so by using a PackageHub class for SQLObject. This is actually similar to how TurboGears manages database connections. The database functionality for the Pylons web framework is defined in the database.py module. This are slightly different for SQLAlchemy support in Pylons. In order to provide SQLAlchemy support, Pylons will attempt to define several functions SQLAlchemy requires to connect to the database.
Something I find a little strange is the way the support for both SQLAlchemy and SQLObject is handled by Pylons. Pylons will attempt to import SQLAlchemy and handle the import error. However, Pylons will always attempt to import SQLObject and will not handle an import failure if the library isn't installed on the system. For instance, the following is a high level view of how the database ORM libraries are imported in Pylons.
There is a slight asymmetry here. At the very least, I think SQLObject errors should be handled as well. But what would happen in the event that there are no supported ORM libraries available for import? That would obviously leave us with a non-functional web application. A nice feature to have, and this really isn't Pylons-specific, is the ability to specify in a configuration sort of way, which ORM library is preferred. The database.py module could then base the imports on this preference. For instance, illustrated below is how the ORM importing mechanism in Pylons might work if the ORM preference could be specified.

Here, the flow is quite simple. We load the configuration data, check which ORM was specified and attempt to import it. On import failure, we complain about an ORN not being available. Of course, we will most likely want a default ORM preference if one is not provided. I think that would provide a much cleaner design than basing the ORM preference on what can be imported. There is certain enhancement functionality in which we can base the availability on the fact that the library can be imported. Such as a plugin for a system. But, these are only enhancements. We can't really make these assumptions about a core component like a database connection manager.
The SQLAlchamy connection functionality in Pylons has no notion of a connection hub. There is absolutely no problem with this. The functions are what is needed to establish a connection to SQLAlchemy and they work. For illustration purposes, lets make a new pretend class that doesn't exist called SQLAlchemyHub that groups all SQLAlchemy-related functions together. The following is what we would end up with when visualizing the database.py module.

It is important to remember that the SQLALchemyHub class isn't real. It is there to help us visualize the SQLAlchemy abstraction within the context of the module.
Something I find a little strange is the way the support for both SQLAlchemy and SQLObject is handled by Pylons. Pylons will attempt to import SQLAlchemy and handle the import error. However, Pylons will always attempt to import SQLObject and will not handle an import failure if the library isn't installed on the system. For instance, the following is a high level view of how the database ORM libraries are imported in Pylons.

There is a slight asymmetry here. At the very least, I think SQLObject errors should be handled as well. But what would happen in the event that there are no supported ORM libraries available for import? That would obviously leave us with a non-functional web application. A nice feature to have, and this really isn't Pylons-specific, is the ability to specify in a configuration sort of way, which ORM library is preferred. The database.py module could then base the imports on this preference. For instance, illustrated below is how the ORM importing mechanism in Pylons might work if the ORM preference could be specified.

Here, the flow is quite simple. We load the configuration data, check which ORM was specified and attempt to import it. On import failure, we complain about an ORN not being available. Of course, we will most likely want a default ORM preference if one is not provided. I think that would provide a much cleaner design than basing the ORM preference on what can be imported. There is certain enhancement functionality in which we can base the availability on the fact that the library can be imported. Such as a plugin for a system. But, these are only enhancements. We can't really make these assumptions about a core component like a database connection manager.
The SQLAlchamy connection functionality in Pylons has no notion of a connection hub. There is absolutely no problem with this. The functions are what is needed to establish a connection to SQLAlchemy and they work. For illustration purposes, lets make a new pretend class that doesn't exist called SQLAlchemyHub that groups all SQLAlchemy-related functions together. The following is what we would end up with when visualizing the database.py module.

It is important to remember that the SQLALchemyHub class isn't real. It is there to help us visualize the SQLAlchemy abstraction within the context of the module.
Friday, March 6, 2009
The dynamic configuration problem
Most applications, especially web applications require some type of configuration management. The configuration data is usually specified in a .cfg file in the application directory. Most languages and web frameworks provide utilities to parse and read these configuration files. These than become variables, possibly altering the control flow, in the application itself.
In many cases, users of these applications do not want to edit configuration files. I know I don't want to half the time. The solution? Integrate the configuration into the GUI. Perfect. So now we have a pretty interface that users (often administrators) can use to update these configuration values. One problem here is where do these values get stored once they have been updated? It is probably not a good idea to alter the configuration file. Or is it?
In ECP the approach is to store the altered configuration values from the web front-end into the database. This is good because we don't need to mess around with writing code in the application to manipulate the configuration file every time a configuration value is changed. When a configuration value is needed somewhere in the application code, ECP will check the database for the configuration value. If the value is found, this value is used in the application.
The problem here may be obvious but I'll mention it anyway. What happens when a configuration value in the configuration file is updated and that same value already exists in the database? Well, as it turns out, that change will have no effect.
What if we were to change the order precedence of the configuration value lookup; if the configuration file were checked before the database? In this case, we have lost the front-end GUI capability. The reason I don't like storing updated configuration values in the configuration files is because that isn't why they are there. The configuration files exist for humans to edit and they are generally quite annoyed when a machine starts messing with the configuration.
So how do we fix this configuration race condition? We have a value that is stored in the database and that same value is stored in the configuration file. Ideally, it would make more to support both configuration storage locations; the file and the database. At the most basic level, we are trying to solve the problem of the most recent value. The most recent configuration value should be used in the application code. One step to take on the database side is to store the modification date for each configuration value. At least then we can determine how fresh the database value is. On the configuration file side, this isn't really feasible since the file is edited by humans. We can't expect them to enter the current timestamp every time the update a value just so their application will behave as expected.
We can't use the modification date of the configuration file because this date doesn't reflect which value was updated. What we could do is introduce some new configuration fields to the database. The table might look like the following.

Here, we have four fields; db_value, db_tstamp, file_value, and file_tstamp. The db_value field stores the value the is sent from the application front-end. The db_tstamp value is updated with the current timestamp when the db_value field is updated. When the application is started for the first time, all file configuration values would then be copied into the database in the file_value and file_tstamp fields. At this point, we would have several file values stored in the database but no front-end editor values.
The reason we would need these file_value and file_tstamp fields is to compare them with the db_value and db_tstamp fields. When a lookup happens, the lookup functionality would read the value from the configuration file. If the value is different, we update the file_value and file_tstamp fields. We would then return either the db_value or file_value based on whichever is greater; db_tstamp or file_tstamp.
In many cases, users of these applications do not want to edit configuration files. I know I don't want to half the time. The solution? Integrate the configuration into the GUI. Perfect. So now we have a pretty interface that users (often administrators) can use to update these configuration values. One problem here is where do these values get stored once they have been updated? It is probably not a good idea to alter the configuration file. Or is it?
In ECP the approach is to store the altered configuration values from the web front-end into the database. This is good because we don't need to mess around with writing code in the application to manipulate the configuration file every time a configuration value is changed. When a configuration value is needed somewhere in the application code, ECP will check the database for the configuration value. If the value is found, this value is used in the application.
The problem here may be obvious but I'll mention it anyway. What happens when a configuration value in the configuration file is updated and that same value already exists in the database? Well, as it turns out, that change will have no effect.
What if we were to change the order precedence of the configuration value lookup; if the configuration file were checked before the database? In this case, we have lost the front-end GUI capability. The reason I don't like storing updated configuration values in the configuration files is because that isn't why they are there. The configuration files exist for humans to edit and they are generally quite annoyed when a machine starts messing with the configuration.
So how do we fix this configuration race condition? We have a value that is stored in the database and that same value is stored in the configuration file. Ideally, it would make more to support both configuration storage locations; the file and the database. At the most basic level, we are trying to solve the problem of the most recent value. The most recent configuration value should be used in the application code. One step to take on the database side is to store the modification date for each configuration value. At least then we can determine how fresh the database value is. On the configuration file side, this isn't really feasible since the file is edited by humans. We can't expect them to enter the current timestamp every time the update a value just so their application will behave as expected.
We can't use the modification date of the configuration file because this date doesn't reflect which value was updated. What we could do is introduce some new configuration fields to the database. The table might look like the following.

Here, we have four fields; db_value, db_tstamp, file_value, and file_tstamp. The db_value field stores the value the is sent from the application front-end. The db_tstamp value is updated with the current timestamp when the db_value field is updated. When the application is started for the first time, all file configuration values would then be copied into the database in the file_value and file_tstamp fields. At this point, we would have several file values stored in the database but no front-end editor values.
The reason we would need these file_value and file_tstamp fields is to compare them with the db_value and db_tstamp fields. When a lookup happens, the lookup functionality would read the value from the configuration file. If the value is different, we update the file_value and file_tstamp fields. We would then return either the db_value or file_value based on whichever is greater; db_tstamp or file_tstamp.
Wednesday, March 4, 2009
ECP and IP address retrieval
The Enomaly Elastic Computing Platform has to retrieve the IP address of the host in which it is running. There are several reasons for this but this isn't the reason for this entry. The concern here is how the IP address is actually obtained from the host machine. This must be accomplished in Python since that is what ECP is written in.
There are several Python recipies available for obtaining the IP address of a given machine. However, on Linux, you must know the which network interface you are retrieving the address from. In the current release of ECP, the default network interface to check is virbr0 since this is the interface that is setup when ECP is started. There are a few problems with this approach.
First, I noticed that there is no real way to change which network interface ECP will use as a basis for IP address retrieval. However, this has been addressed in the ECP trunk. Users may now specify a specific interface in the configuration by providing a enomalism2.ip_interface value. If this value is provided, ECP will check this interface for the machine IP address. One thing that hasn't changed about the host IP address retrieval in ECP is the fact that when all else fails, 127.0.0.1 is returned. This is a good thing since we can always identify a machine IP address as opposed to an empty string.
Secondly, ECP hosts may have several active network interfaces. What then? This deficiency was pointed out here. This is a very valid concern for some people. What would be needed here is a way to associate network interfaces with users in ECP. This results in a new database abstraction that could have some potential in a future ECP release. For the time being, users are restricted to a single network interface that is at least configurable now.
There are several Python recipies available for obtaining the IP address of a given machine. However, on Linux, you must know the which network interface you are retrieving the address from. In the current release of ECP, the default network interface to check is virbr0 since this is the interface that is setup when ECP is started. There are a few problems with this approach.
First, I noticed that there is no real way to change which network interface ECP will use as a basis for IP address retrieval. However, this has been addressed in the ECP trunk. Users may now specify a specific interface in the configuration by providing a enomalism2.ip_interface value. If this value is provided, ECP will check this interface for the machine IP address. One thing that hasn't changed about the host IP address retrieval in ECP is the fact that when all else fails, 127.0.0.1 is returned. This is a good thing since we can always identify a machine IP address as opposed to an empty string.
Secondly, ECP hosts may have several active network interfaces. What then? This deficiency was pointed out here. This is a very valid concern for some people. What would be needed here is a way to associate network interfaces with users in ECP. This results in a new database abstraction that could have some potential in a future ECP release. For the time being, users are restricted to a single network interface that is at least configurable now.
Labels:
configuration
,
ecp
,
enomaly
,
host
,
ipaddress
Tuesday, November 4, 2008
Some TurboGears configuration thoughts
The TurboGears Python web framework uses Cherrypy as the web server. Cherrypy offers several other useful features other than strictly "web server" functionality. One of these features is project configuration. TurboGears basically builds on the Cherrypy configuration functionality. TurboGears also uses a Python package called ConfigObj to help distribute responsibilities. If there is one thing the TurboGears project does well, it would have to be reusing existing functionality rather than rebuild it.
When retrieving a configuration value in a TurboGears project using get(), TurboGears simply uses the Cherrypy configuration. However, the TurboGears framework is responsible for keeping the Cherrypy configuration component up to date. For example, lets say you need to update the project configuration dynamically as the result of some client request. This can be done is one of two ways. The first, you could pass a key/value dictionary to update(). The second, you could pass a configuration file to update_config(). In both cases, we are essentially updating the Cherrypy configuration. The ConfigObj package comes in handy when we need to read configuration files. Not when we already have a key/value configuration dictionary.
My main criticism of the TurboGears config module is that I wish there were a factory function that simply generated a configuration instance. This instance would be a representation of the configuration for the entire project. All the functions that are currently defined in the TurboGears config module could be instance methods of this new configuration class. I haven't yet looked in the trunk yet to see whats happening there with the config module.
When retrieving a configuration value in a TurboGears project using get(), TurboGears simply uses the Cherrypy configuration. However, the TurboGears framework is responsible for keeping the Cherrypy configuration component up to date. For example, lets say you need to update the project configuration dynamically as the result of some client request. This can be done is one of two ways. The first, you could pass a key/value dictionary to update(). The second, you could pass a configuration file to update_config(). In both cases, we are essentially updating the Cherrypy configuration. The ConfigObj package comes in handy when we need to read configuration files. Not when we already have a key/value configuration dictionary.
My main criticism of the TurboGears config module is that I wish there were a factory function that simply generated a configuration instance. This instance would be a representation of the configuration for the entire project. All the functions that are currently defined in the TurboGears config module could be instance methods of this new configuration class. I haven't yet looked in the trunk yet to see whats happening there with the config module.
Monday, November 3, 2008
Enomaly ECP update
First things first. ECP 2.1.1, which is a minor maintenance release, will hopefully be released this week. This release only contains bug fixes and minimal interface enhancements.
On the ECP 2.2 front, there have been lots of enhancements made in the configuration core module. The main change here is the addition of several classes that provide managed python attributes. These classes are also instantiated by the configuration module. Each managed attribute has the ability to either retrieve or store a configuration value depending on how the attribute is used. Under the covers, the attributes use Variable.load() and Variable.save(). So far, this implementation has fitted nicely into the code where configuration values are needed.
The code looks much cleaner with simple dot notation as opposed to several Variable.load() calls.
On the ECP 2.2 front, there have been lots of enhancements made in the configuration core module. The main change here is the addition of several classes that provide managed python attributes. These classes are also instantiated by the configuration module. Each managed attribute has the ability to either retrieve or store a configuration value depending on how the attribute is used. Under the covers, the attributes use Variable.load() and Variable.save(). So far, this implementation has fitted nicely into the code where configuration values are needed.
The code looks much cleaner with simple dot notation as opposed to several Variable.load() calls.
Labels:
configuration
,
ecp
,
enomalism
,
enomaly
,
module
Wednesday, July 9, 2008
Try something new or use what you know?
Good question. What would a software object do? Well, it will do what its creator tells it to do. And a software object knows only what its creator tells it. This doesn't seem right. What about the system in which the object lives? The system is alive and constantly changing state. Shouldn't the software object know about these changes?
Well, in some cases, they do. You pass external variables into your object by sending your object messages. This is the fancy term for "method invocation". Your object then has knowledge about its environment.
But what about your software object's attribute that are initialized during construction? Sometimes these values are hard-coded and can only be changed further down the lifeline of the object. Is this the best way to initialize attribute values? Or are we better off allowing for the possibility of dynamic attribute value initialization?
TurboGears allows developers to retrieve configuration values using a key. It also allows the developer to specify a default value should the key not exist. Using this feature, or a similar pattern for that matter, we can initialize attribute values by checking if there is something new that our software object didn't previously know about. If not, it will always use the specified default.
Well, in some cases, they do. You pass external variables into your object by sending your object messages. This is the fancy term for "method invocation". Your object then has knowledge about its environment.
But what about your software object's attribute that are initialized during construction? Sometimes these values are hard-coded and can only be changed further down the lifeline of the object. Is this the best way to initialize attribute values? Or are we better off allowing for the possibility of dynamic attribute value initialization?
TurboGears allows developers to retrieve configuration values using a key. It also allows the developer to specify a default value should the key not exist. Using this feature, or a similar pattern for that matter, we can initialize attribute values by checking if there is something new that our software object didn't previously know about. If not, it will always use the specified default.
Subscribe to:
Posts
(
Atom
)