IaC and deployment

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

Note

To allow deployment on remote nodes, the local node must have remotes connected in management mode (the replication service field “admin_key_id” must be set).

Deployment file

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

version: 4
content:
    - node: <NODE_NAME>
    # ......
    - node: <NODE_NAME>
    # ......

An alias “.local” can be used for the local node deployment.

AAA

User account, API keys and ACLs can be deployed as well, per node. Note that user passwords contain password hashes, not the passwords itself.

Local user/key authentication service accepts the following hash types:

  • SHA256 and SHA512 hashes (hex). Should be used in test configurations only to avoid possible rainbow table attacks if the password hash is compromised. Can be generated in command-line with:

echo -n password | sha256sum
echo -n password | sha512sum
  • PBKDF2-HMAC (100k iterations, 16-byte salt), for production use, as: $1$BASE64(SALT)$BASE64(SHA256-HASH). Can be generated with:

# with eva-shell
eva svc call eva.aaa.localauth password.hash password=mypassword algo=pbkdf2
# or
/opt/eva4/sbin/bus -s /opt/eva4/var/bus.ipc \
    rpc call eva.aaa.localauth password.hash password=mypassword algo=pbkdf2
- node: .local
  acls:
    - id: admin2
      admin: true
    - id: default2
      read:
        items:
          - "#"
      write:
        items:
          - "#"
      meta:
          name:
            - "default ACL #"
  keys:
    - id: admin2
      key: "mykey2"
      acls:
        - admin2
  users:
    - login: admin2
      password: "$1$CaqoIL8WXkDnqnwMXLeW5g==$qXQVPbRibRSomjtzKuyOePv59lx3eAQUR3yqAUS4YoE="
      acls:
        - admin2

Uploading files

Uploading single files

Local files can be uploaded to the target’s EVA_DIR/runtime directory with the following block:

- node: .local
  upload:
    - src: deploy.info
      target: data/
      # override the file permissions, specify in oct
      permissions: 0o400
    - src: xx.yml
      target: data/x.yml
      svc: eva.filemgr.main # override the file manager
    - text: |
        hello, i am here
      target: data/some-file

Note

If a file content is defined directly in the deployment (field text), the target MUST contain the full destination path, including the file name.

The upload source field accepts both local files and HTTP URLs. If an URL is specified in “src”, it is downloaded first to the local host where the deployment process is started and after pushed to the target node via pub/sub.

Note

The specified way is not recommended to deploy large (>1MB) files as they may block pub/sub replication pipes.

Uploading files in bulk

It is not allowed to upload files in bulk, however an archive can be uploaded and extracted by File manager service service on the target node/spoint.

- node: .local
  upload:
    - src: path/to/archive.tgz
      target: data/
      extract: true

The following archive types are supported: tar, tar.gz (tgz), tar.xz (txz), tar.bz2 (tbz2), zip (requires unzip on the target node/spoint).

The archive type is detected automatically, by the file extension. If the source file/URL has no extension, the archive type can be set manually:

- node: .local
  upload:
    - src: path/to/some.archive
      target: data/
      extract: tgz

The following field values are supported: tar, tgz, txz, tbz2, zip.

Note

File manager service allows archiver processes to run for the limited period of time. In case of timeout errors, increase the service timeout.

Uploading single/multiple files from HTTP URLs

HTTP/HTTPS-hosted files can be fetched by the target node into EVA_DIR/runtime directory with the following block:

- node: .local
  upload:
    - url: http://some.host/deploy.info
      target: data/
      # optional file permissions
      permissions: 0o400

In this case, only file URL is pushed to the remote node, the content is downloaded by the remote node. The parameter “extract” is supported for archives as well.

Make sure the remote node has got access to the specified HTTP resource.

Note

The specified way is the most recommended way to deploy large (>1MB) files as they do not block pub/sub replication pipes.

Uploading UI/PVT files

By default, uploading UI and PVT files is not possible. This can be manually enabled with:

cd /opt/eva4
# make sure no symlinks in runtime are present
rm -f runtime/ui runtime/pvt
# move ui and pvt into runtime and create symlinks
mv ui pvt runtime/ && ln -s runtime/ui && ln -s runtime/pvt

Items

DCS Items can be deployed in “items” section:

- node: .local
  items:
    - oid: unit:tests/door
      status: 1
      action:
        svc: eva.controller.virtual
    - oid: unit:tests/door2
      enabled: true

Units, sensors and lvars can contain initial status and value fields. The fields are ignored if items already exist and the current states are kept.

Macros

lmacro are deployed as all other items. If Python Logic Macros are used, upload the macro code to xc/py subdirectory of the target’s runtime:

- node: .local
  upload:
    - src: m1.py
      target: xc/py/m1.py
  items:
    - oid: lmacro:tests/m1
      action:
        svc: eva.controller.py

Services

It is possible to deploy new services on the target. After the node is deployed, the deployment process waits until all deployed services are started before continue.

- node: .local
  svcs:
    - id: eva.svc.locker2
      params:
        bus:
          path: var/bus.ipc
        command: svc/eva-svc-locker
        config:
          locks:
            - lock1
            - lock2
            - lock3
        workers: 1
        user: nobody

Data objects

Data objects can be deployed as the following:

version: 4
content:
  - node: .local
    data_objects:
    - name: Env
      fields:
      - name: temp
        oid: sensor:env/temp
        type: f64
      - name: hum
        oid: sensor:env/hum
        type: f64
      - name: pressure
        oid: sensor:env/pressure
        type: f64
    - name: sub1
      fields:
      - name: counter_array
        type: u64,10
    - name: test
      fields:
      - name: value2
        type: u16
      - name: value_s1
        oid: sensor:tests/s1
        type: i16
      - name: substruct
        type: sub1

Generator sources

Data generator sources can be deployed as the following:

version: 4
content:
  - node: .local
    generator_sources:
    - kind: random_float
      name: v1
      params:
        max: 5
        min: -5
      sampling: 10
      targets:
      - sensor:tests/voltage

Note

There is no generator service deployed by default. Make sure the service is already either deployed or included into the deployment payload.

Alarms

Alarms can be deployed as the following:

version: 4
content:
  - node: .local
    alarms:
    - group: test
      id: AL001
      level: 20
      # extra parameters

Extra commands

Bus calls

Node bus calls can be automatically executed before/after the deployment is complete:

- node: .local
  extra:
    deploy:
      before:
        - method: test
        - method: item.list
          params:
            i: '#'
        - method: eva.registry::server_set
          params:
            name: auto_flush
            value: false
      after:
        - method: eva.registry::server_set
          params:
            name: auto_flush
            value: true
    undeploy:
      before:
        - method: svc.purge
          params:
            svcs:
              - eva.svc.locker2
          _pass: true

API calls are always executed in the specified order, one-by-one, method: field contains EAPI method function to execute. The default target is the target node core, to specify a service call, use the format “TARGET_SVC::METHOD”.

The special parameter _pass allows to ignore errors.

Additional deploy functions

sleep

Delays execution of next before/after deploy commands. E.g. the following block makes 1-sec delay after undeployment:

- node: .local
  extra:
    undeploy:
      after:
        - function: sleep
          args: [ 1 ]
system

Executes (local) system command:

- node: .local
  extra:
    undeploy:
      after:
        - function: system
          args: [ "touch /tmp/xxx.flag" ]

Deployment via CLI

Deploying

Deployment configuration can be applied using eva cloud deploy (or eva-cloud-manager cloud deploy) command.

Variables

When deployed with CLI, deployment files can contain external variables.

Example:

- node: {{ srv }}
  items:
    - oid: sensor:{{ srv }}/env/temp

Here is srv variable defined. To set its value, e.g. to “plant1”, use -c srv=plant command line argument. If multiple variable values are going to be set, use -c argument multiple times.

The deployment variable values can be also got from the system environment, from variables which have got ECD_ prefix. For the above example, use a variable ECD_srv to set srv template variable:

ECD_srv=node1 eva cloud deploy file.yml

Timeouts

The default deployment timeout is 5 seconds. If some deployment calls require more time to be executed, consider increasing the timeout value with command-line argument -T:

eva -T 15 cloud deploy file.yml

The deployment file can be a local one or HTTP URL.

Flushing registry

If Registry database auto-flush is enabled on the target and multiple items are deployed, the deployment may take long time to complete. The registry auto-flush can be automatically switched off and back on with the following block:

- node: .local
  extra:
    deploy:
      after:
      - method: eva.registry::server_set
        params:
          name: auto_flush
          value: true
      before:
      - method: eva.registry::server_set
        params:
          name: auto_flush
          value: false
    undeploy:
      after:
      - method: eva.registry::safe_purge
      - method: eva.registry::server_set
        params:
          name: auto_flush
          value: true
      before:
      - method: eva.registry::server_set
        params:
          name: auto_flush
          value: false

The block also calls safe_purge to cleanup the registry after undeploy.

Undeploying

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

Node parameters

Node deployment parameters can be used to override the default services the deployment process is applied for:

- node: .local
  params:
    acl_svc: eva.aaa.acl
    key_svc: eva.aaa.localauth
    user_svc: eva.aaa.localauth
    filemgr_svc: eva.filemgr.main
    generator_svc: eva.generator.default
    alarm_svc: eva.alarm.default

Advanced configuration

Advanced configuration directives are processed by the deployment CLI tool on the local machine. If the target deployment (e.g. a service configuration) must contain these directives, use “^^” directive prefix instead of a single one.

Dealing with timeouts

During deployment the following timeouts may appear:

General timeout

Caused by the cloud manager CLI which waits 5 seconds as max by default for each RPC response. To increase the general timeout, use “-T” option of eva-shell or “-t” option if the cloud manger CLI is used directly:

eva -T 60 could deploy file.yml

Remote node timeout

When deploying to remote nodes, RPC calls go thru an instance of Replication service. The service has got own timeouts for RPC calls to particular nodes which can be changed with adding “timeout” field to the remote node configuration:

eva node edit remote-node-name

to modify the default timeout for all nodes, set the value of “timeout/default” field of the replication service:

eva svc edit eva.repl.default

Downloading files on remote nodes

When HTTP URL is pushed to a remote node with a download request, it is processed by an instance of File manager service on the remote. The default download timeout is 5 seconds.

To raise the timeout, set “timeout/default” field of the file manager service instance on the remote node:

eva svc edit eva.filemgr.main

Deployment automation

Starting from the version 0.2.29, EVA ICS Python SDK provides a module for deployment automation. It is recommended to use the module for all tasks where the deployment configuration is generated dynamically, is complicated or requires a lot of boilerplate code.

All deployment automation components except Deploy and Node are optional and allow development operators both using the high-level API with pre-defined elements or modify the payload either directly or using add/set methods, which accept key names as paths.

Full API reference

If using on a non-EVA ICS system, install the SDK and PyYAML:

pip3 install evaics pyyaml

Example:

from evaics.deploy import (Deploy, Node, Service, Item, User, Upload,
                           service_from_tpl, DataObject, Alarm, EAPICall,
                           Function, load_deploy)

# create a node object (if no name is provided, the local node is used)
n = Node()
# set a parameter
n.param('filemgr_svc', 'eva.filemgr.main')
# create a sensor element with an initial value of 25
sensor1 = Item('sensor:test').set('value', 25)
n.add_element(sensor1)

# add a unit with a service action mapped
n.add_element(
    Item('unit:t1').action_svc('eva.controller.plc1').action_timeout(10))

# add another unit, set service action using set method with path
n.add_element(Item('unit:t2').set('action/svc', 'eva.controller.plc2'))

# add a user
n.add_element(User('admin2').set('acls', ['admin']))
# add a file upload element
n.add_element(Upload(src='http://bma.ai/file.txt', target='plant/'))
# add a service element
n.add_element(
    Service('eva.controller.plc1',
            'eva-controller-enip').user('eva').workers(5))
# add another service element, load the service configuration from a template
n.add_element(
    service_from_tpl('eva.controller.plc1',
                     '/opt/eva4/share/svc-tpl/svc-tpl-controller-modbus.yml'))
# add a data object element
n.add_element(DataObject('sensor').field('value', 'float'))
# add an alarm element
n.add_element(Alarm('sensor', 'high', 2))
# add an EAPI call element (executed after the node deployment)
n.add_element(EAPICall('test'))
# add a function element (executed after the node deployment)
n.add_element(Function('sleep', 5))
# same function, but with set method
n.add('extra/deploy/after', dict(function='sleep', args=[5]))
# add more data from an export file
n.add_from_export('items.yml')

# create a deploy object
deploy = Deploy()

# add the node to the deploy object
deploy.add(n)

# save the deploy object to a file
deploy.save('test.yml')

# load the deploy object from a file
d = load_deploy('test.yml')

# print the deploy object
print(d)

It is also possible to deploy using a generation script directly:

python3 deploy.py | eva cloud deploy -