Merge "Add module mangement to Trove"
This commit is contained in:
		
							
								
								
									
										835
									
								
								specs/mitaka/module-management.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										835
									
								
								specs/mitaka/module-management.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,835 @@
 | 
			
		||||
..
 | 
			
		||||
    This work is licensed under a Creative Commons Attribution 3.0 Unported
 | 
			
		||||
    License.
 | 
			
		||||
 | 
			
		||||
    http://creativecommons.org/licenses/by/3.0/legalcode
 | 
			
		||||
 | 
			
		||||
    Sections of this template were taken directly from the Nova spec
 | 
			
		||||
    template at:
 | 
			
		||||
    https://github.com/openstack/nova-specs/blob/master/specs/template.rst
 | 
			
		||||
 | 
			
		||||
..
 | 
			
		||||
    This template should be in ReSTructured text. The filename in the git
 | 
			
		||||
    repository should match the launchpad URL, for example a URL of
 | 
			
		||||
    https://blueprints.launchpad.net/trove/+spec/awesome-thing should be named
 | 
			
		||||
    awesome-thing.rst.
 | 
			
		||||
 | 
			
		||||
    Please do not delete any of the sections in this template.  If you
 | 
			
		||||
    have nothing to say for a whole section, just write: None
 | 
			
		||||
 | 
			
		||||
    Note: This comment may be removed if desired, however the license notice
 | 
			
		||||
    above should remain.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=================
 | 
			
		||||
Module Management
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
.. If section numbers are desired, unindent this
 | 
			
		||||
    .. sectnum::
 | 
			
		||||
 | 
			
		||||
.. If a TOC is desired, unindent this
 | 
			
		||||
    .. contents::
 | 
			
		||||
 | 
			
		||||
Historically, Trove has supported open source databases.  As more datastores
 | 
			
		||||
were added it was inevitable that this would eventually change to include
 | 
			
		||||
proprietary databases as well.  Starting with the Liberty release this is the
 | 
			
		||||
case (support for Vertica and DB2 now being available) and with this comes the
 | 
			
		||||
issue of managing the licenses of said databases.  In addition, operators may
 | 
			
		||||
find it useful to include other software on their images that may require
 | 
			
		||||
'activation' by end-users (for example New Relic's analytical suite).  A method
 | 
			
		||||
of activating this software is also needed.
 | 
			
		||||
 | 
			
		||||
The concept of applying a 'license' or 'activation' or 'configuration' of this
 | 
			
		||||
third-party software is what is referred to herein as 'module' management.
 | 
			
		||||
 | 
			
		||||
Launchpad Blueprint:
 | 
			
		||||
https://blueprints.launchpad.net/trove/+spec/module-management
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Problem Description
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
Users of a particular cloud may be willing to purchase a license to use a
 | 
			
		||||
datastore from the cloud vendor on a pay-as-you-go based model, and this is the
 | 
			
		||||
model assumed at the moment (as both Vertica and DB2 redstack images include a
 | 
			
		||||
fully-functional and licensed database).  It is also desirable, however, that
 | 
			
		||||
users be allowed to 'bring their own license.'  In this scenario the user
 | 
			
		||||
provides a license file that the database requires, and as such a mechanism
 | 
			
		||||
needs to be in place to 'activate' and/or 'renew' the license through the use
 | 
			
		||||
of the user provided file.
 | 
			
		||||
 | 
			
		||||
This same problem exists for any other proprietary software that an operator
 | 
			
		||||
may wish to include in their Trove images.  These software packages also
 | 
			
		||||
typically require activation through the use of a license key or file (such as
 | 
			
		||||
New Relic [1]_) or configuration of some kind.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Proposed Change
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
Trove's responsiblity towards module management will be restricted to the scope
 | 
			
		||||
of encrypting and storing the required data file (for example, a license file)
 | 
			
		||||
and providing a way to apply this data to a new or existing Trove instance or
 | 
			
		||||
cluster.  A mechanism will be put in place to allow end users the ability to
 | 
			
		||||
manage adding, deleting, listing, viewing and updating these module data files.
 | 
			
		||||
 | 
			
		||||
Methods to apply, remove, query and retrieve the actual 'module' data file on
 | 
			
		||||
the Trove instance will also be provided.
 | 
			
		||||
 | 
			
		||||
A repeatable option (--module) will be added to the create and cluster-create
 | 
			
		||||
commands to allow adhoc module selection.  In addition, modules can be set to
 | 
			
		||||
auto-apply, which will have the effect of the Guest Agent installing that
 | 
			
		||||
module on any instance created with the relevant datastore combination.
 | 
			
		||||
 | 
			
		||||
Configuration
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
The following configuration changes are anticipated.
 | 
			
		||||
 | 
			
		||||
A way of specifying valid module 'types' will be needed for proper validation
 | 
			
		||||
on module create:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    cfg.StrOpt('module_types', default=None,
 | 
			
		||||
               help='A list of module types supported.'),
 | 
			
		||||
 | 
			
		||||
A key will be needed in order to be able to encrypt the module data file before
 | 
			
		||||
storing it in the database:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    cfg.StrOpt('module_aes_cbc_key', default='module_aes_cbc_key',
 | 
			
		||||
               help='OpenSSL aes_cbc key for module encryption.'),
 | 
			
		||||
 | 
			
		||||
Database
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
A new table (modules) will be added to the Trove schema:
 | 
			
		||||
 | 
			
		||||
    =================  ============  ===========  ==============================
 | 
			
		||||
    Column             Type          Allow Nulls  Description
 | 
			
		||||
    =================  ============  ===========  ==============================
 | 
			
		||||
    id                 varchar(36)   No           ID of module (autogenerated)
 | 
			
		||||
    type               varchar(255)  No           Type of module.  This will
 | 
			
		||||
                                                  correlate directly to the
 | 
			
		||||
                                                  required plugin (i.e. a
 | 
			
		||||
                                                  plugin must exist of this
 | 
			
		||||
                                                  'type')
 | 
			
		||||
    tenant_id          varchar(36)   No           ID of tenant to apply
 | 
			
		||||
                                                  module to.  'all' means module
 | 
			
		||||
                                                  applies to all tenants
 | 
			
		||||
    datastore          varchar(36)   No           Name of datastore to apply
 | 
			
		||||
                                                  module to.  'all' means module
 | 
			
		||||
                                                  applies to all datastores
 | 
			
		||||
    datastore_version  varchar(36)   No           Name of datastore version to
 | 
			
		||||
                                                  apply module to.  'all' means
 | 
			
		||||
                                                  module applies to all
 | 
			
		||||
                                                  datastores
 | 
			
		||||
    name               varchar(255)  No           Name of module
 | 
			
		||||
    description        varchar(512)  Yes          Description of module
 | 
			
		||||
    auto_apply         tinyint(1)    No           Should this module be
 | 
			
		||||
                                                  automatically applied during
 | 
			
		||||
                                                  instance/cluster create.  Will
 | 
			
		||||
                                                  default to 'no' if not
 | 
			
		||||
                                                  provided
 | 
			
		||||
    visible            tinyint(1)    No           Should this module be
 | 
			
		||||
                                                  visible to non-admin users.
 | 
			
		||||
                                                  Will default to 'yes' if not
 | 
			
		||||
                                                  provided
 | 
			
		||||
    live_update        tinyint(1)    No           Can this module be updated
 | 
			
		||||
                                                  while applied-to instances
 | 
			
		||||
                                                  still exist.  If set to 'no'
 | 
			
		||||
                                                  all instances must have the
 | 
			
		||||
                                                  corresponding module removed
 | 
			
		||||
                                                  before it can be updated.
 | 
			
		||||
                                                  Defaults to 'no'
 | 
			
		||||
    contents           blob          No           Encrypted module contents
 | 
			
		||||
    md5                varchar(32)   No           MD5 hash of module contents
 | 
			
		||||
    created            DateTime      No           Created date
 | 
			
		||||
    updated            DateTime      No           Updated date
 | 
			
		||||
    deleted            tinyint(1)    Yes          Deleted flag
 | 
			
		||||
    deleted_at         DateTime      Yes          Deleted date
 | 
			
		||||
    =================  ============  ===========  ==============================
 | 
			
		||||
 | 
			
		||||
A unique index will be created from the (datastore, datastore_version, name)
 | 
			
		||||
fields, to allow easy determination of the correct module to apply to a
 | 
			
		||||
specific instance.
 | 
			
		||||
 | 
			
		||||
An MD5 hash of the module contents will be stored in the module record as
 | 
			
		||||
well.  This hash will be reported back when querying the module from a
 | 
			
		||||
running instance, as the original module record could have been modified with
 | 
			
		||||
new contents after it was initially applied.
 | 
			
		||||
 | 
			
		||||
On installing the module contents on a given instance, a file will be created
 | 
			
		||||
in a know location using <datastore>-<datastore_version>-<name>.lic as a
 | 
			
		||||
pattern.
 | 
			
		||||
 | 
			
		||||
Creating modules that apply to 'all' tenants or 'all' datastores and ones that
 | 
			
		||||
are auto-applied will require admin credentials.
 | 
			
		||||
 | 
			
		||||
Setting a module to 'not' visible is also an admin-only option.  This will
 | 
			
		||||
allow administrators to 'hide' modules from users if they so desire.  Modules
 | 
			
		||||
that are marked visible=False will not be returned in commands such as list or
 | 
			
		||||
show unless requested by an admin user.  Non-admin users won't be able to apply
 | 
			
		||||
a non-visible module, however they will still be auto-applied if so designated.
 | 
			
		||||
 | 
			
		||||
A new table (instance_modules) will be added to the Trove schema to track which
 | 
			
		||||
modules have been applied to each instance:
 | 
			
		||||
 | 
			
		||||
    =================  ============  ===========  ==============================
 | 
			
		||||
    Column             Type          Allow Nulls  Description
 | 
			
		||||
    =================  ============  ===========  ==============================
 | 
			
		||||
    id                 varchar(36)   No           ID of association
 | 
			
		||||
                                                  (autogenerated)
 | 
			
		||||
    instance_id        varchar(36)   No           ID of instance
 | 
			
		||||
    module_id          varchar(36)   No           ID of module
 | 
			
		||||
    md5                varchar(32)   No           MD5 hash of module contents
 | 
			
		||||
    created            DateTime      No           Created date
 | 
			
		||||
    updated            DateTime      No           Updated date
 | 
			
		||||
    deleted            tinyint(1)    Yes          Deleted flag
 | 
			
		||||
    deleted_at         DateTime      Yes          Deleted date
 | 
			
		||||
    =================  ============  ===========  ==============================
 | 
			
		||||
 | 
			
		||||
Public API
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
New ReST API calls will be added to the Trove infrastructure.  These fall into
 | 
			
		||||
two categories - ones to manage the maintenance of the actual modules, and
 | 
			
		||||
ones to handle the instance interactions.
 | 
			
		||||
 | 
			
		||||
In addition, the create and cluster-create calls will be enhanced.
 | 
			
		||||
 | 
			
		||||
Module Maintenance
 | 
			
		||||
..................
 | 
			
		||||
 | 
			
		||||
To retrieve a list of all modules that can be applied, the following request
 | 
			
		||||
would be made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    GET v1/modules
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        'modules' : [
 | 
			
		||||
            {
 | 
			
		||||
                'id': <id>,
 | 
			
		||||
                'type': 'vertica_license',
 | 
			
		||||
                'tenant': <id>,
 | 
			
		||||
                'datastore': 'vertica',
 | 
			
		||||
                'datastore_version': 'all',
 | 
			
		||||
                'name': '100GB',
 | 
			
		||||
                'description': 'Vertica license for 100GB',
 | 
			
		||||
                'auto_apply': False,
 | 
			
		||||
                'visible': True,  # returned for admin only
 | 
			
		||||
                'live_update': False,
 | 
			
		||||
                'md5': <md5>,
 | 
			
		||||
                'created': <date>,
 | 
			
		||||
                'updated': <date>,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                'id': <id>,
 | 
			
		||||
                'type': 'new_relic_activation',
 | 
			
		||||
                'tenant': <id>,
 | 
			
		||||
                'datastore': 'all',
 | 
			
		||||
                'datastore_version': 'all',
 | 
			
		||||
                'name': 'new_relic',
 | 
			
		||||
                'description': 'New Relic activation',
 | 
			
		||||
                'auto_apply': True,
 | 
			
		||||
                'visible': True,  # returned for admin only
 | 
			
		||||
                'live_update': True,
 | 
			
		||||
                'md5': <md5>,
 | 
			
		||||
                'created': <date>,
 | 
			
		||||
                'updated': <date>,
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    200  Success
 | 
			
		||||
 | 
			
		||||
Note that an admin user will receive the modules for all tenants, whereas
 | 
			
		||||
regular users will see modules for their tenant only.
 | 
			
		||||
 | 
			
		||||
To retrieve a list of valid modules that can be applied to a specific
 | 
			
		||||
datastore, the following request would be made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    GET v1/datastores/{datastore_id}/modules
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        'modules' : [
 | 
			
		||||
            {
 | 
			
		||||
                'id': <id>,
 | 
			
		||||
                'type': 'new_relic_activation',
 | 
			
		||||
                'tenant': <id>,
 | 
			
		||||
                'datastore': 'all',
 | 
			
		||||
                'datastore_version': 'all',
 | 
			
		||||
                'name': 'new_relic',
 | 
			
		||||
                'description': 'New Relic activation',
 | 
			
		||||
                'auto_apply': True,
 | 
			
		||||
                'visible': True,  # returned for admin only
 | 
			
		||||
                'live_update': True,
 | 
			
		||||
                'md5': <md5>,
 | 
			
		||||
                'updated': <date>,
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    200  Success
 | 
			
		||||
 | 
			
		||||
To show the details of a particular module, the following request would be
 | 
			
		||||
made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    GET v1/modules/<id>
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        'id': <id>,
 | 
			
		||||
        'type': 'new_relic_activation',
 | 
			
		||||
        'tenant': <id>,
 | 
			
		||||
        'datastore': 'all',
 | 
			
		||||
        'datastore_version': 'all',
 | 
			
		||||
        'name': 'new_relic',
 | 
			
		||||
        'description': 'New Relic activation',
 | 
			
		||||
        'auto_apply': True,
 | 
			
		||||
        'visible': True,  # returned for admin only
 | 
			
		||||
        'live_update': True,
 | 
			
		||||
        'md5': <md5>,
 | 
			
		||||
        'created': <date>,
 | 
			
		||||
        'updated': <date>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    200  Success
 | 
			
		||||
    404  Not Found
 | 
			
		||||
 | 
			
		||||
To create a module, the following request would be made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    POST /v1.0/modules
 | 
			
		||||
    {
 | 
			
		||||
        'type': 'vertica_license',
 | 
			
		||||
        'tenant': <id>,
 | 
			
		||||
        'datastore': 'vertica',
 | 
			
		||||
        'datastore_version': 'all',
 | 
			
		||||
        'name': '100GB',
 | 
			
		||||
        'description': 'Vertica license for 100GB',
 | 
			
		||||
        'auto_apply': False,
 | 
			
		||||
        'visible': False,  # admin-only option
 | 
			
		||||
        'live_update': True,
 | 
			
		||||
        'contents': <module_contents>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        "module": {
 | 
			
		||||
            'id': <id>,
 | 
			
		||||
            'type': 'vertica_license',
 | 
			
		||||
            'tenant': <id>,
 | 
			
		||||
            'datastore': 'vertica',
 | 
			
		||||
            'datastore_version': 'all',
 | 
			
		||||
            'name': '100GB',
 | 
			
		||||
            'description': 'Vertica license for 100GB',
 | 
			
		||||
            'auto_apply': False,
 | 
			
		||||
            'visible': False,  # returned for admin only
 | 
			
		||||
            'live_update': True,
 | 
			
		||||
            'md5': <md5>,
 | 
			
		||||
            'created': <date>,
 | 
			
		||||
            'updated': <date>,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    200  Success
 | 
			
		||||
    400  Bad Request
 | 
			
		||||
 | 
			
		||||
To update a module, the following request would be made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    PATCH /v1.0/modules/{module_id}
 | 
			
		||||
    {
 | 
			
		||||
        'type': 'new_type',
 | 
			
		||||
        'tenant': <id>,
 | 
			
		||||
        'datastore': 'new_datastore',
 | 
			
		||||
        'datastore_version': 'new_datastore_version',
 | 
			
		||||
        'name': 'new_name',
 | 
			
		||||
        'description': 'new_description',
 | 
			
		||||
        'auto_apply': True,
 | 
			
		||||
        'visible': False,  # admin-only option
 | 
			
		||||
        'live_update': True,
 | 
			
		||||
        'contents': <module_contents>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        "module": {
 | 
			
		||||
            'id': <id>,
 | 
			
		||||
            'type': 'new_type',
 | 
			
		||||
            'tenant': <id>,
 | 
			
		||||
            'datastore': 'new_datastore',
 | 
			
		||||
            'datastore_version': 'new_datastore_version',
 | 
			
		||||
            'name': 'new_name',
 | 
			
		||||
            'description': 'new_description',
 | 
			
		||||
            'auto_apply': True,
 | 
			
		||||
            'visible': False,  # returned for admin only
 | 
			
		||||
            'live_update': True,
 | 
			
		||||
            'md5': <new_md5>,
 | 
			
		||||
            'created': <date>,
 | 
			
		||||
            'updated': <date>,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    200  Success
 | 
			
		||||
    400  Bad Request
 | 
			
		||||
    404  Not Found
 | 
			
		||||
 | 
			
		||||
To delete a module, the following request would be made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    DELETE /v1.0/modules/{module_id}
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    This operation has no response body
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    200  Success
 | 
			
		||||
    404  Not Found
 | 
			
		||||
 | 
			
		||||
To query which instances have a particular module applied, the following
 | 
			
		||||
request would be made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    GET v1/modules/{module_id}/instances
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        'instance': <id>,
 | 
			
		||||
        'modules' : [
 | 
			
		||||
            {
 | 
			
		||||
                'name': '100GB',
 | 
			
		||||
                'id': <id>,
 | 
			
		||||
                'md5': <md5>,
 | 
			
		||||
                'installed': <date>,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                'name': 'new_relic',
 | 
			
		||||
                'id': <id>,
 | 
			
		||||
                'md5': <md5>,
 | 
			
		||||
                'installed': <date>,
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    200  Success
 | 
			
		||||
    404  Not Found
 | 
			
		||||
 | 
			
		||||
Instance Interaction
 | 
			
		||||
....................
 | 
			
		||||
 | 
			
		||||
To apply modules to an instance, the following request would be made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    POST v1/{tenant_id}/instances/{instance_id}/modules
 | 
			
		||||
    {
 | 
			
		||||
        'modules' : [
 | 
			
		||||
            {
 | 
			
		||||
                "id": <id>,
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        'type': 'vertica_license',
 | 
			
		||||
        'datastore': 'vertica',
 | 
			
		||||
        'datastore_version': 'all',
 | 
			
		||||
        'name': '100GB',
 | 
			
		||||
        'md5': <md5>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    202  Success
 | 
			
		||||
    400  Bad Request
 | 
			
		||||
    404  Not Found
 | 
			
		||||
 | 
			
		||||
To query an instance about installed modules, the following request would be
 | 
			
		||||
made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    GET v1/{tenant_id}/instances/{instance_id}/modules
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        'modules' : [
 | 
			
		||||
            {
 | 
			
		||||
                'type': 'vertica_license',
 | 
			
		||||
                'datastore': 'vertica',
 | 
			
		||||
                'datastore_version': 'all',
 | 
			
		||||
                'name': '100GB',
 | 
			
		||||
                'filename': 'vertica-all-100GB.lic',
 | 
			
		||||
                'md5': <md5>,
 | 
			
		||||
                'installed': <date>,
 | 
			
		||||
                'status': 'OK',
 | 
			
		||||
                'error_message': None,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                'type': 'new_relic_activation',
 | 
			
		||||
                'datastore': 'all',
 | 
			
		||||
                'datastore_version': 'all',
 | 
			
		||||
                'name': 'new_relic',
 | 
			
		||||
                'filename': 'all-all-new_relic.lic',
 | 
			
		||||
                'md5': <md5>,
 | 
			
		||||
                'installed': <date>,
 | 
			
		||||
                'status': 'FAILED',
 | 
			
		||||
                'error_message': 'New Relic binaries not found',
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    200  Success
 | 
			
		||||
    404  Not Found
 | 
			
		||||
 | 
			
		||||
To retrieve a module from an instance, the following request would be made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    GET v1/{tenant_id}/instances/{instance_id}/modules/{module_id}
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        'filename': 'vertica-all-100GB.lic',
 | 
			
		||||
        'contents': <module_contents>,
 | 
			
		||||
        'md5': <md5>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    200  Success
 | 
			
		||||
    404  Not Found
 | 
			
		||||
 | 
			
		||||
To delete a module from an instance, the following request would be made:
 | 
			
		||||
 | 
			
		||||
Request::
 | 
			
		||||
 | 
			
		||||
    DELETE v1/{tenant_id}/instances/{instance_id}/modules/{module_id}
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Response::
 | 
			
		||||
 | 
			
		||||
    This operation has no response body
 | 
			
		||||
 | 
			
		||||
Response Codes::
 | 
			
		||||
 | 
			
		||||
    202  Success
 | 
			
		||||
    404  Not Found
 | 
			
		||||
 | 
			
		||||
Creation Enhancements
 | 
			
		||||
.....................
 | 
			
		||||
 | 
			
		||||
The instance create API will be enhanced to include a module field, containing
 | 
			
		||||
a list of modules to apply.  These will be sent down during the normal
 | 
			
		||||
'prepare' call and the appropriate plugin called once this instance has been
 | 
			
		||||
provisioned correctly.
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        'modules' : [
 | 
			
		||||
            {
 | 
			
		||||
                "id": <id>,
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
In a similar manner, the cluster create API will also be enhanced to include
 | 
			
		||||
module information in the instances field, as is currently done with flavors,
 | 
			
		||||
AZs, etc.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Public API Security
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
Since the file will be transmitted clear text across the management
 | 
			
		||||
network, there is a chance that the module can be intercepted if the network
 | 
			
		||||
is compromised.
 | 
			
		||||
 | 
			
		||||
It should be ensured that each plugin created does not 'execute' the contents
 | 
			
		||||
of the supplied module data file, as this would present the opportunity for a
 | 
			
		||||
security breach.  This seems unlikely though (and will not be the case for the
 | 
			
		||||
proposed implementations) as most module data files will be passed to another
 | 
			
		||||
process for validation, and it is up to that process to ensure proper security
 | 
			
		||||
is maintained.  Code reviews will be vital to make sure no plugin accidentally
 | 
			
		||||
executes this data.
 | 
			
		||||
 | 
			
		||||
Python API
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
New methods will be added to the Python API to facilitate the licensing.
 | 
			
		||||
A few existing methods will need to be extended as well.
 | 
			
		||||
 | 
			
		||||
Module Maintenance
 | 
			
		||||
..................
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    def module_list(self, datastore=None):
 | 
			
		||||
        """Get a list of all modules that can be applied. Return only
 | 
			
		||||
        those that apply to the datastore if it is passed in.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    def module_list_instances(self, module):
 | 
			
		||||
        """Get a list of all instances that have a given module applied."""
 | 
			
		||||
 | 
			
		||||
    def module_show(self, module):
 | 
			
		||||
        """Show the details of the module."""
 | 
			
		||||
 | 
			
		||||
    def module_create(self, module_type, name, description, contents,
 | 
			
		||||
                      datastore, datastore_version='all', auto_apply=False,
 | 
			
		||||
                      all_tenants=False, visible=True, live_update=False):
 | 
			
		||||
        """Create a new module."""
 | 
			
		||||
 | 
			
		||||
    def module_update(self, module, module_type=None, name=None,
 | 
			
		||||
                      description=None, contents=None, datastore=None,
 | 
			
		||||
                      datastore_version=None, auto_apply=None,
 | 
			
		||||
                      all_tenants=None, visible=None, live_update=None):
 | 
			
		||||
        """Update an existing module."""
 | 
			
		||||
 | 
			
		||||
    def module_delete(self, module):
 | 
			
		||||
        """Delete a module."""
 | 
			
		||||
 | 
			
		||||
Instance Interaction
 | 
			
		||||
....................
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    def module_apply(self, instance, modules):
 | 
			
		||||
        """Apply modules to an instance."""
 | 
			
		||||
 | 
			
		||||
    def module_query(self, instance):
 | 
			
		||||
        """Query an instance about installed modules."""
 | 
			
		||||
 | 
			
		||||
    def module_retrieve(self, instance, module=None, filename=None):
 | 
			
		||||
        """Retrieve the module data file from an instance and save it in
 | 
			
		||||
        filename.  If module is not supplied, retrieve all the modules.
 | 
			
		||||
        If filename is not supplied, use the generated filename found
 | 
			
		||||
        on the instance.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    def module_remove(self, instance, module):
 | 
			
		||||
        """Remove a module from an instance."""
 | 
			
		||||
 | 
			
		||||
Creation Enhancements
 | 
			
		||||
.....................
 | 
			
		||||
 | 
			
		||||
For instance.create, the modules field will be added to the call:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    def create(self, name, flavor_id, volume=None, databases=None, users=None,
 | 
			
		||||
               restorePoint=None, availability_zone=None, datastore=None,
 | 
			
		||||
               datastore_version=None, nics=None, configuration=None,
 | 
			
		||||
               replica_of=None, slave_of=None, replica_count=None,
 | 
			
		||||
               modules=None):
 | 
			
		||||
        """Create (boot) a new instance."""
 | 
			
		||||
 | 
			
		||||
For cluster.create, the modules field will be added to the
 | 
			
		||||
['cluster']['instances'] data structure that is already being passed in.
 | 
			
		||||
 | 
			
		||||
CLI (python-troveclient)
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
The following Trove CLI commands (upon completion) will be fully functional
 | 
			
		||||
 | 
			
		||||
- module-list          Displays all modules for the tenant.
 | 
			
		||||
- module-show          Shows details for a particular module resource.
 | 
			
		||||
- module-create        Creates a new module resource.
 | 
			
		||||
- module-update        Updates module details for a particular module
 | 
			
		||||
                       resource.
 | 
			
		||||
- module-delete        Delete a module resource.
 | 
			
		||||
 | 
			
		||||
- module-apply         Apply the given modules to a Trove instance.
 | 
			
		||||
- module-query         Query the given Trove instance for any installed
 | 
			
		||||
                       modules.
 | 
			
		||||
- module-retrieve      Retrieves the current modules from a Trove instance.
 | 
			
		||||
- module-remove        Remove a module from a Trove instance.
 | 
			
		||||
 | 
			
		||||
- create --module [--module]
 | 
			
		||||
                       Creates a new instance and applies the given modules.
 | 
			
		||||
 | 
			
		||||
- cluster-create --instance=module=<id>[,module=<id>]
 | 
			
		||||
                       Creates a new cluster and applies the given modules to
 | 
			
		||||
                       each instance.
 | 
			
		||||
 | 
			
		||||
Internal API
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Changes also need to be made to the internal API to include any module IDs as
 | 
			
		||||
a part of the message body that is sent to the task manager.
 | 
			
		||||
 | 
			
		||||
The API server will need to make calls to the Guest Agent for the instance
 | 
			
		||||
interaction type commands.
 | 
			
		||||
 | 
			
		||||
Guest Agent
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
In the Guest Agent, the modules will be managed with a plugin style
 | 
			
		||||
architecture based on the stevedore.driver.DriverManager paradym.  Each plugin
 | 
			
		||||
will need to implement 'apply', 'query' and 'remove' actions.  The 'query'
 | 
			
		||||
action will need to report the status of the module 'apply' action.  This
 | 
			
		||||
would report (at a minimum) 'OK' or 'FAILED' plus any other state that seems
 | 
			
		||||
reasonable for users of the relevant software.  If possible, the
 | 
			
		||||
'error_message' field should be filled with useful information if an error
 | 
			
		||||
occurs.
 | 
			
		||||
 | 
			
		||||
A simple plugin 'base class' that defines the contract will be provided.  It
 | 
			
		||||
will also provide functionality such as placing the file contents into a
 | 
			
		||||
specified location and retrieving the file will be added.  This can be used
 | 
			
		||||
as the basis for all other plugins.
 | 
			
		||||
 | 
			
		||||
The Guest Agent code will use the module 'type' to determine if a plugin exists
 | 
			
		||||
for the given module.  If no plugin can be found, then an error will be written
 | 
			
		||||
to the log and processing stopped.
 | 
			
		||||
 | 
			
		||||
To provide a concrete, real-world plugin implementation, a Vertica license
 | 
			
		||||
module plugin will be created to allow licenses to be applied to a Vertica
 | 
			
		||||
datastore.  A New Relic plugin will also be created to illustrate activation of
 | 
			
		||||
other third party software on a guest image.
 | 
			
		||||
 | 
			
		||||
Alternatives
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Dashboard Impact (UX)
 | 
			
		||||
=====================
 | 
			
		||||
 | 
			
		||||
A multi-dropdown will need to be added to the instance create dialog that
 | 
			
		||||
contains all modules for the selected datastore.  These modules, along with
 | 
			
		||||
any auto-apply ones, will need to be sent along on the create call.  The same
 | 
			
		||||
will be needed for the cluster create dialog.
 | 
			
		||||
 | 
			
		||||
A module detail panel will need to be created.  This panel will have fields
 | 
			
		||||
representing the attributes of a module (see module-create command).
 | 
			
		||||
 | 
			
		||||
A 'modules' list panel will need to be created.  This will have buttons for
 | 
			
		||||
'delete' and 'update' and will have a link to the detail page for each listed
 | 
			
		||||
module.  This will be a high-level panel, similar to 'Instances.'
 | 
			
		||||
 | 
			
		||||
The instance list panel will need to have a new action added: 'apply module.'
 | 
			
		||||
This will cause a pop-up where the available modules are displayed.  The
 | 
			
		||||
selected module will then be passed in to the module-apply command.
 | 
			
		||||
 | 
			
		||||
The instance detail panel will need to run 'module-query' and display the
 | 
			
		||||
results in a new section 'modules.' Alternately, a link could be placed here
 | 
			
		||||
that would open a module list panel with the results of the 'module-query'
 | 
			
		||||
call.  Here, buttons for 'module-remove' and 'module-retrieve' would be
 | 
			
		||||
needed.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation
 | 
			
		||||
==============
 | 
			
		||||
 | 
			
		||||
Assignee(s)
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
Primary assignee:
 | 
			
		||||
    [peterstac]
 | 
			
		||||
 | 
			
		||||
Milestones
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
Mitaka
 | 
			
		||||
 | 
			
		||||
Work Items
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
The work will be undertaken with the following tasks:
 | 
			
		||||
 | 
			
		||||
    * Client (Python and CLI) changes
 | 
			
		||||
    * Server (API) changes
 | 
			
		||||
    * Guest Agent module plugin infrastructure
 | 
			
		||||
    * Vertica/New Relic plugin implementation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Upgrade Implications
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
Since this change is net-new, no upgrade issues are expected.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Dependencies
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
None.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Testing
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
Generic int-tests will be written, however these will not be run under MySQL
 | 
			
		||||
testing as it requires no module-based handling.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Documentation Impact
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
This is a net-new feature, and as such will require documentation.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
References
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
.. [1] nrsysmond-config --set license_key=<new_relic_key>.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Appendix
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
None
 | 
			
		||||
		Reference in New Issue
	
	Block a user