summaryrefslogtreecommitdiff
path: root/cloudinit/dhclient_hook.py
diff options
context:
space:
mode:
authorScott Moser <smoser@ubuntu.com>2018-12-03 22:07:59 +0000
committerServer Team CI Bot <josh.powers+server-team-bot@canonical.com>2018-12-03 22:07:59 +0000
commit230e67ebd489c0d895243a2fc66ca95af2cea13b (patch)
tree652f4cec81d827012c964a38ffd12507c672e180 /cloudinit/dhclient_hook.py
parentadbd950af07a4b613a14bd83049915abdd6ca348 (diff)
downloadvyos-cloud-init-230e67ebd489c0d895243a2fc66ca95af2cea13b.tar.gz
vyos-cloud-init-230e67ebd489c0d895243a2fc66ca95af2cea13b.zip
dhclient-hook: cleanups, tests and fix a bug on 'down' event.
I noticed a bug in dhclient_hook on the 'down' event, using 'is' operator rather than '==' (if self.net_action is 'down'). This refactors/simplifies the code a bit for easier testing and adds tests. The reason for the rename of 'action' to 'event' is to just be internally consistent. The word and Namespace 'action' is used by cloud-init main, so it was not really usable here. Also adds a main which can easily be debugged with: CI_DHCP_HOOK_DATA_D=./my.d python -m cloudinit.dhclient_hook up eth0
Diffstat (limited to 'cloudinit/dhclient_hook.py')
-rw-r--r--cloudinit/dhclient_hook.py110
1 files changed, 72 insertions, 38 deletions
diff --git a/cloudinit/dhclient_hook.py b/cloudinit/dhclient_hook.py
index 7f02d7fa..72b51b6a 100644
--- a/cloudinit/dhclient_hook.py
+++ b/cloudinit/dhclient_hook.py
@@ -1,5 +1,8 @@
# This file is part of cloud-init. See LICENSE file for license information.
+"""Run the dhclient hook to record network info."""
+
+import argparse
import os
from cloudinit import atomic_helper
@@ -8,44 +11,75 @@ from cloudinit import stages
LOG = logging.getLogger(__name__)
+NAME = "dhclient-hook"
+UP = "up"
+DOWN = "down"
+EVENTS = (UP, DOWN)
+
+
+def _get_hooks_dir():
+ i = stages.Init()
+ return os.path.join(i.paths.get_runpath(), 'dhclient.hooks')
+
+
+def _filter_env_vals(info):
+ """Given info (os.environ), return a dictionary with
+ lower case keys for each entry starting with DHCP4_ or new_."""
+ new_info = {}
+ for k, v in info.items():
+ if k.startswith("DHCP4_") or k.startswith("new_"):
+ key = (k.replace('DHCP4_', '').replace('new_', '')).lower()
+ new_info[key] = v
+ return new_info
+
+
+def run_hook(interface, event, data_d=None, env=None):
+ if event not in EVENTS:
+ raise ValueError("Unexpected event '%s'. Expected one of: %s" %
+ (event, EVENTS))
+ if data_d is None:
+ data_d = _get_hooks_dir()
+ if env is None:
+ env = os.environ
+ hook_file = os.path.join(data_d, interface + ".json")
+
+ if event == UP:
+ if not os.path.exists(data_d):
+ os.makedirs(data_d)
+ atomic_helper.write_json(hook_file, _filter_env_vals(env))
+ LOG.debug("Wrote dhclient options in %s", hook_file)
+ elif event == DOWN:
+ if os.path.exists(hook_file):
+ os.remove(hook_file)
+ LOG.debug("Removed dhclient options file %s", hook_file)
+
+
+def get_parser(parser=None):
+ if parser is None:
+ parser = argparse.ArgumentParser(prog=NAME, description=__doc__)
+ parser.add_argument(
+ "event", help='event taken on the interface', choices=EVENTS)
+ parser.add_argument(
+ "interface", help='the network interface being acted upon')
+ # cloud-init main uses 'action'
+ parser.set_defaults(action=(NAME, handle_args))
+ return parser
+
+
+def handle_args(name, args, data_d=None):
+ """Handle the Namespace args.
+ Takes 'name' as passed by cloud-init main. not used here."""
+ return run_hook(interface=args.interface, event=args.event, data_d=data_d)
+
+
+if __name__ == '__main__':
+ import sys
+ parser = get_parser()
+ args = parser.parse_args(args=sys.argv[1:])
+ return_value = handle_args(
+ NAME, args, data_d=os.environ.get('_CI_DHCP_HOOK_DATA_D'))
+ if return_value:
+ sys.exit(return_value)
-class LogDhclient(object):
-
- def __init__(self, cli_args):
- self.hooks_dir = self._get_hooks_dir()
- self.net_interface = cli_args.net_interface
- self.net_action = cli_args.net_action
- self.hook_file = os.path.join(self.hooks_dir,
- self.net_interface + ".json")
-
- @staticmethod
- def _get_hooks_dir():
- i = stages.Init()
- return os.path.join(i.paths.get_runpath(), 'dhclient.hooks')
-
- def check_hooks_dir(self):
- if not os.path.exists(self.hooks_dir):
- os.makedirs(self.hooks_dir)
- else:
- # If the action is down and the json file exists, we need to
- # delete the file
- if self.net_action is 'down' and os.path.exists(self.hook_file):
- os.remove(self.hook_file)
-
- @staticmethod
- def get_vals(info):
- new_info = {}
- for k, v in info.items():
- if k.startswith("DHCP4_") or k.startswith("new_"):
- key = (k.replace('DHCP4_', '').replace('new_', '')).lower()
- new_info[key] = v
- return new_info
-
- def record(self):
- envs = os.environ
- if self.hook_file is None:
- return
- atomic_helper.write_json(self.hook_file, self.get_vals(envs))
- LOG.debug("Wrote dhclient options in %s", self.hook_file)
# vi: ts=4 expandtab