Skip to content

Commit

Permalink
Tweak some logic and cleanup code
Browse files Browse the repository at this point in the history
  • Loading branch information
TheRealFalcon committed Mar 5, 2021
1 parent 4af547c commit ec59faa
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 71 deletions.
42 changes: 20 additions & 22 deletions cloudinit/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
LOG = logging.getLogger(__name__)


# Event types which can generate maintenance requests for cloud-init.
class EventType(object):
"""Event types which can generate maintenance requests for cloud-init."""

BOOT = "System boot"
BOOT_NEW_INSTANCE = "New instance first boot"

# TODO: Cloud-init will grow support for the follow event types:
# UDEV
# Cloud-init should grow support for the follow event types:
# HOTPLUG
# METADATA_CHANGE
# USER_REQUEST

Expand All @@ -30,45 +31,42 @@ class EventType(object):
EventNameMap = {v: k for k, v in EventTypeMap.items()}


def get_allowed_events(sys_events, ds_events, user_events):
'''Merge datasource capabilities with system config to determine which
update events are allowed.'''
def get_enabled_events(config_events, default_events):
"""Determine which update events are allowed.
Merge datasource capabilities with system config to determine events
"""
# updates:
# network:
# when: [boot-new-instance, boot, hotplug]
# storage:
# when: [boot-new-instance, hotplug]
# watch: http://169.254.169.254/metadata/storage_config/
# when: [boot]

LOG.debug('updates: user cfg: %s', user_events)
LOG.debug('updates: system cfg: %s', sys_events)
LOG.debug('updates: datasrc caps: %s', ds_events)
LOG.debug('updates user: %s', config_events)
LOG.debug('updates default: %s', default_events)

updates = util.mergemanydict([copy.deepcopy(user_events),
copy.deepcopy(sys_events),
copy.deepcopy(ds_events)])
LOG.debug('updates: merged cfg: %s', updates)
# If a key exists in multiple places, the first in the list wins.
updates = util.mergemanydict([
copy.deepcopy(config_events),
copy.deepcopy(default_events),
])
LOG.debug('updates merged: %s', updates)

events = {}
for etype in [scope for scope, val in sys_events.items()
for etype in [scope for scope, val in default_events.items()
if type(val) == dict and 'when' in val]:
events[etype] = (
set([EventTypeMap.get(evt)
for evt in updates.get(etype, {}).get('when', [])
if evt in EventTypeMap]))

LOG.debug('updates: allowed events: %s', events)
LOG.debug('updates allowed: %s', events)
return events


def get_update_events_config(update_events):
'''Return a dictionary of updates config'''
"""Return a dictionary of updates config."""
evt_cfg = {}
for scope, events in update_events.items():
evt_cfg[scope] = {'when': [EventNameMap[evt] for evt in events]}

return evt_cfg


# vi: ts=4 expandtab
2 changes: 1 addition & 1 deletion cloudinit/sources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ def get_package_mirror_info(self):
def update_metadata(self, source_event_types):
"""Refresh cached metadata if the datasource supports this event.
The datasource has a list of update_events which
The datasource has a list of supported_update_events which
trigger refreshing all cached metadata as well as refreshing the
network configuration.
Expand Down
76 changes: 39 additions & 37 deletions cloudinit/stages.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from cloudinit.event import (
EventType,
get_allowed_events,
get_enabled_events,
get_update_events_config,
)
from cloudinit.sources import NetworkConfigSource
Expand Down Expand Up @@ -345,6 +345,11 @@ def previous_iid(self):
return self._previous_iid

def is_new_instance(self):
"""Return true if this is a new instance.
If datasource has already been initialized, this will return False,
even on first boot.
"""
previous = self.previous_iid()
ret = (previous == NO_PREVIOUS_INSTANCE_ID or
previous != self.datasource.get_instance_id())
Expand Down Expand Up @@ -697,21 +702,15 @@ def _find_networking_config(self):
return (self.distro.generate_fallback_config(),
NetworkConfigSource.fallback)

def update_event_allowed(self, event_source_type, scope=None):
def update_event_enabled(self, event_source_type, scope=None):
# convert ds events to config
ds_config = get_update_events_config(
self.datasource.supported_update_events
default_events = get_update_events_config(
self.datasource.default_update_events
)
config_events = self.cfg.get('updates', {})
allowed = get_enabled_events(
config_events, default_events
)
LOG.debug('Datasource updates cfg: %s', ds_config)

sys_config = self.cfg.get('updates', {})
LOG.debug('System updates cfg: %s', sys_config)

user_config = self.datasource.get_userdata(True).get('updates', {})
LOG.debug('User updates cfg: %s', user_config)

allowed = get_allowed_events(sys_config, ds_config, user_config)
LOG.debug('Allowable update events: %s', allowed)

if not scope:
scopes = [allowed.keys()]
Expand Down Expand Up @@ -747,41 +746,44 @@ def _is_first_boot(self):
return first_boot

def apply_network_config(self, bring_up):
# get a network config
"""Apply the network config.
Find the config, determine whether to apply it, apply it via
the distro, and optionally bring it up
"""

netcfg, src = self._find_networking_config()
if netcfg is None:
LOG.info("network config is disabled by %s", src)
return

# request an update if needed/available
apply_network = True
if self.datasource is not NULL_DATA_SOURCE:
if not self.is_new_instance() and not self._is_first_boot():
if self.update_event_allowed(EventType.BOOT, scope='network'):
if not self.datasource.update_metadata([EventType.BOOT]):
LOG.debug(
"No network config applied. Neither a new instance"
" nor datasource network update on '%s' event",
EventType.BOOT)
# nothing new, but ensure proper names
apply_network = False
else:
# refresh netcfg after update
netcfg, src = self._find_networking_config()
else:
LOG.debug("No network config applied. "
"'%s' event not allowed", EventType.BOOT)
apply_network = False
current_boot_type = EventType.BOOT
if self.is_new_instance():
current_boot_type = EventType.BOOT_NEW_INSTANCE

if not any([
self.datasource is NULL_DATA_SOURCE,
self.is_new_instance(), # or should it be self._is_first_boot(),
(self.update_event_enabled(current_boot_type, scope='network')
and self.datasource.update_metadata([current_boot_type]))
]):
LOG.debug(
"No network config applied. Neither a new instance"
" nor datasource network update on '%s' event",
EventType.BOOT)
# nothing new, but ensure proper names
self._apply_netcfg_names(netcfg)
return
else:
# refresh netcfg after update
netcfg, src = self._find_networking_config()

# ensure all physical devices in config are present
self.distro.networking.wait_for_physdevs(netcfg)

# apply renames from config
self._apply_netcfg_names(netcfg)

if not apply_network:
return

# rendering config
LOG.info("Applying network configuration from %s bringup=%s: %s",
src, bring_up, netcfg)
Expand Down
24 changes: 13 additions & 11 deletions doc/rtd/topics/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ Events
======

`Cloud-init`_ will fetch and apply cloud and user data configuration
upon serveral event types. The two most common events for cloud-init
upon serveral event types. The two most common events for cloud-init
are when an instance first boots and any subsequent boot thereafter (reboot).
In addition to boot events, cloud-init users and vendors are interested
in when devices are added. cloud-init currently supports the following
in when devices are added. cloud-init currently supports the following
event types:

- **BOOT_NEW_INSTANCE**: ``New instance first boot``
Expand All @@ -29,29 +29,31 @@ Datasource Event Support

All :ref:`datasources` by default support the ``BOOT_NEW_INSTANCE`` event.
Each Datasource will provide a set of events that it is capable of handling.
Datasources may not support all event types. In some cases a system
Datasources may not support all event types. In some cases a system
may be configured to allow a particular event but may be running on
a platform who's datasource cannot support the event.

Configuring Event Updates
=========================

Cloud-init has a default updates policy to handle new instance
events always. Vendors may want an instance to handle additional
events. Users have the final say and may provide update configuration
which can be used to enable or disable handling of specific events.
events always. Vendors may want an instance to handle additional
events. Users have the final say and may provide update configuration
which can be used to enable or disable handling of specific events. The
exception is first boot. Configuration will always be applied at first
boot, and the user cannot disable this.

updates
~~~~~~~
Specify update policy configuration for cloud-init to define which
events are allowed to be handled. This is separate from whether a
events are allowed to be handled. This is separate from whether a
particular platform or datasource has the capability for such events.

**scope**: *<name of the scope for event policy>*
**scope**: *<name of the scope for event policy>*

The ``scope`` value is a string which defines under which domain do the
event occur. Currently there are two known scopes: ``network`` and
``storage``. Scopes are defined by convention but arbitrary values
event occur. Currently there are two known scopes: ``network`` and
``storage``. Scopes are defined by convention but arbitrary values
can be used.

**when**: *<list of events to handle for a particular scope>*
Expand All @@ -73,7 +75,7 @@ found in the datasource.
# apply network config on every boot
updates:
network:
when: ['boot-new-instance', 'boot']
when: ['boot']
.. _Cloud-init: https://launchpad.net/cloud-init
.. vi: textwidth=78

0 comments on commit ec59faa

Please sign in to comment.