This document describes how a developer of murano application can update existing packages to make them synchronized with all implemented features and requirements.
Applications created for murano v0.5, unfortunately, are not supported in Murano stable/juno. This document provides the application code changes required for compatibility with the stable/juno murano version.
In stable/juno the name of section containing class methods is renamed to Methods, as the latter is more OOP and doesn’t cause confusion with Mistral. So, you need to change it in app.name/Classes in all classes describing workflow of your app.
For example:
Workflow:
deploy:
Body:
- $._environment.reporter.report($this, 'Creating VM')
Should be changed to:
Methods:
deploy:
Body:
- $._environment.reporter.report($this, 'Creating VM')
The Instance class was too generic and contained some dirty workarounds to differently handle Windows and Linux images, to bootstrap an instance in a number of ways, etc. To solve these problems more classes were added to the Instance inheritance hierarchy.
Now, base Instance class is abstract and agnostic of the desired OS and agent type. It is inherited by two classes: LinuxInstance and WindowsInstance.
LinuxInstance is inherited by two other classes, having different software config method:
You need to specify the instance type which is required by your app. It specifies a field in UI, where user can select an image matched to the instance type. This change must be added to UI form definition in app.name/UI/ui.yaml.
For example, if you are going to install your application on Ubuntu, you need to change:
Application:
?:
instance:
?:
type: io.murano.resources.Instance
to:
Application:
?:
instance:
?:
type: io.murano.resources.LinuxMuranoInstance
In Kilo, there are no breaking changes that affect backward compatibility. But there are two new features which you can use since Kilo.
Now you can create plug-ins for MuranoPL. A plug-in (extension) is an independent Python package implementing functionality which you want to add to the workflow of your application.
For a demo application demonstrating the usage of plug-ins, see the murano/contrib/plugins/murano_exampleplugin folder.
The application consist of the following components:
- An ImageValidatorMixin class that inherits the generic instance class (io.murano.resources.Instance) and adds a method capable of validating the instance image for having an appropriate murano metadata type. This class may be used as a mixin when added to inheritance hierarchy of concrete instance classes.
- A concrete class called DemoInstance that inherits from io.murano.resources.LinuxMuranoInstance and ImageValidatorMixin to add the image validation logic to a standard, murano-enabled and Linux-based instance.
- An application that deploys a single VM using the DemoInstance class if the tag on the user-supplied image matches the user-supplied constant.
The ImageValidatorMixin demonstrates the instantiation of plug-in provided class and its usage, as well as handling of exception which may be thrown if the plug-in is not installed in the environment.
The core library has a new system class for mistral client that allows to call Mistral APIs from the murano application model.
The system class allows you to:
- Upload a mistral workflow to mistral.
- Trigger the mistral workflow that is already deployed, wait for completion and return the execution output.
To use this feature, add some mistral workflow to Resources folder of your package. For example, create file TestEcho_MistralWorkflow.yaml:
version: '2.0' test_echo: type: direct input: - input_1 output: out_1: <% $.task1_output_1 %> out_2: <% $.task2_output_2 %> out_3: <% $.input_1 %> tasks: my_echo_test: action: std.echo output='just a string' publish: task1_output_1: 'task1_output_1_value' task1_output_2: 'task1_output_2_value' on-success: - my_echo_test_2 my_echo_test_2: action: std.echo output='just a string' publish: task2_output_1: 'task2_output_1_value' task2_output_2: 'task2_output_2_value'
And provide workflow to use the mistral client:
Namespaces: =: io.murano.apps.test std: io.murano sys: io.murano.system Name: MistralShowcaseApp Extends: std:Application Properties: name: Contract: $.string().notNull() mistralClient: Contract: $.class(sys:MistralClient) Usage: Runtime Methods: initialize: Body: - $this.mistralClient: new(sys:MistralClient) deploy: Body: - $resources: new('io.murano.system.Resources') - $workflow: $resources.string('TestEcho_MistralWorkflow.yaml') - $.mistralClient.upload(definition => $workflow) - $output: $.mistralClient.run(name => 'test_echo', inputs => dict(input_1 => input_1_value)) - $this.find(std:Environment).reporter.report($this, $output.get('out_3'))
In Liberty a number of useful features that can be used by developers creating their murano applications were implemented. This document describes these features and steps required to include them to new apps.
Now murano packages have a new optional attribute in their manifest called Version - a standard SemVer format version string. All MuranoPL classes have the version of the package they contained in. To specify the version of your package, add a new section to the manifest file:
Version: 0.1.0
If no version specified, the package version will be equal to 0.0.0.
There are cases when packages may require other packages for their work. Now you need to list such packages in the Require section of the manifest file:
Require: package1_FQN: version_spec_1 ... packageN_FQN: version_spec_N
version_spec here denotes the allowed version range. It can be either in semantic_version specification pip-like format or as partial version string. If you do not want to specify the package version, leave this value empty:
Require: package1_FQN: '>=0.0.3' package2_FQN:
In this case, the last dependency 0.x.y is used.
Note
All packages depend on the io.murano package (core library). If you do not specify this requirement in the list (or the list is empty or even there is no Require key in package manifest), then dependency io.murano: 0 will be automatically added.
Now you can specify the version of objects in UI definition when your application requires specific version of some class. To do this, add new key classVersion to section ? describing object:
?: type: io.test.apps.TestApp classVersion: 0.0.1
classVersion of all classes included to package equals Version of this package.
In Liberty, murano was updated to use yaql 1.0.0. The new version of YAQL allows you to use a number of new functions and features that help to increase the speed of developing new applications.
Note
Usage of these features makes your applications incompatible with older versions of murano.
Also, in Liberty you can change Format in the manifest of package from 1.0 to 1.1 or 1.2.
- 1.0 - supported by all versions of murano.
- 1.1 - supported by Liberty+. Specify it, if you want to use features from yaql 0.2 and yaql 1.0.0 at the same time in your application.
- 1.2 - supported by Liberty+. A number of features from yaql 0.2 do not work with this format (see the list below). We recommend you to use it for new applications where compatibility with Kilo is not required.
- Several functions now cannot be called as MuranoObject methods: id(), cast(), super(), psuper(), type().
- Now you do not have the ability to compare non-comparable types. For example “string != false”
- Dicts are not iterable now, so you cannot do this: If: $key in $dict. Use $key in $dict.keys() or $v in $dict.values()
- Tuples are not available. => always means keyword argument.
Previously, you always had to create execution plans even when some short scripts had to be executed on a VM. This process included creating a template file, creating a script, and describing the sending of the execution plan to the murano agent.
Now you can use a new class io.murano.configuration.Linux from murano core-library. This allows sending short commands to the VM and putting files from the Resources folder of packages to some path on the VM without the need of creating execution plans.
To use this feature you need to:
Declare a namespace (for convenience)
Namespaces:
conf: io.murano.configuration
...
Create object of io.murano.configuration.Linux class in workflow of your application:
$linux: new(conf:Linux)
Run one of the two feature methods: runCommand or putFile:
# first agrument is agent of instance, second - your command
$linux.runCommand($.instance.agent, 'service apache2 restart')
or:
# getting content of file from 'Resources' folder
- $resources: new(sys:Resources)
- $fileContent: $resources.string('your_file.name')
# put this content to some directory on VM
- $linux.putFile($.instance.agent, $fileContent, '/tmp/your_file.name')
Note
At the moment, you can use this feature only if your app requires an instance of LinuxMuranoInstance type.
Since Liberty, you can provide users with the ability to choose where to join their VM: to a new network created during the deployment, or to an already existing network. Dynamic UI now has a new type of field - NetworkChoiseField. This field provides a selection of networks and their subnetworks as a dropdown populated with those which are available to the current project (tenant).
To use this feature, you should make the following updates in the Dynamic UI of an application:
Add network field:
fields:
- name: network
type: network
label: Network
description: Select a network to join. 'Auto' corresponds to a default environment's network.
required: false
murano_networks: translate
To see the full list of the network field arguments, refer to the UI forms specification.
Add template:
Templates:
customJoinNet:
- ?:
type: io.murano.resources.ExistingNeutronNetwork
internalNetworkName: $.instanceConfiguration.network[0]
internalSubnetworkName: $.instanceConfiguration.network[1]
Add declaration of networks instance property:
Application:
?:
type: com.example.exampleApp
instance:
?:
type: io.murano.resources.LinuxMuranoInstance
networks:
useEnvironmentNetwork: $.instanceConfiguration.network[0]=null
useFlatNetwork: false
customNetworks: switch($.instanceConfiguration.network[0], $=null=>list(), $!=null=>$customJoinNet)
For more details about this feature, see use-cases
Note
To use this feature, the version of UI definition must be 2.1+
Previously, each class of an application had a name property. It had no built-in predefined meaning for MuranoPL classes and mostly used for dynamic UI purposes.
Now you can create your applications without this property in classes and without a corresponding field in UI definitions. The field for app name will be automatically generated on the last management form before start of deployment. Bonus of deleting this - to remove unused property from muranopl class that is needed for dashboard only.
So, to update existing application developer should make 3 steps:
In Newton a number of useful features that can be used by developers creating their murano applications were implemented. Also some changes are not backward compatible. This document describes these features, how they may be included into the new apps and what benefits the apps may gain.
Previously, for declaring action in MuranoPL application, following syntax was used:
methodName: Usage: Action
This syntax is deprecated now for packages with FormatVersion starting from 1.4, and you should use the Scope attribute:
methodName: Scope: Public
For more information about actions in MuranoPL, see Murano actions.
Now you can declare static method as action with Scope and Usage attributes
methodName: Scope: Public Usage: Static
For more information about static methods in MuranoPL, see Static methods and properties.
New contract function template was introduced. template works similar to the class in regards to the data validation but does not instantiate objects. The template is just a dictionary with object model representation of the object.
It is useful when you do not necessarily need to pass the actual object as a property or as a method argument and use it right away, but rather to create new objects of this type in runtime from the given template. It is especially beneficial for resources replication or situations when object creation depends on some conditions.
Objects that are assigned to the property or argument with template contract will be automatically converted to their object model representation.
Starting from Newton release cloud resource classes (instances, networks, volumes) can be explicitly put into OpenStack regions other than environment default. Thus it becomes possible to have applications that make use of more than one region including stretching/bursting to other regions.
Each resource class has got new regionName property which controls its placement. If no value is provided, default region for environment is used. Applications wanting to take advantage of multi-region support should access security manager and Heat stacks from regions of their resources rather than from the environment.
Regions need to be configured before they can be used. Please refer to documentation on how to do this: Multi-region application.
io.murano.Environment class contains regions property with list of io.murano.CloudRegion objects. Heat stack, networks and agent listener are now owned by io.murano.CloudRegion instances rather than by Environment.
You can not get io.murano.resources.Network objects from Enviromnent::defaultNetworks now. This property only contains templates for io.murano.CloudRegion default networks.
The proper way to retrieve io.murano.resources.Network object is now the following:
$region: $instance.getRegion() $networks: $region.defaultNetworks
string() contract no longer converts to string anything but scalar values.
New approach to resource deallocation was introduced.
Previously murano used to load Objects and ObjectsCopy sections of the JSON object model independently which cause for objects that were not deleted between deployments to instantiate twice. If deleted object were to cause any changes to such alive objects they were made to the objects loaded from ObjectsCopy and immidiately discarded before the deployment. Now this behaviour is changed and there is no more duplicates of the same object.
Applications can also make use of the new features. Now it is possible to perform on-demand destruction of the unreferenced MuranoPL objects during the deployment from the application code. The io.murano.system.GC.GarbageCollector.collect() static method may be used for that.
Also objects obtained ability to set up destruction dependencies to the other objects. Destruction dependencies allow to define the preferable order of objects destruction and let objects be aware of other objects destruction, react to this event, including the ability to prevent other objects from being destroyed.
Please refer to the documentation on how to use the garbage collector: garbage_collection.