Skip to content

Commit

Permalink
Merge pull request #18 from VOLTTRON/develop
Browse files Browse the repository at this point in the history
Merge develop to main for release of 1.3
  • Loading branch information
laroque authored Sep 22, 2021
2 parents 36cd59e + dca04c1 commit 962fe67
Show file tree
Hide file tree
Showing 19 changed files with 217 additions and 88 deletions.
54 changes: 44 additions & 10 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ file. The search path for those is documented by ansible and includes your home
the working directory. In order to use python3 whenever possible it is useful to set the interpreter
setting to ``auto`` as shown in the included file:

.. literalinclude:: ../../examples/ansible.cfg
.. literalinclude:: ../../examples/vagrant-vms/ansible.cfg
:caption: ansible.cfg
:language: yaml
:linenos:
Expand All @@ -210,7 +210,7 @@ is available for use with recipes. For this example we start with a truely minim
which consists only of the list of our three machines (where we've made the conventional choice
that the inventory host names will match the VM names in the generated ssh configuration.

.. literalinclude:: ../../examples/recipe-inventory.minimal.yml
.. literalinclude:: ../../examples/vagrant-vms/recipe-inventory.minimal.yml
:caption: recipe-inventory.minimal.yml
:language: yaml
:linenos:
Expand All @@ -222,7 +222,7 @@ matching the inventory host name. (This location is configurable via inventory v
In this case the instance name is taken to be the inventory host name and all other configs are
left blank. That configuration looks like:

.. literalinclude:: ../../examples/web/web.yml
.. literalinclude:: ../../examples/vagrant-vms/web/web.yml
:caption: web/web.yml
:language: yaml
:linenos:
Expand All @@ -240,7 +240,7 @@ By way of a demonstration, we'll expand the minimal inventory to achieve the fol

These are all achived with an inventory that looks like:

.. literalinclude:: ../../examples/recipe-inventory.yml
.. literalinclude:: ../../examples/vagrant-vms/recipe-inventory.yml
:caption: recipe-inventory.yml
:language: yaml
:linenos:
Expand All @@ -253,7 +253,7 @@ Similarly, we can modify the platform configuration by updating the platform con
For example, we'll set collector1 to use the RabbitMQ message bus by replacing the empty dictionary
with that configuration as seen here:

.. literalinclude:: ../../examples/collector1/collector1.yml
.. literalinclude:: ../../examples/vagrant-vms/collector1/collector1.yml
:caption: collector1/collector1.yml
:language: yaml
:linenos:
Expand Down Expand Up @@ -313,6 +313,45 @@ during the platform installation step. This activates the VOLTTRON environment a
setting appropriate environment variables. If you run ``vctl``, you'll see that the platform
is running (but currently has no agents installed).

Step 5: Configure agents
~~~~~~~~~~~~~~~~~~~~~~~~

With a platform running, you can install agents using local configuration files using the ``volttron_agent``
module (which is the core of the ``configure-agents`` playbook. The command is::

ansible-playbook -i <path/to/your/inventory>.yml \
<path/to/>volttron/deployment/recipes/configure-agents.yml \
[-l hostname]

where the optional ``-l hostname`` option can be used to only make changes on one of the managed systems.
This flag is a standard feature of ansible, and is stressed here because it is assumed that
it will be common that the configuration of a single system will need to be updated to reflect changes
in the building configuration or other similar building-specific details.

This playbook ensures that the local configuration directory is synchronized to the remote system and then
installs the listed agents into the platform.

.. note::
If an agent is already installed, the system will *not* update its configuration by default. You must
set the "force" option to ``True``, which will result in the agent being reinstalled (and therefore updated).

The per-agent configuration supports the same flags as the legacy ``install-agents.py`` script upon which it is built.
The system will also install entries into the agent's configuration store, these can be done individually, or an entire
directory structure can be used, where the directory path is used to construct the configuration store entry name.
To allow for cases where the full key for one entry would be part of the name of another (and therefore would need to
be both a file and a directory on the filesystem), any path element which ends in ``.d`` will have those two characters
removed from the key in the configuration store.
The system also supports explicit renaming of individual elements, which is more verbose but allows any particular special
case to be covered.
The system is also able to remove entries from the configuration store, but you must explicitly list them by name
with a status of absent.
There is currently no support for removing all configuration store entries installed but not present in the local tree.

.. note::
Entries are added to the configuration store sequentially using the ``vctl`` tool.
If an agent has a large number of entries, this can be slow and may even result in a timeout.
Experience thus far shows that progress is retained and re-running the playbook will complete the installation process.

Extra steps
~~~~~~~~~~~

Expand Down Expand Up @@ -351,17 +390,12 @@ priorities:

* Expanded support for managing agents in the installed platforms, eventually to include:

* updating configuration and/or configuration store of an installed agent

* deploying source for an agent which is not part of the volttron repository so that custom agents can be used

* performing code updates to an installed platform

* Support for a multi-platform patterns, including cert exchange between managed platforms

* Support for backing up the configured state of a remote platform (into an archive file and/or into a set of
configuration files which could be used in a subsequent recipes deployment).


.. _recipes-configuration:

Expand Down
3 changes: 3 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This directory includes a mix of utility scripts, example ansible playbooks, and a complete set of configuration files for using vagrant to provision VMs and then use the tooling of this repo to deploy volttron onto those VMs.
The scripts and components here are meant as a starting point and/or example to demonstrate how the system can be used; they have not been developed, tested, or evaluated to a standard that would make them suitable for all uses.
Community members are encouraged to understand and modify them to meet their specific needs, and/or reach out to the core development team for assistance if needed.
86 changes: 86 additions & 0 deletions examples/process_configuration_store.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/bin/bash

doc_string="
A script which parses a configuration store file from a VOLTTRON platform into a
directory structure suitable for use in the ansible-based recipe system. For
each configuration store entry, the key is parsed as a path and the full
directory structure generated. In the event that a key is an exact subset of
another key (i.e. some path would need to be both a file and a directory), a
directory is created with '.d' appended to the name, with the file keeping the
original name. The recipes system will ignore the '.d' ending of any directory
name when parsing the paths back into keys.
Options:
-cs (--configuration-store) is the full path to the configuration store file
-o (--output-dir) is the directory within which the configuration store
output files will be placed (this directory can be passed
to an agent's spec in the recipe system; when doing so it is
not included as part of the generated keys).
"

if ! [ -x "$(command -v jq)" ]; then
echo "this script requires the jq command line tool; please install (https://stedolan.github.io/jq/download/)" >&2
exit 1
fi

config_store_path=""
output_dir_path=""
## parse arguments
while (( "$#" )); do
case "$1" in
-h|--help)
printf "${doc_string}"
exit 0
;;
-cs|--configuration-store)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
config_store_path=$2
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
-o|--output-dir)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
output_dir_path=$2
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
*)
echo "unrecognized argument '$1'" >&2
exit 1
;;
esac
done
## validate arguments and compute vars
if [ -z "$config_store_path" ]; then
echo "path to configuration store (-cs PATH_TO_FILE or --configuration-store PATH_TO_FILE) is required)." >&2
exit 1
fi
if [ -z "$output_dir_path" ]; then
echo "path to output directory (-o PAT or --output-dir PATH) is required)." >&2
exit 1
fi
if [[ ! $config_store_path == *\.store ]]; then
## Note: could probably expand logic here to support other patterns, left as an exercise to whoever needs that
echo "the input config store must end in '.store'"
exit 1
fi
mkdir -p $output_dir_path

## do the parsing
cat $config_store_path | jq -c 'keys[]' | while read an_entry; do
echo "entry is ${an_entry}"
this_out_dir=${output_dir_path}/$(dirname $(echo $an_entry|tr -d '"'))
this_out_file=$(basename $(echo $an_entry|tr -d '"'))
if [ -f $this_out_dir ]; then
this_out_dir=${this_out_dir}.d
fi
mkdir -p ${this_out_dir}
echo "-> put output in [${this_out_dir}]/[${this_out_file}]"
cat $config_store_path | jq -r ".[$an_entry].data" | cat > ${this_out_dir}/${this_out_file}
done
43 changes: 43 additions & 0 deletions examples/update-one-agent.playbook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
- name: configure agents
hosts: all

tasks:

- name: Limit to single inventory host
fail:
msg: "update-agent is only allowed to update a single host, select one of {{ ansible_play_hosts_all }} with `-l HOST`"
when: (ansible_play_hosts | length) != 1
delegate_to: localhost
run_once: true

- include_role:
name: volttron.deployment.set_defaults
- name: confirm agent_identity was provided and is valid
fail:
msg: "agent_identity provided '{{ agent_identity | default('') }}' is not one of {{ identities }}; configure with `-e agent_identity=AGENT_IDENTITY` "
when: (agent_identity | default("")) not in identities
vars:
identities: "{{ host_configuration['agents'].keys() | default([]) }}"

- debug:
msg:
- "item is: {{ item }}"
vars:
- item: "{{ host_configuration['agents'][agent_identity] | combine({'force_install': True}, recursive=true) }}"

- include_role:
name: volttron.deployment.ansible_venv
- include_role:
name: volttron.deployment.copy_agent_configs

- name: update agent
volttron.deployment.volttron_agent:
volttron_root: "{{ volttron_root }}"
volttron_home: "{{ volttron_home }}"
volttron_venv: "{{ volttron_venv }}"
agent_vip_id: "{{ agent_identity }}"
agent_spec: "{{ this_spec }}"
agent_configs_dir: "{{ host_configs_dir }}"
vars:
- this_spec: "{{ host_configuration['agents'][agent_identity] | combine({'force_install': True}, recursive=true) }}"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion galaxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace: volttron
name: deployment

# The version of the collection. Must be compatible with semantic versioning
version: 1.2.0
version: 1.3.0

# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
readme: README.md
Expand Down
4 changes: 4 additions & 0 deletions plugins/modules/volttron_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,11 @@ def _construct_entry_args(path, name=None, absolute_path=False, present=True):
for a_file in glob.glob(os.path.join(data_path, '**'), recursive=True):
if os.path.isdir(a_file):
continue
# stored name should not start with '/'
stored_name = a_file.split(a_config_listing['path'])[-1].lstrip('/')
# path elements ending with `.d` shoudl have that removed (directory marker for name conflicts)
stored_name = os.path.join(*[i.rsplit('.d',1)[0] if i.endswith('.d') else i for i in stored_name.split(os.path.sep)])
# plugin support for name manipulation
stored_name = os.path.join(a_config_listing.get('name',''), stored_name)
store_entries.append(_construct_entry_args(
a_file,
Expand Down
70 changes: 6 additions & 64 deletions roles/get_volttron_source/tasks/from-github.yml
Original file line number Diff line number Diff line change
@@ -1,68 +1,10 @@
### download volttron source from github and place at volttron_root
### registers `volttron_source_changed` fact (based on hash of tarball)
---
- name: "Download volttron repository tarball [{{ source_url }}]"
get_url:
url: "{{ source_url }}"
dest: "/tmp/volttron-{{ volttron_git_branch | regex_replace('/', '.') }}.tar.gz"
force: yes
use_proxy: yes
vars:
source_url: "https://github.com/{{ volttron_git_organization }}/{{ volttron_git_repo }}/archive/{{ volttron_git_branch }}.tar.gz"
- name: Clone git repo for volttron root
ansible.builtin.git:
repo: https://github.com/volttron/volttron
dest: "{{ volttron_root }}"
version: "{{ volttron_git_branch }}"
force: "{{ volttron_git_force_clone }}"
environment:
http_proxy: "{{ http_proxy }}"
https_proxy: "{{ https_proxy }}"

- name: Stat volttron dir
stat:
path: "{{ volttron_root }}"
register: volttron_path_stat

- name: Stat tar file
stat:
path: "/tmp/volttron-{{ volttron_git_branch | regex_replace('/', '.') }}.tar.gz"
get_checksum: yes
register: new_tar_stat

- name: Stat old tar
stat:
path: "{{ ansible_env.HOME }}/volttron-source.tar.gz"
get_checksum: yes
register: old_tar_stat

- name: Check if volttron source is updated
set_fact:
volttron_source_changed: "{{ (not volttron_path_stat.stat.exists or not old_tar_stat.stat.exists or (new_tar_stat.stat.checksum != old_tar_stat.stat.checksum)) }}"

- name: "[re]establish volttron source directory"
when: volttron_source_changed
block:
- name: Move new file
command: mv "/tmp/volttron-{{ volttron_git_branch | regex_replace('/', '.') }}.tar.gz" "{{ ansible_env.HOME }}/volttron-source.tar.gz"

- name: Remove volttron source root dir if exists
file:
path: "{{ volttron_root }}"
state: absent

- name: Create a path to unarchive into
file:
path: "/tmp/volttron_source"
state: absent
- name: Create a path to unarchive into
file:
path: "/tmp/volttron_source"
state: directory
- name: Extract source archive
unarchive:
src: "{{ ansible_env.HOME }}/volttron-source.tar.gz"
dest: "/tmp/volttron_source"
remote_src: yes

- name: Place source tree at volttron root
command: mv "/tmp/volttron_source/{{volttron_git_repo}}-{{ volttron_git_branch | regex_replace('/', '-') }}" "{{ volttron_root }}"
- name: Cleanup unarchive artifacts
file:
path: "/tmp/volttron_source"
state: absent
## end of block to update volttron source
24 changes: 22 additions & 2 deletions roles/get_volttron_source/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@
### It is only responsible for updating the source, not updating what is installed.
### (Though an editable pip install uses symbolic links and may still update in place)
---
- name: check for replaced vctl
stat:
path: "{{ volttron_root }}/volttron/platform/.control.py.recipes.moved"
register: stashed_vctl
- name: restore old vctl
when: stashed_vctl['stat']['exists']
copy:
remote_src: true
src: "{{ volttron_root }}/volttron/platform/.control.py.recipes.moved"
dest: "{{ volttron_root }}/volttron/platform/control.py"
changed_when: false

- name: copy volttron source from github
when: not volttron_use_local_source
include: from-github.yml
- name: copy local volttron source
when: volttron_use_local_source
include: from-local.yml

- name: check vctl version
stat:
path: "{{ volttron_root }}/volttron/platform/control.py"
Expand All @@ -16,8 +29,15 @@
msg:
- "vctl hash is: {{ vctl_stat['stat']['checksum'] }}"
when: verbose_debug_tasks | bool
- name: replace vctl
when: vctl_stat['stat']['checksum'] == "d2369e903a82ba43732ff9cc58917094743a9c44"
- name: stash old vctl
copy:
remote_src: true
src: "{{ volttron_root }}/volttron/platform/control.py"
dest: "{{ volttron_root }}/volttron/platform/.control.py.recipes.moved"
changed_when: false
- name: replace vctl if needed
copy:
src: volttron.platform.control.py
dest: "{{ volttron_root }}/volttron/platform/control.py"
when: vctl_stat['stat']['checksum'] == "d2369e903a82ba43732ff9cc58917094743a9c44"
changed_when: false
Loading

0 comments on commit 962fe67

Please sign in to comment.