めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

Private notes on Packstack plugin development (1)

Introduction

RDO is an OpenStack distribution for RHEL/Fedora platforms, and Packstack is an installation tool for RDO. It internally uses bunch of Puppet manifests for installing and configuring various OpenStack components.

But as always for every new open source tool, it lacks useful documentation at the moment. So I've started to dig into its source codes to figure out how I can leverage its real possibilities. Amazingly, I found that it is a generic framework for developing Puppet based configuration tools for distributed server systems rather than just an OpenStack installation tool. Since it is based on the plugin mechanism, you can develop new plugins to install/configure your favorite applications.

In this note, I will strip Packstack down to its core components and try to explain how you can build your own plugins on top of that. This will be a good starting point to understand Packstack's internal, and be beneficial in two senses:

1) Packstack could evolve as a more generic configuration tool for distributed systems. I think a little refactoring is required for that because, though it has a good module structure, some OpenStack terms are hard coded in core modules. They should be in separate plugins specific to OpenStack configuration.

2) More people can contribute to extend Packstack to catch up with the rapid evolution of OpenStack. Some people around me complain that Packstack cannot configure Quantum at the moment. I hope this note will help them contribute to the development of Packstack's Quntum plugin module, for example.

Note: I'm just one of casual Packstack users, not a developer. This note is solely based on my own reverse engineering. Please forgive me if some explanation is different from the real intention of the original developers ;-)

Preparation

1) Install RHEL6.4 (or Fedora18 or some clone...).

2) Add RDO repo, and install the latest packstack.

# yum install -y http://rdo.fedorapeople.org/openstack/openstack-grizzly/rdo-release-grizzly-3.noarch.rpm
# yum install -y openstack-packstack

The following version is used in this document.

# rpm -qa | grep packstack
openstack-packstack-2013.1.1-0.5.dev538.el6.noarch

3) Remove original plugins except some core plugins.

# cd /usr/lib/python2.*/site-packages/packstack
# mv plugins plugins.orig
# mkdir plugins
# cd plugins.orig
# cp __init__.py prescript_000.py sshkeys_000.py serverprep_901.py postscript_949.py puppet_950.py ../plugins

Note: File paths are expressed relative to "/usr/lib/python2.*/site-packages/packstack" hereafter.

Remove line 29 to 88, and line 111 to 122 of "plugins/prescript_000.py" as below (because they are OpenStack specific options):

plugins/prescript_000.py

     27     paramsList = [
### Cut from here.
     28                   {"CMD_OPTION"      : "os-glance-install",
     29                    "USAGE"           : "Set to 'y' if you would like Packstack to install Glance",
     30                    "PROMPT"          : "Should Packstack install Glance image service",
     31                    "OPTION_LIST"     : ["y", "n"],
     32                    "VALIDATORS"      : [validators.validate_options],
...
     99                    "CONDITION"       : False },
### Cut until here.
    100                   {"CMD_OPTION"      : "ntp-severs",
    101                    "USAGE"           : "Comma separated list of NTP servers. Leave plain if Packstack should not install nt        pd on instances.",
...
    108                    "USE_DEFAULT"     : False,
    109                    "NEED_CONFIRM"    : False,
    110                    "CONDITION"       : False },
### Cut from here.
    111                   {"CMD_OPTION"      : "nagios-install",
    112                    "USAGE"           : "Set to 'y' if you would like Packstack to install Nagios to monitor openstack hosts        ",
    113                    "PROMPT"          : "Should Packstack install Nagios to monitor openstack hosts",
    114                    "OPTION_LIST"     : ["y", "n"],
    115                    "VALIDATORS"      : [validators.validate_options],
    116                    "DEFAULT_VALUE"   : 'n',
    117                    "MASK_INPUT"      : False,
    118                    "LOOSE_VALIDATION": False,
    119                    "CONF_NAME"       : "CONFIG_NAGIOS_INSTALL",
    120                    "USE_DEFAULT"     : False,
    121                    "NEED_CONFIRM"    : False,
    122                    "CONDITION"       : False },
### Cut until here.
    123                  ]
    124     groupDict = { "GROUP_NAME"            : "GLOBAL",
    125                   "DESCRIPTION"           : "Global Options",

4) Remove original manifests except for core components.

# cd /usr/lib/python2.*/site-packages/packstack/puppet
# mv templates templates.orig
# mkdir templates
# cp templates.orig/ntpd.pp templates/
# touch templates/prescript.pp templates/postscript.pp

5) Create the "NOOP" plugin as a starting template of new plugin.

plugins/noop_010.py

"""
noop plugin
"""

import os
import logging

from packstack.installer import validators
import packstack.installer.common_utils as utils
from packstack.installer.exceptions import ScriptRuntimeError

from packstack.modules.ospluginutils import getManifestTemplate, appendManifestFile, manifestfiles

# Controller object will be initialized from main flow
controller = None

PLUGIN_NAME = "NOOP"

logging.debug("plugin %s loaded", __name__)

def initConfig(controllerObject):
    global controller
    controller = controllerObject

    paramsList = [
                  {"CMD_OPTION"      : "noop",
                   "USAGE"           : "Only the pre-req configurations are done on the target server.",
                   "PROMPT"          : "Enter the IP address of the target server",
                   "OPTION_LIST"     : [],
                   "VALIDATORS"      : [validators.validate_ip, validators.validate_ssh],
                   "DEFAULT_VALUE"   : utils.getLocalhostIP(),
                   "MASK_INPUT"      : False,
                   "LOOSE_VALIDATION": True,
                   "CONF_NAME"       : "CONFIG_NOOP_HOST",
                   "USE_DEFAULT"     : False,
                   "NEED_CONFIRM"    : False,
                   "CONDITION"       : False },
                 ]
    groupDict = { "GROUP_NAME"            : "NOOP",
                  "DESCRIPTION"           : "NOOP Options",
                  "PRE_CONDITION"         : False,
                  "PRE_CONDITION_MATCH"   : True,
                  "POST_CONDITION"        : False,
                  "POST_CONDITION_MATCH"  : True}
    controller.addGroup(groupDict, paramsList)

def initSequences(controller):
    noopsteps = [
             {'title': 'Do nothing', 'functions':[donothing]},
    ]
    controller.addSequence("Installing NOOP Component", [], [], noopsteps)

def donothing():
    pass

Test the "bare" packstack.

Now you can try the stripped down version of packstack which contains some core plugins and the "NOOP" plugin.

The NOOP plugin accepts an IP address of target server and does nothing on that, but as a side effect, other core plugins do some preparation works on that server. So you can understand what will be done on the target server as a pre-req of packstack configuration works.

Let's run packstack in the debug mode. As it asks some optional parameters, you need to enter the IP of your NTP server. Just leave blank for other options. During the execution, you will be asked to input root password of localhost. So supply it.

# packstack --debug
Welcome to Installer setup utility
Enter the path to your ssh Public key to install on servers  [/root/.ssh/id_rsa.pub] : 
Enter a comma separated list of NTP server(s). Leave plain if Packstack should not install ntpd on instances.: 192.168.122.1
Enter the IP address of the target server  [192.168.122.191] : 
To subscribe each server to EPEL enter "y" [y|n]  [y] : 
Enter a comma separated list of URLs to any additional yum repositories to install: 
To subscribe each server to Red Hat enter a username here: 
To subscribe each server to Red Hat enter your password here :
To subscribe each server to Red Hat Enterprise Linux 6 Server Beta channel (only needed for Preview versions of RHOS) enter "y" [y|n]  [n] : 
To subscribe each server with RHN Satellite enter RHN Satellite server URL: 

Installer will be installed using the following configuration:
==============================================================
ssh-public-key:                /root/.ssh/id_rsa.pub
ntp-severs:                    192.168.122.1
noop:                          192.168.122.191
use-epel:                      y
additional-repo:               
rh-username:                   
rh-password:                   
rh-beta-repo:                  n
rhn-satellite-server:          
Proceed with the configuration listed above? (yes|no): yes

Installing:
Clean Up...                                              [ DONE ]
Setting up ssh keys...root@192.168.122.191's password: 
                                   [ DONE ]
Adding pre install manifest entries...                   [ DONE ]
Installing time synchronization via NTP...               [ DONE ]
Do nothing...                                            [ DONE ]
Preparing servers...                                     [ DONE ]
Adding post install manifest entries...                  [ DONE ]
Installing Dependencies...                               [ DONE ]
Copying Puppet modules and manifests...                  [ DONE ]
Applying Puppet manifests...
Applying 192.168.122.191_prescript.pp
192.168.122.191_prescript.pp :                                       [ DONE ]
Applying 192.168.122.191_ntpd.pp
192.168.122.191_ntpd.pp :                                            [ DONE ]
Applying 192.168.122.191_postscript.pp
192.168.122.191_postscript.pp :                                      [ DONE ]
                            [ DONE ]

 **** Installation completed successfully ******

Additional information:
 * A new answerfile was created in: /root/packstack-answers-20130519-220517.txt
 * The installation log file is available at: /var/tmp/packstack/20130519-220509-xMPdEW/openstack-setup.log

You may understand what packstack has done on localhost (as a default target) from the configuration summary. It has done the following things:

1) Setup password-less public key SSH authentication to the target.
2) Setup ntpd configuration.
3) Setup EPEL repo (and subscribe to RHN/RHN Satellite if requested).
4) Install pre-req packages such as puppet.

You can check the actual commands executed on the target server from the log file "/var/tmp/packstack/20130519-220509-xMPdEW/openstack-setup.log".

If you add some actual works to the NOOP plugin, you can do meaningful configuration works for multiple servers. But as Packstack is well integrated with Puppet, you don't have to write configuration procedures with python functions. Instead, you specify templates of Puppet manifest with configuration parameters in each plugin. Then the core plugin "puppet_950.py" will apply them on the target servers in the later execution phase. So what you need to know is conventions for adding new optional parameters and its Puppet manifests.

Let's delve into those conventions with sample plugins. Continue to the next part.