Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ data/reftree.cache
data/op_cache.json
# autogenerated vyos-configd JSON definition
data/configd-include.json
# autogenerated activation-scripts JSON definition
data/activation-list
data/activation-init

# autogenerated vyos-commitd protobuf files
python/vyos/proto/*.desc
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ vyshim:
$(MAKE) -C $(SHIM_DIR)

.PHONY: all
all: clean copyright libvyosconfig pylint interface_definitions op_mode_definitions test j2lint vyshim generate-configd-include-json
all: clean copyright libvyosconfig pylint interface_definitions op_mode_definitions test j2lint vyshim generate-configd-include-json generate-activation-scripts-json

.PHONY: copyright
copyright:
Expand Down Expand Up @@ -130,6 +130,10 @@ deb:
generate-configd-include-json:
@scripts/generate-configd-include-json.py

.PHONY: generate-activation-scripts-json
generate-activation-scripts-json:
@scripts/generate-activation-scripts-json.py

.PHONY: schema
schema:
trang -I rnc -O rng schema/interface_definition.rnc schema/interface_definition.rng
Expand Down
61 changes: 61 additions & 0 deletions op-mode-definitions/system-activation.xml.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<interfaceDefinition>
<node name="set">
<properties>
<help>Set parameters and behaviors</help>
</properties>
<children>
<node name="system">
<properties>
<help>Set system operational parameters</help>
</properties>
<children>
<node name="activation">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you do not check the details in the description, the CLI, at first glance, wants to "activate" the system with a license key.

set system activation script ххх

At least that's what I associate with it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps 'activation unit' is preferable ? That would avoid some confusion for the op-mode directive ...

<properties>
<help>Activation scripts at boot</help>
</properties>
<children>
<tagNode name="script">
<properties>
<help>Script name</help>
<completionHelp>
<script>${vyos_op_scripts_dir}/activation.py show_list</script>
</completionHelp>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a constraint to allow only files which are present in the supported directory. Also prevent escape paths like ../../../

</properties>
<children>
<tagNode name="active">
<properties>
<help>Set activation on reboot</help>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<help>Set activation on reboot</help>
<help>Execute on reboot</help>

<completionHelp>
<script>${vyos_op_scripts_dir}/activation.py show_opts</script>
</completionHelp>
</properties>
<command>${vyos_op_scripts_dir}/activation.py set_active --name "${5}" --value "${7}"</command>
</tagNode>
</children>
</tagNode>
</children>
</node>
</children>
</node>
</children>
</node>
<node name="show">
<children>
<node name="system">
<properties>
<help>Show system information</help>
</properties>
<children>
<node name="activation">
<properties>
<help>Activation scripts at boot</help>
</properties>
<command>${vyos_op_scripts_dir}/activation.py show</command>
</node>
</children>
</node>
</children>
</node>
</interfaceDefinition>

2 changes: 1 addition & 1 deletion op-mode-definitions/system-image.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
</node>
<node name="set">
<properties>
<help>Install a new system</help>
<help>Set parameters and behaviors</help>
</properties>
<children>
<node name="system">
Expand Down
2 changes: 1 addition & 1 deletion op-mode-definitions/terminal.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</node>
<node name="set">
<properties>
<help>Set operational options</help>
<help>Set parameters and behaviors</help>
</properties>
<children>
<tagNode name="builtin">
Expand Down
4 changes: 4 additions & 0 deletions python/vyos/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,7 @@
'ED25519) to avoid authentication failures after the upgrade.'

reference_tree_cache = '/usr/share/vyos/reftree.cache'

activation_list = os.path.join(directories['config'], 'activation-list')
activation_init = os.path.join(directories['data'], 'activation-init')
activation_hint = os.path.join(directories['data'], '.activation_hint')
126 changes: 126 additions & 0 deletions python/vyos/utils/activate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Copyright (C) VyOS Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.


import json
import typing
from pathlib import Path

from vyos.base import Warning as Warn
from vyos.defaults import activation_list
from vyos.defaults import activation_init
from vyos.defaults import activation_hint
from vyos.defaults import directories


ActiveOpt = typing.Literal['enabled', 'once', 'off', 'never']


def get_activation_scripts() -> dict:
list_path = Path(activation_list)
return json.loads(list_path.read_text())


def set_activation(file_name: str, value: ActiveOpt):
script_dict = get_activation_scripts()
file_key = Path(file_name).stem
script_dict[file_key] = value
list_path = Path(activation_list)
list_path.write_text(json.dumps(script_dict))


def get_activation(file_name: str) -> ActiveOpt:
script_dict = get_activation_scripts()
file_key = Path(file_name).stem
return script_dict[file_key]


def is_active(file_name: str) -> bool:
script_dict = get_activation_scripts()
file_key = Path(file_name).stem
if script_dict[file_key] in ('enabled', 'once'):
return True
return False


def stable_update(new: dict, old: dict):
res = {}
for key in new.keys():
res[key] = old[key] if key in old.keys() else new[key]
return res


def init_activation_list() -> bool:
"""Init if activation_hint exists, left on install_image or if image was
built as raw_image"""

init_hint = Path(activation_hint)
if not init_hint.exists():
return False

init_hint.unlink()
init_list = Path(activation_init)
data_list = Path(activation_list)
data_obj = json.loads(init_list.read_text())
data_list.write_text(json.dumps(data_obj))

return True


def refresh_activation_list():
"""Refresh activation list, as will be needed after image update"""

if init_activation_list():
return

new_list_path = Path(directories['data']).joinpath(Path(activation_list).name)
if not new_list_path.exists():
return

new_obj = json.loads(new_list_path.read_text())

orig_list_path = Path(activation_list)
if orig_list_path.exists():
orig_obj = json.loads(orig_list_path.read_text())
if orig_obj == new_obj:
return

obj = stable_update(new_obj, orig_obj)
else:
obj = new_obj

orig_list_path.write_text(json.dumps(obj))


first_installed_boot_file = '/run/first_installed_boot'


def set_first_installed_boot():
try:
Path(first_installed_boot_file).touch(exist_ok=False)
except FileExistsError:
Warn('redundant set of first_installed_boot')


def is_first_installed_boot():
return Path(first_installed_boot_file).exists()


def set_config_path_hint():
"""The config hint allows subsequent installs to find previous disk
resident config data. It is traditionally added as part of the image
install procedure, however, for raw image builds an alternative is
needed."""
Path(directories['config']).joinpath('.vyatta_config').touch(exist_ok=True)
28 changes: 28 additions & 0 deletions python/vyos/utils/func.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright (C) VyOS Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.


"""Module for functors and higher order functions."""


class FalseCallable:
"""Define falsy callable for use as default value for getattr of a
function from a module defined by vyos.utils.system.load_as_module"""

def __call__(self, *args, **kwargs):
pass

def __bool__(self):
return False
47 changes: 47 additions & 0 deletions scripts/generate-activation-scripts-json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
#
# Copyright (C) VyOS Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.


import re
import json
from pathlib import Path


def filter_key(s: Path):
s = s.stem
return re.match(r'\d+\-.+', s)


def sort_key(s: Path):
s = s.stem
pre, rem = re.match(r'(\d+)(?:-)(.+)', s).groups()
return int(pre), rem


activation_dir = 'src/activation-scripts'
activation_list = 'data/activation-list'
activation_list_init = 'data/activation-init'

activation_scripts = Path(activation_dir).glob('*.py')

filtered = filter(filter_key, activation_scripts)
script_list = sorted(filtered, key=sort_key)

script_dict = dict.fromkeys(map(lambda s: s.stem, script_list), 'off')
script_dict_init = dict.fromkeys(map(lambda s: s.stem, script_list), 'enabled')

Path(activation_list).write_text(json.dumps(script_dict))
Path(activation_list_init).write_text(json.dumps(script_dict_init))
35 changes: 35 additions & 0 deletions src/activation-scripts/00-first-installed-boot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (C) VyOS Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library. If not, see <http://www.gnu.org/licenses/>.


from vyos.configtree import ConfigTree
from vyos.system.image import is_live_boot
from vyos.utils.activate import set_activation
from vyos.utils.activate import set_first_installed_boot
from vyos.utils.activate import is_first_installed_boot


def pre_condition() -> bool:
return not is_live_boot()


def activate(_config: ConfigTree) -> None:
pass


def post_condition() -> None:
set_first_installed_boot()
if is_first_installed_boot():
set_activation(__file__, 'never')
35 changes: 35 additions & 0 deletions src/activation-scripts/01-set-config-path-hint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (C) VyOS Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library. If not, see <http://www.gnu.org/licenses/>.


from vyos.configtree import ConfigTree
from vyos.system.image import is_live_boot
from vyos.utils.activate import set_activation
from vyos.utils.activate import set_config_path_hint
from vyos.utils.activate import is_first_installed_boot


def pre_condition() -> bool:
return not is_live_boot()


def activate(_config: ConfigTree) -> None:
pass


def post_condition() -> None:
if is_first_installed_boot():
set_config_path_hint()
set_activation(__file__, 'never')
Loading
Loading