Application development framework¶
Application development framework is a library that helps application
developers to create applications that can be scalable, highly available,
(self)healable and do not contain boilerplate code for common application
workflow operations. This library is placed into the Murano repository under
the meta/io.murano.applications folder.
To allow your applications to use the code of the library, zip it and upload to the Murano application catalog.
Framework objectives¶
The library allows application developers to focus on their application-specific tasks without the real need to dive into resource orchestration, server farm configuration, and so on. For example, on how to install the software on the VMs, how to configure it to interact with other applications. Application developers are able to focus more on the software configuration tools (scripts, puppets, and others) and care less about the MuranoPL if they do not need to define any custom workflow logic.
The main capabilities the library provides and its main use-cases are as follows:
Standard operations are implemented in the framework and can be left as is
The capability to create multi-server applications and scale them
The capability to create composite multi-component applications
The capability to track application failures and recover from them
The capability to define event handlers for various events
Quickstart¶
To use the framework in your application, include the following lines to the
manifest.yaml file:
Require:
io.murano.applications:
Create a one-component single-server application¶
To create a simple application deployed on a single server:
Include the following lines to the code of the application class:
Namespaces: =: my.new.ns apps: io.murano.applications Name: AppName Extends: apps:SingleServerApplication
Provide an input for the application
serverproperty in yourui.yamlfile:Application: ?: type: my.new.ns.AppName server: ?: type: io.murano.resources.LinuxMuranoInstance name: generateHostname($.instanceConfiguration.unitNamingPattern, 1) flavor: $.instanceConfiguration.flavor ... <other instance properties>
Now you already have the app that creates a server ready for installing software on it.
To create a fully functional app, add an installation script to the body of the
onInstallServermethod:Methods: onInstallServer: Arguments: - server: Contract: $.class(res:Instance).notNull() - serverGroup: Contract: $.class(apps:ServerGroup).notNull() Body: - $file: sys:Resources.string('installScript.sh') - conf:Linux.runCommand($server.agent, $file)
Optional. Add other methods that handle certain stages of the application workflow, such as
onBeforeInstall,onCompleteInstallation,onConfigureServer,onCompleteConfiguration, and others. For details about these methods, see the Software components section.
Create a one-component multi-server application¶
To create an application that is intended to be installed on several servers:
Make it inherit the
MultiServerApplicationclass:Namespaces: =: my.new.ns apps: io.murano.applications Name: AppName Extends: apps:MultiServerApplication
Instead of the
serverproperty inSingleServerApplication, provide an input for theserversproperty that accepts the instance of one of the inheritors of theServerGroupclass. Theui.yamlfile in this case may look as follows:Application: ?: type: my.new.ns.AppName servers: ?: type: io.murano.applications.ServerList servers: - ?: type: io.murano.resources.LinuxMuranoInstance name: "Server-1" flavor: $.instanceConfiguration.flavor ... <other instance properties> - ?: type: io.murano.resources.LinuxMuranoInstance name: "Server-2" flavor: $.instanceConfiguration.flavor ... <other instance properties>
Define the custom logic of the application in the handler methods, and it will be applied to the whole app, exactly like with
SingleServerApplication.
Create a scalable multi-server application¶
To provide the application with the ability to scale:
Make the app extend the
MultiServerApplicationWithScalingclass:Namespaces: =: my.new.ns apps: io.murano.applications Name: AppName Extends: apps:MultiServerApplicationWithScaling
Provide the
ui.yamlfile:Application: ?: type: my.new.ns.AppName servers: ?: type: io.murano.applications.ServerReplicationGroup numItems: $.appConfiguration.numNodes provider: ?: type: io.murano.applications.TemplateServerProvider template: ?: type: io.murano.resources.LinuxMuranoInstance flavor: $.instanceConfiguration.flavor ... <other instance properties> serverNamePattern: $.instanceConfiguration.unitNamingPattern
The
serversproperty accepts instance of theServerReplicationGroupclass, and in turn it requires input of thenumItemsandproviderproperties.
After the deployment, the scaleOut and scaleIn public methods
(actions) become available in the dashboard UI.
For a working example of such application, see the
com.example.apache.ApacheHttpServer package version 1.0.0.
Library overview¶
The framework includes several groups of classes:
replication.yamlClasses that provide the capability to replicate the resources.
servers.yamlClasses that provide instances grouping and replication.
component.yamlClasses that define common application workflows.
events.yamlClass for handling events.
baseapps.yamlBase classes for applications.
As it is described in the QuickStart section, the application makes use
of the Application development framework by inheriting from one of the base
application classes, such as SingleServerApplication,
MultiServerApplication, MultiServerApplicationWithScaling. In turn,
these classes are inheritors of the standard Application class and the
SoftwareComponent class. The latter class binds all of the framework
capabilities.
The SoftwareComponent class inherits both Installable and
Configurable classes which provide boilerplate code for the installation
and configuration workflow respectively. They also contain empty methods
for each stage of the workflow (e.g. onBeforeInstall, onInstallServer),
which are the places where application developers can add their own
customization code.
The entry point to execute deployment of the software component is its
deployAt method which requires instance of one of the inheritors of the
serverGroup class. It is the object representing the group of servers
the application should be deployed to. The application holds such an object as
one of its properties. It can be a single server (SingleServerGroup
subclass), a prepopulated list of servers (ServerList subclass) or a list
of servers that are dynamically generated in runtime
(ServerReplicationGroup subclass).
ServerReplicationGroup or, more precisely, one of its parent classes
ReplicationGroup controls the number of items it holds by releasing items
over the required amount and requesting creation of the new items in runtime
from the ReplicaProvider class which acts like an object factory. In case
of servers, it is TemplateServerProvider which creates new servers from the
given template. Replication is done during the initial deployment and during
the scaling actions execution.
Framework detailed description¶
This section provides technical description of all the classes present in the application development library, their hierarchy and usage.
Scaling primitives¶
There is an ability to group similar resources together, produce new copies of the same resources or release the existing ones on request. Now it is implemented for instances only, other resources may be added later.
The following is the hierarchy of classes that provide grouping and replication of resources:
+-------+
| +-------+
| | +--------+ +------------------+ +-----------------+
| | | | | | | |
+-+ | Object <--------+ ReplicationGroup +--------> ReplicaProvider |
+-+ | | | | |
+--------+ +---+--------------+ +-+--------+------+
^ ^ ^
| | |
| +------------------+-----+ |
| | | |
+-------+ | | CloneReplicaProvider | |
| +-------+ | | + other | |
| | +----------+ | +------------------------+ |
| | | | | |
+-+ | Instance | | |
+-+ | | |
+----+-----+ | |
| | |
+-----+-------+ | |
| | | |
| ServerGroup | | +---------------+--+
| | | | Template |
+-----^-------+ +---+----------+ | Server +--+
| | Server +-------> Provider | |
+------------+ Replication | +-----+------------+ +---+
| Group | | | |
+--------------+ +---+---other---+ |
| |
+---------------+
ReplicationGroup
A base class which holds the collection of objects generated in runtime in its
itemsoutput property and contains a reference to aReplicaProviderobject in itsproviderproperty which is used to dynamically generate the objects in runtime.Input properties of this class include the
minItemsandmaxItemsallowing to limit the number of objects it holds in its collection.An input-output property
numItemsallows to declaratively change the set of objects in the collection by setting its size.The
deploy()method is used to apply the replica settings: it drops the objects from the collection if their number exceeds the number specified by thenumItemsor generate some new if there are not enough of them.The
scale()method is used to increase or decrease thenumItemsby some number specified in thedeltaargument of the method, but in range betweenmaxItemsandminItems.
ReplicaProvider
A class which does the object replication. The base one is abstract, its inheritors should implement the abstract
createReplicamethod to create the actual object. The method accepts theindexparameter to properly parametrize the newly created copy and optionalownerparameter to use it as an owner for the newly created objects.The concrete implementations of this class should define all the input properties needed to create new instances of object. Thus the provider actually acts as a template of the object it generates.
CloneReplicaProvider
An implementation of
ReplicaProvidercapable to create replicas by cloning some user-provided object, making use of thetemplate()contract.
PoolReplicaProvider
Replica provider that takes replicas from the prepopulated pool instead of creating them.
RoundrobinReplicaProvider
Replica provider with a load balancing that returns replica from the prepopulated list. Once the provider runs out of free items it goes to the beginning of the list and returns the same replicas again.
CompositeReplicaProvider
Replica provider which is a composition of other replica providers. It holds the collection of providers in its
providersinput property. ItsReplicaProvidermethod returns a new replica created by the first provider in that list. If that value is null, the replica created by the second provider is returned, and so on. If no not-null replicas are created by all providers, the method returns null.This provider can be used to have some default provider with the ability to fall back to the next options if the preferable one is not successful.
Servers replication¶
ServerGroup
A class that provides static methods for deployment and releasing resources on the group of instances.
The
deployServers()static method accepts instance ofServerGroupclass and a list of servers as the parameters and deploys all servers from the list in the environment which owns the server group, unless server is already deployed.The
releaseServers()static method accepts a list of servers as the parameter and consequentially callsbeginReleaseResources()andendReleaseResources()methods on each server.
ServerList
A class that extends the
ServerGroupclass and holds a group of prepopulated servers in itsserversinput property.The
deploy()method calls thedeployServers()method with the servers defined in theserversproperty.The
.destroy()method calls thereleaseServers()method with the servers defined in theserversproperty.
SingleServerGroup
Degenerate case of a
ServerGroupwhich consists of a single server. Has theserverinput property to hold a single server.
CompositeServerGroup
A server group that is composed of other server groups.
ServerReplicationGroup
A subclass of the
ReplicationGroupclass and theServerGroupclass to replicate theInstanceobjects it holds.The
deploy()method of this group not only generates new instances of servers but also deploys them if needed.
TemplateServerProvider
A subclass of
ReplicaProviderwhich is used to produce the objects of one of theInstanceclass inheritors by creating them from the provided template with parameterization of the hostnames. The resulting hostname looks like ‘Server {index}{groupName}’.May be passed as
providerproperty to objects of theServerReplicationGroupclass.
other replica providers
Other subclasses of
ReplicaProvidermay be created to produce different objects ofInstanceclass and its subclasses depending on particular application needs.
Classes for grouping and replication of other kinds of resources are to be implemented later.
Software Components¶
The class to handle the lifecycle of the application is the
SoftwareComponent class which is a subclass of Installable and
Configurable:
+-----------+-+ +-+------------+
| | | |
| Installable | | Configurable |
| | | |
+-----------+-+ +-+------------+
^ ^
| |
| |
+-+---------------+-+
| |
| SoftwareComponent |
| |
+-------------------+
The hierarchy of the SoftwareComponent classes is used to define the
workflows of different application lifecycles. The general logic of the
application behaviour is contained in the methods of the base classes and
the derived classes are able to implement the handlers for the custom logic.
The model is event-driven: the workflow consists of the multiple steps, and
most of the steps invoke appropriate on%StepName% methods intended to
provide application-specific logic.
Now ‘internal’ steps logic and their ‘public’ handlers are split into the separate methods. It should improve the developers’ experience and simplify the code of the derived classes.
The standard workflows (such as Installation and Configuration) are defined
by the Installable and Configurable classes respectively. The
SoftwareComponent class inherits both these classes and defines its
deployment workflow as a sequence of Installation and Configuration flows.
Other future implementations may add new workflow interfaces and mix them in
to change the deployment workflow or add new actions.
Installation workflow consists of the following methods:
+----------------------------------------------------------------------------------------------------------------------+
| INSTALL |
| |
| +------------------------------+ +---------------+ |
| +------------------------------+ | +---------------+ | |
| +------------------------------+ | | +---------------+ +---------------+ | | +----------------------+ |
| | | | | | | | | | | | | |
| | checkServerIsInstalled | +-+ +----> beforeInstall +----> installServer | +-+ +----> completeInstallation | |
| | +-+ | | | +-+ | | |
| +------------------------------+ +------+--------+ +------+--------+ +-----------+----------+ |
| | | | |
+----------------------------------------------------------------------------------------------------------------------+
| | |
| | |
| | |
v v v
onBeforeInstall onInstallServer onCompleteInstallation
Method |
Arguments |
Description |
|---|---|---|
install |
|
Entry point of the installation workflow.
Iterates through all the servers of the passed ServerGroup and calls the
|
checkServerIsInstalled |
|
Checks if the given server requires a (re)deployment of the software component. By default checks for the value of the attribute installed of the instance. May be overridden by subclasses to provide some better logic (e.g. the app developer may provide code to check if the given software is pre-installed on the image which was provisioned on the VM). |
beforeInstall |
|
Reports the beginning of installation process, sends notification about
this event to all objects which are subscribed for it (see
Event notification pattern section for details) and calls the public
event handler |
onBeforeInstall |
|
Public handler of the beforeInstall event. Empty in the base class, may be overridden in subclasses if some custom pre-install logic needs to be executed. |
installServer |
|
Does the actual software deployment on a given server by calling an
|
onInstallServer |
|
An event-handler method which is called by the |
completeInstallation |
|
It is executed after all the |
onCompleteInstallation |
|
An event-handler method which is called by the |
Configuration workflow consists of the following methods:
+----------------------------------------------------------------------------------------------------------------------+
| CONFIGURATION |
| +-----------------+ |
| | | |
| | +---------------+ +-----------------+ |
| | +---------------+ | +-----------------+ | |
| +------------v--+ +---------------+ | | +--------------+ +-----------------+ | | +-----------------------+ |
| | | | | | | | | | | | | | | |
| | checkCluster\ +---> checkServer\ | +-+---> preConfigure +---> configureServer | +-+---> completeConfiguration | |
| | IsConfigured | | IsConfigured +-+ | | | +-+ | | |
| +------------+--+ +---------------+ +------+-------+ +--------+--------+ +-----------+-----------+ |
| | | | | |
| | | | | |
| +----------v----------+ | | | |
| | | | | | |
| | getConfigurationKey | | | | |
| | | | | | |
| +---------------------+ | | | |
| | | | |
+----------------------------------------------------------------------------------------------------------------------+
| | |
| | |
v v v
configureSecurity, onConfigureServer onCompleteConfiguration
onPreConfigure
Method |
Arguments |
Description |
|---|---|---|
configure |
|
Entry point of the configuration workflow.
Calls a |
checkClusterIsConfigured |
|
Has to return true if the configuration (i.e. the values of input
properties) of the component has not been changed since it was last
deployed on the given server group. Default implementation calls the
|
getConfigurationKey |
None |
Should return some values describing the configuration state of the
component. This state is used to track the changes of the configuration
by the |
checkServerIsConfigured |
|
It is called to check if the particular server of the server group has
to be reconfigured thus providing more precise control compared to
cluster-wide |
preConfigure |
|
Reports the beginning of configuration process, calls the
|
configureSecurity |
|
Intended for configuring the security rules. It is empty in the base
class. Fully implemented in the |
onPreConfigure |
|
Public event-handler which is called by the |
configureServer |
|
Does the actual software configuration on a given server by calling the
|
onConfigureServer |
|
An event-handler method which is called by the |
completeConfiguration |
|
It is executed after all the |
onCompleteConfiguration |
|
The event-handler method which is called by the |
The OpenStackSecurityConfigurable class extends Configurable by
implementing the configureSecurity method of the base class and adding the
empty getSecurityRules method.
Method |
Arguments |
Description |
|---|---|---|
getSecurityRules |
None |
Returns an empty dictionary in default implementation. Inheritors which want to add security rules during the app configuration should implement this method and make it return a list of dictionaries describing the security rules with the following keys:
|
configureSecurity |
|
Gets the list of security rules provided by the |
Consider the following example of this class usage:
Namespaces:
=: com.example.apache
apps: io.murano.applications
Name: ApacheHttpServer
Extends:
- apps:MultiServerApplicationWithScaling
- apps:OpenStackSecurityConfigurable
Methods:
getSecurityRules:
Body:
- Return:
- ToPort: 80
FromPort: 80
IpProtocol: tcp
External: true
- ToPort: 443
FromPort: 443
IpProtocol: tcp
External: true
In the example above, the ApacheHttpServer class is configured to create
a security group with two security rules allowing network traffic over HTTP
and HTTPS protocols on its deployment.
The SoftwareComponent class inherits both Installable and
Configurable and adds several additional methods.
Method |
Arguments |
Description |
|---|---|---|
deployAt |
|
Binds all workflows into one process. Consequentially calls |
report |
|
Reports a |
detectSuccess |
|
Static method that returns true in case the actual number of failures
(number of |
Event notification pattern¶
The Event class may be used to issue various notifications to other
MuranoPL classes in an event-driven manner.
Any object which is going to emit the notifications should declare the
instances of the Event class as its public Runtime properties. You can see
the examples of such properties in the Installable and Configurable
classes:
Name: Installable
Properties:
beforeInstallEvent:
Contract: $.class(Event).notNull()
Usage: Runtime
Default:
name: beforeInstall
The object which is going to subscribe for the notifications should pass
itself into the subscribe method of the event along with the name of its
method which will be used to handle the notification:
$event.subscribe($subscriber, handleFoo)
The specified handler method must be present in the subscriber class
(if the method name is missing it will default to handle%Eventname%)
and have at least one standard (i.e. not VarArgs or KwArgs) argument
which will be treated as sender while invoking.
The unsubscribe method does the opposite and removes object from the
subscribers of the event.
The class which is going to emit the notification should call the notify
method of the event and pass itself as the first argument (sender). All
the optional parameters of the event may be passed as varargs/kwargs of the
notify call. They will be passed all the way to the handler methods.
This is how it looks in the Installable class:
beforeInstall:
Arguments:
- servers:
Contract:
- $.class(res:Instance).notNull()
- serverGroup:
Contract: $.class(ServerGroup).notNull()
Body:
- ...
- $this.beforeInstallEvent.notify($this, $servers, $serverGroup)
- ...
The notifyInParallel method does the same, but invokes all handlers of
subscribers in parallel.
Base application classes¶
There are several base classes that extend standard io.murano.Application
class and SoftwareComponent class from the application development
library.
- SingleServerApplication
A base class for applications running a single software component on a single server only. Its
deploymethod simply creates theSingleServerGroupwith theserverprovided as an application input.- MultiServerApplication
A base class for applications running a single software component on multiple servers. Unlike
SingleServerApplication, it has theserversinput property instead ofserver. It accepts instance of on of the inheritors of theServerGroupclass.- MultiServerApplicationWithScaling
Extends
MultiServerApplicationwith the ability to scale the application by increasing (scaling out) or decreasing (scaling in) the number of nodes with the application after it is installed. The differences fromMultiServerApplicationare:the
serversproperty accepts only instances ofServerReplicationGrouprather than anyServerGroupthe additional optional
scaleFactorproperty accepts the number by which the app is scaled at once; it defaults to 1the
scaleOutandscaleInpublic methods are added
Application developers may as well define their own classes using the same approach and combining base classes behaviour with the custom code to satisfy the needs of their applications.