IaC and deployment

EVA ICS supports infrastructure-as-code paradigm, providing a way to deploy items and their params from deployment files in YAML format.

Live deployment examples can be found in EVA ICS demos:

Deployment is possible only on SFA servers.

Note

To allow deployment, SFA must have child controllers connected in management mode (connected controllers master keys should be set). Also, cloud manager feature should be enabled in SFA configuration file.

Deployment configuration file

Deployment configurations are stored in YAML files, which have the following format:

Controllers

Section controller contains:

Common variables setup

Here’s an example to set two cvar values:

controller:
    uc/controller1:
        cvar:
            var1: value1
            var2: value2

Driver setup

Here is an example, which:

  • Loads two PHI vrtrelay modules into Universal Controller uc/controller1 as vr1 and vr2. The second module has configuration option state_full=true.
  • Creates vr1.opener driver using vr1 PHI module and multistep LPI.
controller:
    uc/controller1:
        phi:
            vr1:
                module: vrtrelay
                # src: path/to/module
            vr2:
                module: vrtrelay
                config:
                    state_full: true
        driver:
            vr1.opener:
                module: multistep
                config:
                    bose: true

“src” field tells deployment function to get PHI module from file or URL and upload it to the target controller.

API keys and local users

Keys can contain any valid key properties, local user accounts MUST have both “password” and “key” properties set.

controller:
  uc/controller1:
    key:
      tkey1:
        key: verysecret1
        groups:
          - plant1/#
          - plant2/#
        sysfunc: true
        allow:
          - lock
          - cmd
      tkey2:
        key: verysecret2
        groups: "#"
        allow:
          - device
    user:
      test1:
        password: "iu4i32j432s"
        key: tkey1
      test2:
        password: "u89132u2183"
        key:
          - tkey1
          - tkey2

Uploading files

Local files can be uploaded into remote controller runtime directory:

controller:
    uc/controller1:
        upload-runtime:
            - localfile:remotefile
            - localfile2:path/to/remotefile2

File list: local/remote files, separated with “:”. If remote directory doesn’t exist, it will be created automatically.

It’s possible to use masks for local files, e.g. in the example below, contents of “bundle” directory will be uploaded to remote node “runtime/upload”, directory structure will be duplicated as-is.

controller:
    uc/controller1:
        upload-runtime:
            - bundle/*:upload/

Note

To upload directory contents recursively, set file mask to **

It’s possible to keep content in the deployment file as well. In this case, use “file_put” API function to create the remote file:

controller:
  uc/controller1:
    before-deploy:
      - api: file_put
        i: upload/test.yml
        m: |
          test: true
          test2: false
          tags:
            - valid: true
              x: 2
            - valid: true
              x: 3

File paths:

  • if the file path starts with http: or https:, it’s processed as URI
  • if the file path starts with “/”, it’s considered as absolute
  • if the file path starts with “./” it’s relative to the current directory
  • otherwise, the file path is relative to the directory where the deployment file is located

Before/After deploy

Remote calls

Note

EVA ICS architecture does not allow Cloud Manager to execute API calls on remote SFAs. Use the remote node UC or LM instance to act as the agent for that.

Controller API calls may be automatically executed after deployment is complete:

controller:
    lm/lm1:
        before-deploy:
            - api: reset
              i: timers/timer1
        after-deploy:
            - { api: clear, i: timers/timer1 }
            - { api: reload_controller, i: uc/uc1 }
            - { api: reload_controller, i: uc/uc2 }
            - api: custom_fn
              _pass: true
              param1: 123
              param2: "x"

API calls are always executed in the specified order, one-by-one, api: field contains API function to execute, others specify function parameters. The special parameter _pass in the last call allows deployment to ignore failed API call (warning will be printed).

Custom API call timeout can be defined with special parameter _timeout.

Note

It is usually recommended to call reload_controller for Logic Manager to let it instantly load newly deployed items from connected UCs for EVA ICS prior to 3.3.2.

Starting from 3.3.2, if units or sensors were deployed, all LM PLCs, listed in “controller” section, are reloaded automatically. If a LM PLC has no deployment configuration, it should be listed as an empty dict:

controller:
  lm/lm1: {}
Local calls

Sometimes it’s useful to call local SFA function deployment process. This can be done with directive “cm-api” which has the same format as “api” for remote calls:

controller:
    lm/lm1:
        before-deploy:
            - api: reset
              i: timers/timer1
            # just for a test
            - cm-api: reload_controller
              i: lm/lm1

The local calls can be also merged into “local” controller section. The section can contain any before/after deploy commands, which are executed on local SFA:

controller:
  local:
    after-deploy:
      - install-pkg: test.evapkg
        o: { x: 2 }
        w: 5

The “local” section is always executed after all other controllers’ sections.

Additionally, local SFA resources, allowed to be deployed:

  • API keys
  • user accounts
Macro functions

The following functions execute API macros:

reboot_controller

Executes “shutdown_core” method for the selected controller, after - waits until the controller is back online. The function can be used in for both “api” and “cm-api”.

Parameters:

  • wait wait until the controller should be back online, sec (default: 30)

Installing packages

A package can be installed during any deployment stage.

Syntax:

controller:
  uc/uc1:
    after-deploy:
      - install-pkg: /path/to/package.evapkg
        o: { x: 2 }
        w: 5

Additional deploy functions

sleep

Delays execution of next before/after deploy commands. E.g. let’s reload remote UC and wait 5 seconds until its core is restarted:

controller:
  uc/uc1:
    after-deploy:
      - api: shutdown_core
      - function: sleep
        args: [ 5 ]
system

Executes (local) system command:

controller:
  uc/uc1:
    after-deploy:
      - function: system
        args: [ 'ls' ]

Loops, conditions and variables

Deployment files are pre-processed as Jinja2 templates and may contain any valid Jinja2 tags and variables.

The pre-set variable “time_ns” contains current UNIX timestamp (in nanoseconds), which can be used e.g. as “no-cache” value for bypassing cache of proxied external resources.

It’s also possible to import any external Python module with “import_module” function:

{%- set io=import_module('io') %}
{%- set units=io.open('units.list').readlines() %}
unit:
  {%- for u in units %}
  {{ u.strip() }}:
    controller: uc/uc1
  {%- endfor %}

Items

Control and monitoring items can be deployed with unit, sensor and lvar sections. All sections are similar, the format is:

unit:
    group1/u1:
        controller: uc/uc1
        action_enabled: true
        update_interval: 0
        status: 0 # initial status, optional
        driver:
            id: vr1.default
            config:
                port: 1

All child fields specify item properties, except:

  • Field controller specifies controller, where item should be deployed
  • For units and sensors, driver field may be used to assign driver to the item.

Note

It is not necessary to list the target controller in “controller” section, unless it needs to be additionally configured (e.g. load drivers/PHIs)

If action_exec or update_exec values are started with ^ symbol, it tells deployment tool to upload local file on the controller.

Setting “status” field to “update” asks the controller to trigger item update after deployment:

unit:
    group1/u1:
        controller: uc/uc1
        # ....
        status: update

The second example shows how to deploy a sensor and logical variable:

sensor:
    group1/s1:
        controller: uc/uc1
        driver:
            id: somedriver.default
            config:
                port: 1
        value: 77 # initial value, optional, initial status for sensor
                  # is not required (automatically set to 1 - enabled)

lvar:
    group1/timer1:
        controller: lm/lm1
        expires: 30
        status: 0 # initial status, optional
        value: 77 # initial value, optional

Macros

Logic macros are deployed in lmacro section:

lmacro:
    group1/macro1:
        controller: lm/lm1
        action_exec: ^macro1.py

All child fields specify item properties, except:

  • Field controller specifies Logic Manager, where macro should be deployed

If field action_exec value is started with ^ symbol, it tells deployment tool to upload local file on the controller.

Note

To make deployment process more easy, it is recommended to start it in directory, where macro files are located.

Logical rules

Decision-making matrix can be configured with dmatrix_rule section.

Rule example:

dmatrix-rule:
  5ef9b8fd-d527-44ce-ae89-9629afd40d76:
      controller: lm/farm-scada
      description: light normal
      enabled: true
      oid: "sensor:#/ldr.value"
      condition: x = 1
      break_after_exec: true
      macro: stop_lamp
      macro_kwargs:
        lamp_id: 1

All child fields specify item properties, except:

  • Field controller specifies Logic Manager, where rule should be configured

Rule UUID should be pre-generated with any UUID generator, e.g. with uuidgen Linux console command.

Scheduled jobs

Jobs can be deployed the similar way as rules:

job:
  e407f61c-a251-455b-92bc-9eee9adcb93b:
    controller: lm/lab-ws2
    description: "scheduled job 1"
    enabled: true
    macro: do_scheduled_task
    macro_args: [ 'task1' ]
    every: "wednesday at 12:00"

Cycles

lcycle:
  test:
    controller: lm/lab-ws2
    description: "scheduled job 1"
    interval: 1
    macro: do_cycle1
    macro_args: [ 'task1' ]

How to deploy configuration

Currently there is no API functions for deploy EVA ICS configuration. The item configuration can be deployed either via CLI or during installation.

Deployment via CLI

Deploying

Deployment configuration can be applied using eva sfa cloud deploy command. When deployed with CLI, deployment file may contain variables.

Example:

unit:
    light/room1:
        controller: uc/{{ srv }}

Here is srv variable defined. To set its value, e.g. to “uc1”, use -c srv=uc1 command line argument. If multiple variable values are set, they should be comma separated, e.g.: -c srv1=uc1,srv2=uc2 etc.

There’s also command line argument -u which tells CLI to try undeploying target configuration before doing deployment of it. Undeployment process ignores missing items and deletes only existing.

Undeploying

Deployment configuration can be removed with eva sfa cloud undeploy command. Custom variable values can be set in the same way as during deployment.

Deployment during installation

Configuration also can be deployed with easy-setup during SFA installation. Use –deploy FILE command line argument to specify path to the deployment file.

Complex deployments

Bare-metal

Sometimes deployment is more complex than just creating items. In this case deployment scripts are used to prepare environment, call eva sfa cloud deploy command and finish deployment.

Containers

There is no problems when the regular bare-metal or virtual machine installation is performed, but if EVA ICS is being installed into Docker machine or Kubernetes cluster, there is a special environment variable after_install, which tells EVA ICS Docker image to apply the deployment script after the initial setup process is finished. Here is an example part of a docker-compose file:

eva-scada:
    environment:
        - DEPLOY=/path/or/url/to/yaml

Devices

Starting from EVA ICS 3.3.2, device template format is equal to IaC files.

For cvar deployment, a proper “controller” property should be present in the device template. In “unit” and “sensor” sections, “controller” property is not required and ignored if present.