Overview

Murano use packages to manage the launch and configuration of instances. This page will help you create your own package. Currently, Murano is using the Newton release. You can find official documentation for this release here.

You can see some packages as example here.

Introduction

Murano package must be in .zip form. The folder tree must look as follows :

Murano tree

The main parts of the package are the manifest file, the UI file, the classes file(s) and the ressources.

Manifest file

The manifest file will look like this:

Murano manifest

UI file

UI files start with a version number

There are 2 main parts needed.

Template & Application

Simple example Jupyter

Murano package ui

The Templates customJoinNet part will not change. It is needed on Genostack to get an IP in the instances

For basic packages, you only need the network.

Templates:
  customJoinNet:
    - ?:
        type: io.murano.resources.ExistingNeutronNetwork
      internalNetworkName: $.instanceConfiguration.network[0]
      internalSubnetworkName: $.instanceConfiguration.network[1]  

The Application part will link the UI to the class files. It needs to directly call a class name (defined in the manifest part) You will need to set here the variable needed by the class file

Application:
  ?:
    type: myPackage.Jupyter                                             # Class Name (defined in manifest).
  password: $.instanceConfiguration.password                            # Refers to form instanceConfiguration, password field.
  instance:                                                             # instance is a variable used in the class file. It need several properties.  
    ?:                                  
      type: io.murano.resources.LinuxMuranoInstance                     # instance is a murano ressource.
    flavor: $.instanceConfiguration.flavor                              # flavor variable refers to form instanceConfiguration, flavor field.
    image: $.instanceConfiguration.osImage                              # Same for the following.
    keyname: $.instanceConfiguration.keyPair
    name: $.instanceConfiguration.name
    networks:                                                           # That part will not change. It is need for network initialisation.
      useEnvironmentNetwork: $.instanceConfiguration.network[0]=null
      useFlatNetwork: false
      customNetworks: switch($.instanceConfiguration.network[0], $=null=>list(), $!=null=>$customJoinNet)
More complex example (cluster)

If you want to create a cluster with one host, and several nodes, you will need to make a class for each type, and one additional class for orchestration. (So 3 classes) The orchestration class will go in the Application part, and the other two will go in the template part. Same as before, you will need to link each class to the UI.

Template part (Including Host and Node classes)

Templates:
  customJoinNet:
    - ?:
        type: io.murano.resources.ExistingNeutronNetwork
      internalNetworkName: $.hostConfiguration.network[0]
      internalSubnetworkName: $.hostConfiguration.network[1]
  host:
    ?:
      type: myPackage.SGEHost           #Link to the host class
    host:
      ?:
        type: io.murano.resources.LinuxMuranoInstance
      flavor: m1.small
      image: $.hostConfiguration.osImage
      keyname: $.hostConfiguration.keyPair
      name: $.hostConfiguration.name
      networks:
        useEnvironmentNetwork: $.hostConfiguration.network[0]=null
        useFlatNetwork: false
        customNetworks: switch($.hostConfiguration.network[0], $=null=>list(), $!=null=>$customJoinNet)
  mednode:
    ?:
      type: myPackage.SGEMedNode        #Link to the node class
    mednode:
      ?:
        type: io.murano.resources.LinuxMuranoInstance
      flavor: m1.medium
      image: $.hostConfiguration.osImage                                       #Use same option as host.
      keyname: $.hostConfiguration.keyPair                                     #Use same option as host.
      name: generateHostname($.appConfiguration.unitNamingPattern, $index)     #Generate hostname based on a pattern  
      networks:
        useEnvironmentNetwork: $.hostConfiguration.network[0]=null
        useFlatNetwork: false
        customNetworks: switch($.hostConfiguration.network[0], $=null=>list(), $!=null=>$customJoinNet)

Application part

Application:
  ?:
    type: myPackage.SGECluster          #Link to orchestration class 
  host: $host                           # Set the variable host as the result of the host template 
  mednode: repeat($mednode, $.appConfiguration.maxnodeCount) # Set the variable node as the result of multiple node templates 

Forms

The “Forms” part will create the user UI.

Murano form

Each “form” in the Forms part will create a tab for the user to fill at application launch. You will have an additional windows to give the application a name.

Murano ui example

You can set as much fields as you wish. You will need to set at least the flavor, image, keyname and name variables (either by letting the user setting it, or setting it yourself). Make sure to set all variables your application need.

 - instanceConfiguration:         #Name you will need to call to access the variables (ex : instanceConfiguration.title) 
      fields:                           
        - name: title             # Non interactive part, only to inform the user     
          type: string
          required: false
          hidden: true            # The field will not be visible, only the description
          description: Specify some instance parameters on which the application would be created
        - name: name                # Name of the variable 
          type: string              # Variable type
          initial: Jupyter          # Initial value
          label: Instance name      # Label visible in the UI
          maxLength: 64             # Max length
          description: >-
              Select your instance name.
        - name: flavor
          type: flavor              # Special type : will show a dropdown list of available flavors
          label: Instance flavor
          description: >-
            Select registered in Openstack flavor. Consider that application performance
            depends on this parameter.
          required: false
        - name: osImage
          type: image               # Special type : will show a dropdown of images
          imageType: jupyter        # Will only show images with the "jupyter" metadata
          label: Instance image
          description: >-
            Select valid image for the application. Image should already be prepared and
            registered in glance.
        - name: keyPair
          type: keypair             # Special type: will show the user available keypairs
          label: Key Pair
          description: >-
            Select the Key Pair to control access to instances. You can login to
            instances using this KeyPair after the deployment of application.
          required: false
        - name: password
          type: password            # Special type : string with constraint (complexity/length)
          label: WebUI password
          description: >-
            Select the password wich will be used to connect to the web interface.
          required: true
        - name: network
          type: network             # Special type: Will show available networks
          label: Network
          description: Select a network to join. 'Auto' corresponds to a default environment's network.
          required: false
          allow_auto: false
          murano_networks: translate

Ressources

Ressources are what you will need when the instance is launched. Meaning, scripts and data. Ressources need to be set as follow in your package, in the Resources folder

Murano Class

All your scripts need to be in the Resources/scripts folder. Scripts need to be called by a .template file. Each template file must contain at least a Body and Scripts part. You can also add a Parameters part if you need to pass parameters to your script.

Example of a template file:

FormatVersion: 2.0.0
Version: 1.0.0
Name: Deploy Master

Parameters:                         # Parameters passed by the class file
  password: $password
  hostName: $hostName
  hostIP: $hostIP
  nodesNamelist: $nodesNamelist
  nodesIPlist: $nodesIPlist

Body: |
  return hostDeploy('{0} {1} {2} {3} {4}'.format(args.password, args.hostName, args.hostIP, args.nodesNamelist, args.nodesIPlist)).stdout  
    # (Launch the hostDeploy script defined below, with the parameters)
Scripts:
  hostDeploy:
    Type: Application
    Version: 1.0.0
    EntryPoint: launch_Host.sh      # The script to launch
    Files:
      - conf/slurm.conf             # Files to include
    Options:
      captureStdout: true
      captureStderr: true

You will manually call the template file in the class file. You can get the result of the script if needed.

Class file(s)

Murano Class

Namespaces

Namespaces are shortcut to call external classes

You will need to edit the “=:” part and the “Name” part so that the combination gives the Full Name written in the manifest

(Ex: myPackage.Jupyter)

Extends

You will need at least - std:Application in this part. You can import other classes here if you need.

Properties

Properties are variable that are needed to launch the application (for instance, a password, or an instance). These variable need to be set, either by the UI, or the orchestrator class.

Properties:
  password:                             # Set in the UI 
    Contract: $.string().notNull()      # Need to be a not null string
  instance:                             # Set in te UI
    Contract: $.class(res:Instance).notNull()   # Need to be an instance  

Methods

The initialize and deploy methods are automatically called. You should set up your environment in the initialize method, and setup the instance launch in the deploy method.

Methods:
  initialize:
    Body:
      - $._environment: $.find(std:Environment).require()                           # Get the murano Environment class
      - $resources: new(sys:Resources)
  deploy:
    Body:
      - If: not $.getAttr(deployed, false)                                          # Make sure it's not deployed yet
        Then:
          - $._environment.reporter.report($this, 'Creating VM')                    # Print a message
          - $securityGroupIngress:                                                  # Define a new security group
            - ToPort: 80                                                            # Open port 80 & 443 to the outside
              FromPort: 80
              IpProtocol: tcp
              External: true
            - ToPort: 443
              FromPort: 443
              IpProtocol: tcp
              External: true
            - ToPort: 65535                                                          # Open all port between instances in this application (ex:cluster)  
              IpProtocol: tcp
              External: false
          - $._environment.reporter.report($this, 'Adding security group')  
          - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) # Add the security group
          - $._environment.reporter.report($this, 'Deploying')
          - $.instance.deploy()                                                         # Deploy the instance
          - $template: $resources.yaml('DeployJupyter.template').bind(dict(password => $.password)) #Prepare DeployJupyter.template with the variable $password  
          - $._environment.reporter.report($this, 'Instance is created. Launching Jupyter')
          - $.instance.agent.call($template, $resources)                               # Launch template
          - $.setAttr(deployed, true)
          - $._address: $.instance.ipAddresses.first()                                  # Get instance IP
          - $._environment.reporter.report($this, 'Running on'+' '+str($._address))

This is a basic example : this script create a security group, launch an instance, and launch a script on it.

For a more complex example (multiple classes, deploy and pass variables between instances), please check here