Showing posts with label development. Show all posts
Showing posts with label development. Show all posts

2012-08-02

Makefile Deployment

In the latest commits to OpenGrouwpare we've added "Makefile deployment".   This allows for simple and rapid creation of a development instance.

So long as you have PostgreSQL and RabbitMQ available you can just
  1. Check out the code
  2. Tweak the first few lines of the Makefile to tell it your PostgreSQL database name, user name, etc...
  3. If you already have a database then touch 0_database_complete.txt to indicate that the database has already been initialized.
  4. To fire-up a working test instance just run "make run-master"
  5. Point your WebDAV client at 127.0.0.1:8080/dav
That's it! The make file will create a Python virtualized container to handle dependencies, retrieve the required dependencies, install a bare minimum configuration, create a document root, and start the service.  You will have the administrative account with the password you specified in the first few lines of the make file.

You can run "make install-dependencies" if you want to update the Coils dependencies check  - just delete the 0_dependencies_installed.txt file which indicates that dependencies have already been isntalled.

Aside: You may also want to add some of the optional Python modules to your virtual environment such as PIL, informixdb, z3c.rml, procname, or YaJL.  These are not required by OpenGroupware Coils and are not automatically installed.

Simple steps to provision PostgreSQL & RabbitMQ
sudo sudo -u postgres createuser --password --no-superuser --no-created --no-createrole OGoDev
sudo sudo -u postgres createdb -E UTF-8 -O OGoDev OGoDev0
sudo /usr/sbin/rabbitmqctl add_user OGoDev {AMQPASSWORD}
sudo /usr/sbin/rabbitmqctl add_vhost OGoDev0
sudo /usr/sbin/rabbitmqctl set_permissions -p OGoDev0 OGoDev ".*" ".*" ".*"
Text1: Provision PostgreSQL and RabbitMQ
This will give you a PG and AMQ user "OGoDev" with the password specified, a database named "OGoDev0", and an AMQ virtual host named "OGoDev0".  These values are set in the top of the Makefile.

2012-05-24

Creating An OpenGroupware Coils Development Instance

These instructions provide a simple recipe to create an OpenGroupware Coils development instance.  Previously creating an instance has admittedly been a bit tedious; but in the last couple of weeks an effort has been made to simplify deployment.

Aside: Always see the Administrator's Guide (WMOGAG) for complete documentation.  Ask questions on the coils-project mailing list.


Step#1) Provisioning dependencies

Both PostgreSQL and RabbitMQ must be provisioned on the host as well as the dependencies of the required Python modules.  The dependencies are easily met on either CentOS6 (with RPMForge enabled) and openSUSE 12.1.  Installing PostgreSQL and RabbitMQ should be performed using the standard methods.
CentOS6  rpm --import http://apt.sw.be/RPM-GPG-KEY.dag.txt
  rpm -Uvh
    http://packages.sw.be/rpmforge-release/rpmforge-release-0.5.2-2.el6.rf.x86_64.rpm
  yum install python python-ldap  python-devel python-setuptools gcc \
              make binutils libxml2-devel libxslt-devel libyaml \
              libyaml-devel postgresql-devel postgresql-libs cmake \
              gcc-c++ freetype-devel libpng-devel libjpeg-devel \
              libsmbclient-devel

openSUSE
  zypper in python python-ldap  python-devel python-Distutils2 gcc make \
            binutils libxml2-devel libxslt-devel libyaml libyaml-devel \
            postgresql-devel postgresql cmake gcc-c++ freetype2-devel \
           libpng14-devel libjpeg62-devel libsmbclient-devel
Text: These commands will install the required system prerequisites for the required Python modules.
A simple method for installing RabbitMQ on openSUSE 12.1 is described  in the "Idjit's Guide To Installing RabbitMQ on openSUSE 12.1"

Step#1.1) Installed a compatible version of the YAJL library.


The YAJL library provides SAX like stream-processing of JSON data.  This library is required by the ijson Python module. This module is not required by OpenGroupware Coils so this step can be skipped;  but usage of this module (and thus library) are strongly recommended
curl -o yajl-1.0.11.tar.gz http://gentoo.osuosl.org/distfiles/yajl-1.0.11.tar.gz
tar xzvf yajl-1.0.11.tar.gz
cd lloyd-yajl-f4baae0/
mkdir build
cd build
cmake ..
sudo make install
sudo /sbin/ldconfig
Text: Fetching, building, and installing the YAJL library.


Step#1.2) Creating PostgreSQL role & database.

A role must be created in the PostgreSQL engine; the recommendation is to name the role "OGoDev".  Remember the password you enter for this role!
sudo -u postgres createuser --password --no-superuser --no-created --no-createrole OGoDev
Text: Creating the "OGoDev" role.


Once a role has been created a database must be created.  The default name for the OpenGroupware database is typically "OGo" but in order to disambiguate the development database(s) from a production instance the recommendation is to name the database "OGoDev?".  I prefer to make ten databases [OGoDev0, OGoDev1, ... OGoDev9] so I have several to play with - this is especially useful for testing.
sudo -u postgres createdb -E UTF-8 -O OGoDev OGoDev0
Text: Creating a database named "OGoDev0" owned by the OGoDev role.

Step#1.3) Define RabbitMQ role & vhost(s).

The OpenGroupware Coils components also need a context with which to connect to the RabbitMQ service.  Keeping to the same role naming scheme you used with PostgreSQL is helpful for keeping this straight.  Just as with PostgreSQL I prefer to define ten roles and virtual hosts [OGoDev0, OGoDev1, .... OGoDev9] so that I can switch the instance between fresh instances.  The commands listed here will create a role with a password, create an eponymous virtual host, and then grant that role full permissions over its vhost.
sudo rabbitmqctl add_user OGoDev0 {AMQPASSWORD}
sudo rabbitmqctl add_vhost OGoDev0
sudo rabbitmqctl set_permissions -p OGoDev0 OGoDev0 ".*" ".*" ".*"
Text: Creating a role and virtual host in RabbitMQ.
A RabbitMQ virtual host is a means to allow multiple services to use the same message broker service while remaining isolated from each other/

Step#2) Create the container for the development install.
virtualenv OGo
cd OGo
echo -e '\nexport PYTHONPATH="$VIRTUAL_ENV/coils/src"\n' >> bin/activate
echo -e '\nexport OGO_SERVER_ROOT="$VIRTUAL_ENV/root"\n' >> bin/activate
Text: Creating a virtualenv instance for development.
The Python module virtualenv provides a convenient means for creating a container for performing Python development.  This [virtualenv] module should be installed in the host's global Python site-packages.

For this container it is also helpful to add the Coils code checkout to the Python path [via PYTHONPATH] and the establish a server root [via OGO_SERVER_ROOT] within the development container.  This customer server root keeps file created by the instance, including configuration, confined to the development container.
Aside: If OGO_SERVER_ROOT is not defined the server root will default to "/var/lib/opengroupware.org".

Step#2) Populate the container.

Now the container needs to be activated and the required Python modules installed.  The ". bin/activate" builds the environment for the container and should be performed from the root directory of the container whenever working with the development instance.
. bin/activate
pip install lxml==2.3.4
pip install psycopg2==2.4.5
pip install pytz
pip install sqlalchemy==0.7.4
pip install xlrd==0.7.7
pip install xlwt==0.7.4
pip install pysmbc==1.0.13
pip install procname==0.3
pip install PyYAML==3.10
pip install ijson==0.8.0
pip install apscheduler==2.0.3
pip install python-dateutil==2.1
pip install vobject==0.8.1c
pip install http://effbot.org/downloads/Imaging-1.1.7.tar.gz
Text: Activating the virtualenv container and installing Python modules.

Now we are ready to checkout the OpenGroupware Coils code into the container.

Read/Write
  hg clone ssh://{YOURUSERNAME}@hg.code.sf.net/p/coils/code coils
Read-Only
  hg clone http://hg.code.sf.net/p/coils/code coils
Text: Checking out the OpenGroupware Coils code base.


Step#3) Test the development container.

The "coils-dependency-check" tool tries to load all the modules used by OpenGroupware Coils and reports success or failure.  This verifies that the Python requirements for operating an OpenGroupware Coils instance have been met.

(OGo)awilliam@workstation:~/OGo> cd coils/src
(OGo)awilliam@workstation:~/OGo/coils/src> tools/coils-dependency-check
OK: Module xlwt (XLS<2007 write support) available.
OK: Module sqlalchemy (Object Relational Modeling) available.
OK: Module coils.foundation.api.dateutil (Date & Time Arithmatic) available.
OK: Module pytz (Python Time Zone tables) available.
OK: Module xlrd (XLS<2007 read support) available.
OK: Module coils.foundation.api.vobject (vCard and vEvent parsing) available.
OK: Module lxml (SAX & DOM XML Processing) available.
OK: Module PIL (Python Imaging Library) available.
OK: Module psycopg2 (PostgreSQL RDBMS connectivity) available.
OK: Module base64 (Encode and decode Base64 data) available.
OK: Module coils.foundation.api.elementflow (Streaming XML Creation) available.
OK: Module coils.foundation.api.pypdf (Simple PDF Operations) available.
OK: Module yaml (YAML parser & serializer) available.
WARN: Module informixdb (Informix RDBMS connectivity) not available.

1 database connectivity modules found.
 * Make sure the RDBMS you intend to use for the SQLalchemy  *
 * ORM is installed and operational.                         *

1 package warnings found.
 * You are missing packages that extend the operation and    *
 * capacity of the OpenGroupware Coils service.  The service *
 * will provide core functionality but some features,        *
 * particularly in regard to OIE, may not be available. It   *
 * is recommended you install the appropriate packages.      *
Text: Run the coils-dependency-check tool to verify your Python installation.
The warning ("WARN") indicates that an optional module could not be loaded.  Warnings indicate that the service will operate but with potentially reduced functionality.  Any error ["ERROR"] means the service will fail to operate.

Step#4) Initialize the development instance.

Now that the environment is ready the Coils tools can be used to initialize and configure the instance.
(OGo)awilliam@workstation:~/OGo/coils/src> tools/coils-initialize-install  --user=awilliam --group=users --log=../../coils.log
Text: Initialize the server's installation; this create the required structure in the server's document root - in this case the value defined by $OGO_SERVER_ROOT.


(OGo)awilliam@workstation:~/OGo/coils/src> tools/coils-server-config --bootstrap
Initialized a new server defaults file.
Loaded configuration BLOB successfully.
Text: Initialize the instance's configuration with default values.
(OGo)awilliam@workstation:~/OGo/coils/src> tools/coils-server-config --directive LSConnectionDictionary --value "{'databaseName': 'OGoDev0', 'hostName': '127.0.0.1', 'password': '{SQLPASSWORD}', 'port': 5432, 'userName': 'OGoDev'}"
Text: Configure the connection to the PostgreSQL database.

(OGo)awilliam@workstation:~/OGo/coils/src> tools/coils-server-config --directive AMQConfigDictionary --value "{'hostname': '127.0.0.1', 'password': '{AMQPASSWORD}', 'port': 5672, 'username': 'OGoDev0', 'vhost': 'OGoDev0' }"
Text: Configure the connection to the RabbitMQ message broker.
(OGo)awilliam@workstation:~/OGo/coils/src> tools/coils-initialize-database --initdb --password={COILSADMINPASSWORD}
Text: Create the initial database schema and provision the administrative "ogo" account with the specified password.

Step#5) Test the instance.

A simple way to test the instance is to start just the HTTP component.
(OGo)awilliam@workstation:~/OGo/coils/src> tools/coils-service-http --asuser
Text: State the Coils HTTP component.
If this component is running you should be able to connect to and browse the WebDAV hierarchy at http://127.0.0.1:8080/dav (authenticate as the OpenGroupware Coils administrative account "ogo" and the COILSADMINPASSWORD you provided.  Browsing the hierarchy can be performed with Nautilus, cadaver, or any WebDAV client.  Use your systems break sequence [typically Ctrl-C] to stop the component.
(OGo)awilliam@workstation:~/OGo/coils/src> tools/coils-master-service --asuser
Text: Start the Coil's master service which will start and manage an instance of every available component.
The coils-master-service tool can be used in the same manner to start-up the full suite of service components providing HTTP and workflow services (include the TCP/9100 and SMTP listeners).

2012-02-03

Building Legacy 5.5rc1

The following instructions provide a detailed step-by-step for how to build the OpenGroupware Legacy release candidate 1 on openSUSE 12.1.  Building on other distributions should be very similar. These instructions are intended for those interested in participating in OpenGroupware Legacy development and testing.

Step#1) Install dependencies
zypper in libapr-util1-devel libapr1-devel gcc46-objc libobjc46 postgresql-devel libmysqlclient-devel apache2-utils apache2-devel openldap2-devel libxmlsec1-devel libxmlsec1-gnutls-devel libxml2-devel libxslt-devel
Step #2) Get GNUstep
svn co http://svn.gna.org/svn/gnustep/modules/core
Step #3) Get SOPE
curl-o sope.tar.gz \
   http://www.sogo.nu/files/downloads/SOGo/Sources/SOPE-1.3.11.tar.gz
Step #4) Get OpenGroupware Legacy
hg clone http://opengroupware.hg.sourceforge.net:8000/hgroot/opengroupware/opengroupware
Step #5) Build and install GNUstep make
cd  core/make/
./configure
make
sudo make install
Step #6) Build and install GNUstep base
. /usr/local/share/GNUstep/Makefiles/GNUstep.sh
cd ../base
./configure --disable-tls
make
make check
sudo make install
Step #7) Build and install SOPE
cd ../..
tar xzvf sope.tar.gz
cd SOPE-1.3.11
./configure
make
sudo make install
sudo /sbin/ldconfig

Step #8) Build OGo
cd ../opengroupware/opengroupware/
hg pull
hg update
./configure
make APR=/usr/bin/apr-1-config APXS=/usr/sbin/apxs2
sudo make installsudo /sbin/ldconfig
Step #9) Configure OGo via defaults
If you have an existing OpenGroupware Legacy installation you probably already have the LSConnectionDictionary default defined appropriately. Note that the command used to editing defaults has changed from "Defaults" to "defaults".
su - ogo
mkdir /var/lib/opengroupware.org/run
defaults write ogo-webui WOPidFile /var/lib/opengroupware.org/run/ogo-webui.pid
defaults write ogo-zidestore WOPidFile /var/lib/opengroupware.org/run/ogo-zidestore.pid
defaults write ogo-xmlrpcd WOPidFile /var/lib/opengroupware.org/run/ogo-xmlrpcd.pid
defaults write ogo-webui WOLogFile /var/lib/opengroupware.org/run/ogo-webui.log
defaults write ogo-zidestore WOLogFile /var/lib/opengroupware.org/run/ogo-zidestore.log
defaults write ogo-xmlrpcd WOLogFile /var/lib/opengroupware.org/run/ogo-xmlrpcd.log
defaults write ogo-webui WOPort 20000
defaults write ogo-zidestore WOPort 21000
defaults write ogo-xmlrpcd WOPort 22000
defaults write NSGlobalDomain imap_host 127.0.0.1
defaults write NSGlobalDomain LSConnectionDictionary \
    '{userName="OGo"; databaseName="OGo";hostName="localhost";password="*******";}'
defaults write NSGlobalDomain NGBundlePath "/usr/local/lib64/opengroupware.org-5.5/commands:/usr/local/lib64/opengroupware.org-5.5/webui:/usr/local/lib64/opengroupware.org-5.5/datasources"
defaults write NSGlobalDomain LSModelName OpenGroupware.org_PostgreSQL
Step #9) Fire-up ZideStore
ogo-zidestore -WOLogFile - -WONoDetach YES -WOUseWatchDog NO
If it runs then your development build is probably OK!

2011-12-28

Accessing Server Configuration (Defaults)

When developing Logic, either a Command or a Service component, one frequent need is to check the value of a server's configuration directive [a "default" in OpenGroupware speak].  Accessing server configuration is performed using an instance of the ServerDefaultsManager object.  The following code retrieves the value of the CoilsListenAddress address; and if no such default is defined it returns the value "127.0.0.1".
sd = ServerDefaultsManager()
HTTP_HOST = sd.string_for_default('CoilsListenAddress', '127.0.0.1')
The ServerDefaultsManager will cache the server's configuration - so if you are going to be checking a lot of defaults is better to keep the object around rather than repeatedly creating it. The ServerDefaultsManager provides the following methods for retrieving server defaults:
  • bool_for_default(directive) - Values of boolean configuration values are stored as "YES" and "NO" strings.  Actually, any value that isn't "YES" is interpreted as False, which is also the default if no such directive is defined. The value returned by the method is a Python bool type.
  • string_for_default(directive, default value) - Returns the value of the default as string or returns the specified default value if no such directive is defined.
  • integer_for_default(directive, default value) - Returns the value as an integer, or returns the specified default value if no such directive is defined. An exception is raised if the value cannot be represented as an integer.
  • default_as_dict(directive, default value) - Returns the value of the specified directive as a dictionary, or the specified default value if no such directive is defined.  An exception is raised if the value is not a dictionary.
  • default_as_list(directive, default) - Returns the value as a list, or the specified default value if no such directive is defined.  An exception is raised if the value is not a list.
Regarding the actually loading of defaults the defaults manager will load from (or save to) one of two sources.  If the file ".server_defaults.pickle" exists in the document root of the server the defaults are loaded from (and saved to) that Python pickle file; otherwise the defaults are loaded from (and saved to) the OpenSTEP plist file at ".libFoundation/Defaults/NSGlobalDomain.plist".  Use of the OpenSTEP plist file facilitates parallel operation of OpenGroupware Coils with Legacy - both OpenGroupware Coils and OpenGroupware Legacy will operate using the shared configuration. 
One caveat to remember is that OpenSTEP plist files are always stored in the ISO8859-1 encoding.  This includes both the server defaults and user defaults.  Both OpenGroupware Coils and OpenGroupware Legacy always store user defaults in OpenSTEP plist format.  Facilities for parsing and writing OpenSTEP plist files are provided by the coils.foundation module.
If you are developing a remote component that does not have access to the server's document root your component can acquire a copy of the server's configuration by sending a "get_server_defaults" message to the coils.administrator component.  The payload of the response should contain the cluster's GUID [as the "GUID" key] and a copy of all the server defaults [in the "defaults" key).

2010-07-19

EOGlobalIDs And Primary Keys

What is a primary key?
Every document stored within OpenGroupware has a numeric id that is unique. In fact, almost every entry concerning anything in OpenGroupware has one of these ids. This id namespace is flat; that is: ids do not overlap between document types; if there is a person with an id of 99450 then there is no enterprise, file, appointment, or anything else with the id of 99450. When anything is created it is assigned one of these unique numbers, called the primary key (often abbreviated pkey or pk. And that is the id of that document for its entire lifetime.

One note of clarification: by "document" we don't mean "file". An enterprise, person, job, project, file, appointment, or resource are all documents. You could call them objects too, but that gets even more confusing. :)  Sometimes, especially in OpenGroupware Coils, they are referred to as entities;  all these are names of the same things.

What is an EOGlobalID / EOKeyGlobalID?
Serialized an EOKeyGlobalID looks something like
<0x0x82cb23c[EOKeyGlobalID]: Date 28260>
Yea, that was helpful wasn't it? But look at it... there is the string "Date" and a number of "28260". If you guessed that this refers to the date [appointment] document with a pkey [primary key] of 28260 then you are correct. Internally EOKeyGlogalIDs (often abbreviated as gid / gids) are what OpenGroupware uses, on the database level, in order to retrieve and store data. OGo Legacy / SOPE uses a database abstraction layer called GDL, apparently very much like the GDL from GNU-Step. You can think of the EOKeyGlobalID as a "handle" for the data related to a given document.

The EOKeyGlobalID object has the following accessors:
- (NSString *)entityName;
- (unsigned int)keyCount;
- (id *)keyValues;
- (NSArray *)keyValuesArray;

The entityName accessor will provide the type of document that the key refers like. The keyValuesArray provides access to the payload which is the pkey [primary key] of the object. Since the value of keyValuesArray is an NSArray and you almost certainly want just the single value which it contains you'd use code like:
[[key keyValuesArray] objectAtIndex: 0]
which in the above example would provide you with the value "28260" (as an NSNumber object).  Of course it is possible to have a composite primary key, which explains why keyValuesArray is an array, but this doesn't happen in OpenGroupware.

How to turn a pkey into an EOKeyGlobalId?
It is a very frequent case where you have a pkey or an NSArray of pkeys for which you need to marshal the corrsponding document objects. Marshalling an object requires having the EOKeyGlobalId (handle). Fortunately the OpenGroupware framework provides a very simple means to generate an EOKeyGlobalId from a pkey; this is via the typeManager object.
The first requirement for getting handles from typeManager is that your pkey or array of pkeys must be NSNumber objects. You cannot use NSString objects. If what you have are strings then use the NSString class's intValue method to produce NSNumber objects:
[NSNumber numberWithInt:[_arg intValue]]
where _arg is your NSString object. Of course the contents of _arg have to actually be numeric. Then you can invoke the typeManager object like:
gid = [[[self commandContext] typeManager] globalIDForPrimaryKey:_arg]
where _arg is the NSNumber object containing your pkey and you get an instance of the EOKeyGlobalId required to retrieve the specified document from the database.