Skip to content

Commit 99cb1c2

Browse files
authored
Merge pull request #2804 from schandrika/historian_upgrade_8.x
Historian upgrade 8.x
2 parents 3cead8c + 06674fe commit 99cb1c2

File tree

13 files changed

+305
-41
lines changed

13 files changed

+305
-41
lines changed

README.md

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,38 @@ with that data.
1010

1111
## Upgrading to VOLTTRON 8.x
1212

13-
VOLTTRON 8 and above introduces dynamic RPC authorization, which requires a modification to the auth file.
14-
If you have a pre-existing instance of VOLTTRON running on an older version, the auth file will need to be updated.
15-
To begin the upgrade process, activate the volttron environment, and run ```python bootstrap.py --force```. If you
16-
have any additional bootstrap options that you need (rabbitmq, web, drivers, etc.) include these in the above command.
17-
After the bootstrap process is completed, run ```volttron-update-auth``` to update the auth file.
18-
13+
VOLTTRON 8 introduces three changes that require an explict upgrade step when upgrading from a earlier VOLTTRON version
14+
15+
1. Dynamic RPC authorization feature - This requires a modification to the auth file. If you have a pre-existing
16+
instance of VOLTTRON running on an older version, the auth file will need to be updated.
17+
2. Historian agents now store the cache database (backup.sqlite file) in
18+
<volttron home>/agents/<agent uuid>/<agentname-version>/<agentname-version>.agent-data directory instead of
19+
<volttron home>/agents/<agent uuid>/<agentname-version> directory. In future all core agents will write data only
20+
to the <agentname-version>.agent-data subdirectory. This is because vctl install --force backs up and restores
21+
only the contents of this directory.
22+
3. SQLHistorians (historian version 4.0.0 and above) now use a new database schema where metadata is stored in
23+
topics table instead of separate metadata table. SQLHistorians with version >= 4.0.0 can work with existing
24+
database with older schema however the historian agent code should be upgraded to newer version (>=4.0.0) to run
25+
with VOLTTRON 8 core.
26+
27+
To begin the upgrade process, activate the volttron environment, and run ```python bootstrap.py --force```. If you have
28+
any additional bootstrap options that you need (rabbitmq, web, drivers, etc.) include these in the above command.
29+
30+
After the bootstrap process is completed, run ```volttron-upgrade``` to update the auth file and move historian
31+
cache files into agent-data directory. Note that the upgrade script will only move the backup.sqlite file and will not
32+
move sqlite historian's db file if they are within the install directory. If using a SQLite historian, please backup
33+
the database file of sqlite historian before upgrading to the latest historian version.
34+
35+
Once the volttron-upgrade script is complete, you can do a vctl install --force command to upgrade to the latest
36+
historian version. vctl install --force will backup the cache in <agent-version>.agent-data folder, install the latest
37+
version of the historian and restore the contents of <agent-version>.agent-data folder.
38+
39+
40+
### Upgrading aggregate historians
41+
42+
VOLTTRON 8 also comes with updated SQL aggregate historian schema. However, there is no automated upgrade path for
43+
aggregate historian. To upgrade an existing aggregate historian please refer to the CHANGELOG.md within
44+
SQLAggregateHistorian source directory
1945

2046
## Features
2147

docs/source/volttron-topics/VOLTTRON-releases/upgrading-versions.rst

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,42 @@ your existing deployment to the latest version.
1111
VOLTTRON 8
1212
==========
1313

14-
VOLTTRON 8 and above introduces dynamic RPC authorization, which requires a modification to the auth file.
15-
If you have a pre-existing instance of VOLTTRON running on an older version, the auth file will need to be updated.
14+
VOLTTRON 8 introduces three changes that require an explict upgrade step when upgrading from a earlier VOLTTRON version
15+
16+
1. Dynamic RPC authorization feature - This requires a modification to the auth file. If you have a pre-existing
17+
instance of VOLTTRON running on an older version, the auth file will need to be updated.
18+
2. Historian agents now store the cache database (backup.sqlite file) in
19+
<volttron home>/agents/<agent uuid>/<agentname-version>/<agentname-version>.agent-data directory instead of
20+
<volttron home>/agents/<agent uuid>/<agentname-version> directory. In future all core agents will write data only
21+
to the <agentname-version>.agent-data subdirectory. This is because vctl install --force backs up and restores
22+
only the contents of this directory.
23+
3. SQLHistorians (historian version 4.0.0 and above) now use a new database schema where metadata is stored in
24+
topics table instead of separate metadata table. SQLHistorians with version >= 4.0.0 can work with existing
25+
database with older schema however the historian agent code should be upgraded to newer version (>=4.0.0) to run
26+
with VOLTTRON 8 core.
27+
1628
To begin the upgrade process, activate the volttron environment, and run ```python bootstrap.py --force```.
1729

1830
.. note::
1931

2032
If you have any additional bootstrap options that you need (rabbitmq, web, drivers, etc.)
2133
include these in the above command.
2234

23-
After the bootstrap process is completed, run ```volttron-update-auth``` to update the auth file.
35+
After the bootstrap process is completed, run ```volttron-upgrade``` to update the auth file and move historian
36+
cache files into agent-data directory. Note that the upgrade script will only move the backup.sqlite file and will not
37+
move sqlite historian's db file if they are within the install directory. If using a SQLite historian, please backup
38+
the database file of sqlite historian before upgrading to the latest historian version.
39+
40+
Once the volttron-upgrade script is complete, you can do a vctl install --force command to upgrade to the latest
41+
historian version. vctl install --force will backup the cache in <agent-version>.agent-data folder, install the latest
42+
version of the historian and restore the contents of <agent-version>.agent-data folder.
43+
44+
Upgrading aggregate historians
45+
------------------------------
46+
47+
VOLTTRON 8 also comes with updated SQL aggregate historian schema. However, there is no automated upgrade path for
48+
aggregate historian. To upgrade an existing aggregate historian please refer to the CHANGELOG.md within
49+
SQLAggregateHistorian source directory
2450

2551
VOLTTRON 7
2652
==========

services/core/SQLHistorian/tests/test_sqlitehistorian_unit.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import os
22
from shutil import rmtree
33
import subprocess
4+
from pathlib import Path
45

56
import pytest
67
from gevent import sleep
78
from datetime import timedelta
89
from services.core.SQLHistorian.sqlhistorian import historian
910

10-
CACHE_NAME = "backup.sqlite"
11+
agent_data_dir = os.path.join(os.getcwd(), os.path.basename(os.getcwd()) + ".agent-data")
12+
os.makedirs(agent_data_dir, exist_ok=True)
13+
CACHE_NAME = str(Path(agent_data_dir).joinpath("backup.sqlite"))
1114
HISTORIAN_DB = "./data/historian.sqlite"
1215

1316

@@ -76,6 +79,8 @@ def sql_historian():
7679
rmtree("./data")
7780
if os.path.exists(CACHE_NAME):
7881
os.remove(CACHE_NAME)
82+
if os.path.exists(agent_data_dir):
83+
os.rmdir(agent_data_dir)
7984

8085

8186
def query_db(query, db):

setup.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@
7070
'vctl = volttron.platform.control:_main',
7171
'vpkg = volttron.platform.packaging:_main',
7272
'vcfg = volttron.platform.config:_main',
73-
# 'volttron-update = ...',
74-
'volttron-update-auth = volttron.platform.update_auth_file:_main',
73+
'volttron-upgrade = volttron.platform.upgrade.upgrade_volttron:_main',
7574
]
7675
},
7776
zip_safe = False,

volttron/platform/agent/base_historian.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,13 +1745,13 @@ def close(self):
17451745
def _setupdb(self, check_same_thread):
17461746
""" Creates a backup database for the historian if doesn't exist."""
17471747

1748-
_log.debug("Setting up backup DB.")
1749-
if utils.is_secure_mode():
1750-
# we want to create it in the agent-data directory since agent will not have write access to any other
1751-
# directory in secure mode
1752-
backup_db = os.path.join(os.getcwd(), os.path.basename(os.getcwd()) + ".agent-data", 'backup.sqlite')
1753-
else:
1754-
backup_db = 'backup.sqlite'
1748+
_log.debug(f"Setting up backup DB. {os.getcwd()}")
1749+
# we want to create it in the agent-data directory since agent will not have write access to any other
1750+
# directory in secure mode
1751+
# TODO - revisit logic to get agent-data directory. Refer to aip.get_agent_data()
1752+
# Also take into account dynamic agents - especially for testing
1753+
backup_db = os.path.join(os.getcwd(), os.path.basename(os.getcwd()) + ".agent-data", 'backup.sqlite')
1754+
17551755
_log.info(f"Creating backup db at {backup_db}")
17561756
self._connection = sqlite3.connect(
17571757
backup_db,

volttron/platform/aip.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ def set_agent_user_permissions(self, volttron_agent_user,
343343
# except agent-data dir. agent-data dir has rwx
344344
self.set_acl_for_path("rx", volttron_agent_user, agent_dir)
345345
# creates dir if it doesn't exist
346-
data_dir = self._get_agent_data_dir(agent_path_with_name)
346+
data_dir = self.get_agent_data_dir(agent_path_with_name)
347347

348348
for (root, directories, files) in os.walk(agent_dir, topdown=True):
349349
for directory in directories:
@@ -609,7 +609,7 @@ def _unauthorize_agent_keys(self, agent_uuid):
609609
publickey = self.get_agent_keystore(agent_uuid).public
610610
AuthFile().remove_by_credentials(publickey)
611611

612-
def _get_agent_data_dir(self, agent_path):
612+
def get_agent_data_dir(self, agent_path):
613613
pkg = UnpackedPackage(agent_path)
614614
data_dir = os.path.join(os.path.dirname(pkg.distinfo),
615615
'{}.agent-data'.format(pkg.package_name))
@@ -618,7 +618,7 @@ def _get_agent_data_dir(self, agent_path):
618618
return data_dir
619619

620620
def create_agent_data_dir_if_missing(self, agent_uuid):
621-
new_agent_data_dir = self._get_agent_data_dir(self.agent_dir(agent_uuid))
621+
new_agent_data_dir = self.get_agent_data_dir(self.agent_dir(agent_uuid))
622622
return new_agent_data_dir
623623

624624
def _get_data_dir(self, agent_path, agent_name):
@@ -953,7 +953,7 @@ def start_agent(self, agent_uuid):
953953
resmon = getattr(self.env, 'resmon', None)
954954
agent_user = None
955955

956-
data_dir = self._get_agent_data_dir(agent_path_with_name)
956+
data_dir = self.get_agent_data_dir(agent_path_with_name)
957957

958958
if self.secure_agent_user:
959959
_log.info("Starting agent securely...")

volttron/platform/upgrade/__init__.py

Whitespace-only changes.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# -*- coding: utf-8 -*- {{{
2+
# vim: set fenc=utf-8 ft=python sw=4 ts=4 sts=4 et:
3+
#
4+
# Copyright 2020, Battelle Memorial Institute.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# This material was prepared as an account of work sponsored by an agency of
19+
# the United States Government. Neither the United States Government nor the
20+
# United States Department of Energy, nor Battelle, nor any of their
21+
# employees, nor any jurisdiction or organization that has cooperated in the
22+
# development of these materials, makes any warranty, express or
23+
# implied, or assumes any legal liability or responsibility for the accuracy,
24+
# completeness, or usefulness or any information, apparatus, product,
25+
# software, or process disclosed, or represents that its use would not infringe
26+
# privately owned rights. Reference herein to any specific commercial product,
27+
# process, or service by trade name, trademark, manufacturer, or otherwise
28+
# does not necessarily constitute or imply its endorsement, recommendation, or
29+
# favoring by the United States Government or any agency thereof, or
30+
# Battelle Memorial Institute. The views and opinions of authors expressed
31+
# herein do not necessarily state or reflect those of the
32+
# United States Government or any agency thereof.
33+
#
34+
# PACIFIC NORTHWEST NATIONAL LABORATORY operated by
35+
# BATTELLE for the UNITED STATES DEPARTMENT OF ENERGY
36+
# under Contract DE-AC05-76RL01830
37+
# }}}
38+
39+
import sys
40+
import shutil
41+
from pathlib import Path
42+
import glob
43+
import re
44+
from gevent import monkey as curious_george
45+
curious_george.patch_all(thread=False, select=False)
46+
47+
from volttron.platform import get_home
48+
from volttron.platform.aip import AIPplatform
49+
from volttron.platform.instance_setup import fail_if_instance_running
50+
51+
52+
def get_aip():
53+
"""Get AIPplatform to interface with agent directories in vhome"""
54+
55+
vhome = get_home()
56+
options = type("Options", (), dict(volttron_home=vhome))
57+
aip = AIPplatform(options)
58+
return aip
59+
60+
61+
def move_historian_cache_files(aip):
62+
"""
63+
Moves any keystore.json from agent-data to dist-info.
64+
Only applies to agents in auth file.
65+
"""
66+
67+
vhome = Path(aip.env.volttron_home)
68+
install_dir = vhome.joinpath("agents")
69+
# pattern example - (/vhome/agents/uuid/agentname-version/)(backup.sqlite)
70+
# pattern example - (vhome/agents/uuid/agentname-version/)(data/subdir/sqlitehistoriandb.sqlite)
71+
pattern = "(" + str(install_dir) + "/[^/]*/[^/]*/)(.*)"
72+
re_pattern = re.compile(pattern)
73+
# Look for all .sqlite files in installed agents
74+
print(f"Attempting to move backup.sqlite files in {install_dir} into corresponding agent-data directory")
75+
# currently this is only used for backup.sqlite
76+
# In 9.0 we could use the same code for *.sqlite files
77+
# for example ones created by sqlitetagging, topic watcher, weather etc. when we make agent-data folder default
78+
# agent write directory for all core agents
79+
glob_path = str(install_dir.joinpath("**/backup.sqlite"))
80+
for sqlite_file in glob.glob(glob_path, recursive=True):
81+
result = re_pattern.match(sqlite_file).groups()
82+
agent_dir = result[0] # <volttron home>/agents/uuid/<agent name-version>
83+
source_file = result[1].split('/', 1)[0] # file or directory name to be moved to agent-data folder
84+
source_path = str(Path(agent_dir).joinpath(source_file))
85+
dest_dir = aip.get_agent_data_dir(agent_path=agent_dir)
86+
if source_path != dest_dir:
87+
# if file is not already in agent-data dir
88+
result = shutil.move(source_path, dest_dir)
89+
90+
# print from uuid dir name so that it is easy to read
91+
print_src = source_path.split(str(install_dir)+"/")[1]
92+
print_dest = result.split(str(install_dir) + "/")[1]
93+
print(f"Moved {print_src} to {print_dest}")
94+
95+
96+
def main():
97+
"""Upgrade auth file to function with dynamic rpc authorizations"""
98+
99+
fail_if_instance_running()
100+
aip = get_aip()
101+
move_historian_cache_files(aip)
102+
print("")
103+
print("Moving historian backup files complete. "
104+
"You can now safely upgrade historian agents other than SQLITE Historian with vctl install --force. "
105+
"If using using SQLite historian please back up and restore sqlite historian's db manually")
106+
107+
108+
def _main():
109+
""" Wrapper for main function"""
110+
111+
try:
112+
sys.exit(main())
113+
except KeyboardInterrupt:
114+
sys.exit(1)
115+
116+
117+
if __name__ == "__main__":
118+
_main()

volttron/platform/update_auth_file.py renamed to volttron/platform/upgrade/update_auth_file.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@
3939
import sys
4040
import shutil
4141
from pathlib import Path
42-
from gevent import monkey as curious_george
43-
curious_george.patch_all(thread=False, select=False)
4442

4543
from volttron.platform import get_home
4644
from volttron.platform.aip import AIPplatform
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# -*- coding: utf-8 -*- {{{
2+
# vim: set fenc=utf-8 ft=python sw=4 ts=4 sts=4 et:
3+
#
4+
# Copyright 2020, Battelle Memorial Institute.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# This material was prepared as an account of work sponsored by an agency of
19+
# the United States Government. Neither the United States Government nor the
20+
# United States Department of Energy, nor Battelle, nor any of their
21+
# employees, nor any jurisdiction or organization that has cooperated in the
22+
# development of these materials, makes any warranty, express or
23+
# implied, or assumes any legal liability or responsibility for the accuracy,
24+
# completeness, or usefulness or any information, apparatus, product,
25+
# software, or process disclosed, or represents that its use would not infringe
26+
# privately owned rights. Reference herein to any specific commercial product,
27+
# process, or service by trade name, trademark, manufacturer, or otherwise
28+
# does not necessarily constitute or imply its endorsement, recommendation, or
29+
# favoring by the United States Government or any agency thereof, or
30+
# Battelle Memorial Institute. The views and opinions of authors expressed
31+
# herein do not necessarily state or reflect those of the
32+
# United States Government or any agency thereof.
33+
#
34+
# PACIFIC NORTHWEST NATIONAL LABORATORY operated by
35+
# BATTELLE for the UNITED STATES DEPARTMENT OF ENERGY
36+
# under Contract DE-AC05-76RL01830
37+
# }}}
38+
39+
import sys
40+
from gevent import monkey as curious_george
41+
curious_george.patch_all(thread=False, select=False)
42+
43+
from . import update_auth_file
44+
from . import move_sqlite_files
45+
46+
47+
def main():
48+
# Upgrade auth file to function with dynamic rpc authorizations
49+
update_auth_file.main()
50+
print("")
51+
# Moves backup cache of historian (backup.sqlite files) into corresponding agent-data directory so that
52+
# historian agents other than sqlitehistorian, can be upgraded to latest version using
53+
# vctl install --force without losing cache data. vctl install --force will backup and restore
54+
# contents of <agent-install-dir>/<agentname-version>/<agentname-version>.agent-data directory
55+
# If using sqlite historian manually backup and restore sqlite historian's db before upgrading to historian version
56+
# 4.0.0 or later
57+
move_sqlite_files.main()
58+
59+
60+
def _main():
61+
""" Wrapper for main function"""
62+
try:
63+
sys.exit(main())
64+
except KeyboardInterrupt:
65+
sys.exit(1)
66+
67+
if __name__ == "__main__":
68+
_main()

0 commit comments

Comments
 (0)