Creating your VNF Charm

From OSM Public Wiki
Revision as of 09:10, 5 September 2018 by Candelpreste (talk | contribs)
Jump to: navigation, search

Introduction to Juju and charms in OSM

A charm is a collection of scripts and metadata that encapsulate the distilled DevOps knowledge of experts in a particular product. Charms make it easy to reliably and repeatedly deploy applications, then scale them as required with minimal effort.

In the context of OSM, Juju, through the use of charms, is responsible for VNF configuration and life-cycle management, excluding resource operations (instantiation, termination). This is called "manual provider" mode in Juju.

OSM supports a limited version of charms that we call "proxy charms", this charms are installed into an LXD container, and is only responsible for day-1 and day-2 configuration, executed remotely (typically via ssh).

Configurations are mapped to Juju Actions which manage configuration within the VNF. In the case of the proxy charm, the configuration might be done via SSH, via RESTful API, etc.

Here is a simple diagram showing how a proxy charm fits into the OSM workflow:

Proxy charm Workflow.png

-A VNF package is instantiated via the LCM 
-The LCM requests a virtual machine from the RO
-The RO instantiates a VM with your VNF image 
-The LCM instructs N2VC, using the VCA, to deploy a VNF proxy charm, and tells it how to access your VM (hostname, user name, and password)

Setup

We recommend that you are running Ubuntu 16.04 or newer, or install snapd on the Linux distribution of your choice.

Install the charm snap, which provides the charm command and libraries necessary to compile your charm:

snap install charm

Setup your workspace for writing layers and building charms:

mkdir -p ~/charms/layers
export JUJU_REPOSITORY=~/charms
export LAYER_PATH=$JUJU_REPOSITORY/layers
cd $LAYER_PATH

Creating a charm

Charms must be created in the layers folder ($LAYER_PATH).

Layers

Layers are individual components that, when combined, result in a finished product, they have individual components that, when combined, result in a finished product.

The Base layer contains the core code needed for other layers to function.

Some of the most used layers in the context of OSM are:

  • layer:basic -> imports the reactive framework, contains the core code needed for other layers to function.
  • layer:vnfproxy -> imports the required functions to run actions in the VNF via SSH. This layer has been designed to aid in the development of proxy charms.
  • layer:metrics -> imports the required functions to get metrics from the VNF.
  • layer:restapi -> imports the required functions to run actions in the VNF via REST API.
  • layer:netconf -> imports the required functions to run actions in the VNF via Netconf primitives.

The diagram below describes an example of some of the layers contained in a charm, the completed charm is available in the juju-charms repository : LayersCharm.PNG

The command "charm create" will create the layer for your proxy charm:

$ cd $JUJU_REPOSITORY/layers
$ charm create 'charmName'
$ cd 'charmName'

This will create a charm layer ready for customization:

$JUJU_REPOSITORY/layers 
└── 'charmName' 
	├── config.yaml 
	├── icon.svg 
	├── layer.yaml 
	├── metadata.yaml 
	├── reactive 
		└── 'charmName'.py 
	├── README.ex 
	└── tests 
		  └── 00-setup
		  └── 10-deploy

When you create the charm, you will have to modify different files:

  • layer.yaml: specify which layers should be "imported" as part of the charm.
  • metadata.yaml: describes what your charm is and sets certain properties used by Juju.
     Typically you should specify at least:
        name: the name of the charm
        maintainer: contact info for the charm
        provides: what interfaces are provided by the charm
        requires: what interfaces are used by the charm
        peers: what peer relations exist
        series: which base image to use for the container running the charm

  • metrics.yaml: if the VNF wants to expose some metrics, a new file metrics.yaml has to be created to specify which metrics should be collected.
$JUJU_REPOSITORY/layers 
└── 'charmName' 
	├── metrics.yaml


  • actions.yaml: let’s create `actions.yaml` in the root of the simple charm. Actions are functions that can be called automatically when a VNF is initialized (day-1 configuration) or on-demand by the operator (day2 configuration). In OSM terminology, we know these as config primitives.
$JUJU_REPOSITORY/layers 
└── 'charmName' 
	├── actions.yaml

For each action, we need to create a script to invoke the reactive framework. This is a boilerplate script that will be used for every action.

$JUJU_REPOSITORY/layers 
└── 'charmName' 
	├── actions 
		└── action1
		└── action2
		       .
		       .
		       .
	├── actions.yaml		

  • reactive/'charmName.py'  : this is where all reactive states are handled. The reactive framework, coupled with the script in the actions/ directory, maps the SO's invocation of the action to the block of code with the matching @when decorator.

Building

Be sure that you are in the charm folder (e.g.: $LAYER_PATH/'charmName') and you have Internet connectivity before building the charm.

$ charm build

This combines all layers that you included, and those that they include, into a charm called 'charmName', located in the ~/charms/builds directory.

Updating VNF Descriptor and VNF Package

In your Virtual Network Function Descriptor (VNFD), you specify the name of the charm as demonstrated below, add 'initial-config-primitive' and 'config-primitive' parameters if needed:

vnfd:vnfd-catalog:
    vnfd:vnfd:
     -  vnfd:id: idvnfExample
        vnfd:name: vnfExample
        vnfd:vnf-configuration:
            vnfd:juju:
                vnfd:charm: 'charmName'
            vnfd:initial-config-primitive:
                         .
                         .
                         .
            vnfd:config-primitive:
                         .
                         .
                         .

Then the compiled charm (from the builds directory) has to be packaged with the descriptor package under the charm directory. So the VNF with the charm would be:

vnfExample
├── charms
│   └── 'charmName'
├── checksums.txt
├── icons
├── images
├── vnfExample.yaml
├── README
└── scripts


Example VNF Charms

This section is intended to be an index to VNF charms written by members of the OSM community.


Simple Charm

You can find all the material for execute Simple-charm:


Simple Charm provides different capacities through three layers:

SimplecharmLayers.png

  • The Base layer contains the core code needed for other layers to function.
  • Vnfproxy is a runtime layer providing common functionality to interoperate with a VNF.
  • Simple is the charm layer containing code to manage your vnf.

Edit layer.yaml to include the vnfproxy layer:

includes: ['layer:basic', 'layer:vnfproxy']
options:
    basic:
        use_venv: false

Edit metadata.yaml with the name and description of your charm:

name:simple
summary:A simple VNF proxy charm
maintainer:Name<user@domain.tld>
subordinate:false
series:['xenial']

Let´s create 'actions.yaml' in the root of the simple charm:

touch:
  description: "Touch a file on the VNF."
  params:
    filename:
        description: "The name of the file to touch."
        type: string
        default: ""
  required:
    - filename

$ mkdir actions

Create `actions/touch` with the following content:

#!/usr/bin/env python3
import sys
sys.path.append('lib')
from charms.reactive import main, set_flag
from charmhelpers.core.hookenv import action_fail, action_name

set_flag('actions.{}'.format(action_name()))

try:
   main()
except Exception as e:
   action_fail(repr(e))

When you’re done, mark the script executable:

$ chmod +x actions/touch

Note: The same content has to be used for every action in the charm layer. It is only a boilerplate script to invoke the reactiveframework


Edit 'reactive/simple.py', this is where all reactives states are handled

from charmhelpers.core.hookenv import ( 
     action_get, 
     action_fail, 
     action_set, 
     status_set, 
) 
from charms.reactive import ( 
     clear_flag, 
     set_flag, 
     when, 
     when_not,
) 
import charms.sshproxy
# Set the charm’s state to active so the LCM knows
# it’s ready to work.
@when_not('simple.installed') 
def install_simple_proxy_charm():     
     set_flag('simple.installed') 
     status_set('active', 'Ready!')
# Define what to do when the `touch` primitive is invoked. 
@when('actions.touch')
def touch(): 
     err = ''
     try: 
          filename = action_get('filename') 
          cmd = ['touch {}'.format(filename)]
          result, err = charms.sshproxy._run(cmd) 
     except: 
         action_fail('command failed:' + err) 
     else: 
         action_set({'output': result})
      finally: 
          clear_flag('actions.touch')

We’re ready to compile the charm with our new action:

$ charm build


Finally, you have to update VNF Descriptor and VNF Package.

vnfd:vnfd-catalog:
    vnfd:
    -   id: hackfest3charmed-vnf
        name: hackfest3charmed-vnf
        vnf-configuration:
            juju:
                charm: simple
            initial-config-primitive:
            -   seq: '1'
                name: config
                parameter:
                -   name: ssh-hostname
                    value: <rw_mgmt_ip>
                -   name: ssh-username
                    value: ubuntu
                -   name: ssh-password
                    value: osm4u
            -   seq: '2'
                name: touch
                parameter:
                -   name: filename
                    value: '/home/ubuntu/first-touch'
            config-primitive:
            -   name: touch
                parameter:
                -   name: filename
                    data-type: STRING
                    default-value: '/home/ubuntu/touched'


Ansible

Under the scope of a H2020 project, 5GinFIRE has developed a charm that enables the configuration of a VNF, instantiated through OSM, using an Ansible playbook. The charm builds off of the base vnfproxy and ansible-base layers, and provides a template ready for customization that supports the execution of an Ansible playbook within the Juju framework used by OSM.

You can find here all files about ansible-charm.


Step by step instructions to use the base charm layer:

-Include playbook.yaml under the playbook folder of the base charm layer:

$JUJU_REPOSITORY/layers 
└── ansible-charm 
	├── playbook 
		└── playbook.yaml

-The base charm layer already implements a Juju action, ansible-playbook, which runs the playbook. You can define additional actions, if needed by your VNF.

$JUJU_REPOSITORY/layers 
└── ansible-charm 
	├── actions 
		└── ansible-playbook
		└── action1
		       .
		       .
		       .
	├── actions.yaml		

-We are ready to build the charm:

$ charm build

-Update the VNF descriptor (VNFD) to use the charm: a) specify the name of the Juju charm in in the VNF configuration; b) Include the action “ansible-playbook” with no arguments as a service primitive and as an initial configuration primitive.

-Include the compiled charm in the VNF package.