Ryan Harper
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- tags: cloud-init --- # cloud-init refresh-metadata Cloud-init will grow a CLI subcommand to trigger a refresh of the current instance metadata. Cloud-init will examine the current instance datasource and re-read the vendor/user metadata and update the on-disk cache of this information. This is crudely approximated by the following sequence ``` def load_cloud_object(object_path=OBJ_PKL): print('loading object %s' % object_path) return _pkl_load(object_path) def read_metadata_service(cloud): print('Reading metadata service') return cloud._get_data() def metadata_service_results(cloud): print('Fetching network-data metadata') return ds_openstack.read_metadata_service(cloud.metadata_address) def write_cloud_object(obj): _pkl_store(obj, obj.paths.get_ipath_cur("obj_pkl")) ``` # cloud-init modules --mode=update Many cloud-init config modules are not yet ready to be invoked multiple times. Specifically many of the 'per-instance' modules will not check to see if they need to perform actions. For example, adding users will fail on a second invocation due to users already existing on the system. To help the transition of updating existing configuration modules to support being called multiple times (idempotency) we will introduce a new module mode: 'update' which will only run modules which have been modified to support this mode. The 'update' mode will will rerun any config module that has been marked idempotent. # cloud-init datasource notification polling metadata Some clouds which provide metadata via URL support long polling mode which allows a program to block/sleep while waiting for an update to the contents of the URL. Not all clouds support this sort of mode in there metadata service. As clouds indicate they have support for long polling the datasource will accept configuration to indicate the URI where cloud-init can longpoll to receieve notification that some or all of the instance metadata has changed. The long poll URI needs to support several modes. The first mode is 'simple'. The simple mode is effectively a flag that indicates to cloud-init that it can invoke the 'refresh-metadata' and trigger any of the configured subsystem command ('network', 'storage', 'modules'). The second mode allows datasoures to map specific URIs to handlers. For example a datasource might specify a URI for network configuration changes (http://169.254.169.254/openstack/2017-02-22/network_data.json) and the will only trigger cloud-init's network update mode. Datasources will have a new method/attribute to provide the URL to longpool for the built-in update types ('network', 'storage', 'modules') ``` Datasource.get_configchange_uri(configtype) """ Return a URI on which caller can read to obtain an updated configuration for 'configtype' configurations Supported 'configtype' values in ['network', 'storage', 'modules'] """ ``` In the absence of a long pollable URL, a vendor may opt to configure cloud-init to allow the hotplug events to trigger not just a network or storage config update, but to call cloud-init modules --mode=update which would apply config changes to modules which are updatable. # cloud-init hotplug --nic/--storage clouds can provide vendor configuration to enable cloud-init to be called upon the instance receiving a udev/hotplug event when a nic or a disk have been added or removed. Cloud-init would add a udev rule to invoke: * cloud-init refresh-metadata (network metadata only if separate) * cloud-init network --mode=update The network --mode=update would extract the latest network configuration data from the metadata provided and render network configuration to disk and optionally invoke distro tools to apply the updated network configuration. # cloud-init update handlers Configure cloud-init to update network settings on nic hotplug, update storage settings on disk hotplug and reconfigure modules if the datasource user or metadata changes. The 'hotplug' key will configure cloud-init to render a udev script for either 'net' or 'block' linux kernel subsystems. For each udev hook, invoke a list of handlers. The values 'network', 'storage' and 'modules' are reserved for cloud-init to invoke it's built-in handlers for updating network, storage and cloud-init config modules. Alternatively users may supply a path to a program which will be exec'ed inside the handler instead. events are of type: ['boot', 'boot-new-instance', 'udev', 'metadata-change', 'user-request'] policy: * never renders never * per-instance renders on boot-new-instance * boot renders on boot-new-instance, boot, boot-change * boot-change renders on boot if network metadata or devices changed * always renders on any event type (ideally on re-rendering if a change) ``` ## updated/suggested 2018-06-13 smoser updates: policy-version: [1] # default to 1 network: when: [never, per-instance, boot, boot-change, always] watch-url: http://..../ storage: when: update-handlers: hotplug: net: [network] storage: [storage] watchurl: datasource: [modules] ``` # enable nic hotplug to update network config and modules, don't watch datasource for changes ``` update-handlers: hotplug: net: [network, modules] ``` > [smoser] changed 'nic:' to 'net:'" # only watch datasource for changes, including network and storage 'datasource' is a special keyword which indicates that cloud-init will watch the datasource specific url for config changes. This may be the normal metadata url (e.g. http://169.254.169.254/latest) or a cloud may have configured a specific url inside the datasource for long polling. The value is a list of handlers which are called with the updated configuration. The values 'network', 'modules' and 'storage' are markers for invoking cloud-init subcommands. ``` update-handlers: watchurl: datasource: [network, modules, storage] ``` # watch a custom url and use a custom handler on change Configure a url watch to a custom URL and call a custom program. When the URL returns data it is passed to the configured binary for handling. ``` update-handlers: watchurl: http://myhost.internal/v1/config-users: [/usr/local/sbin/updateusers] ``` # configure nic hotplug to call a custom program Upon nic hotplug, invoke a custom program in the udev hook. This tool will run under udev environment and will need to read values from the environment. ``` update-handlers: hotplug: nic: [/usr/local/bin/my-network-config-changed] ``` # Triggering updates outside of hotplug Without a metadata server with polling support, clouds will need to provide some mechanism to trigger cloud-init to refresh-metadata or update-config. If configured, on hotplug (or unplug) clouds can configure cloud-init to refresh all metadata and apply changes to network, storage and modules; however in the case that change are not accompanied by a device change (update an ip address, modify ntp settings) etc, then a general notification mechanism is needed. # Existing cloud-aware hotplug/netconfig tools https://github.com/SUSE/Enceladus/tree/master/cloud-netconfig (SuSE support in EC2/Azure) https://github.com/lorengordon/ec2-net-utils/tree/master/ec2-net-utils (Amazon Linux) ---------------------- OLD # Cloud-init hotplug/event network config Initial configuration for configuring event-channel --------------------------------------------------- # ip-based (for clouds without an in-band channel) ``` reporting: 'event-channel': { 'level': 'DEBUG', 'type': 'event-channel', 'endpoint': 'http://169.254.169.254/events', 'channels': ['/custom/foobar'], } ``` # ip-based (for clouds without an in-band channel) ``` reporting: 'event-hook': { 'level': 'DEBUG', 'type': 'event-hook', > [name=Chad Smith] Bikeshed alert: reporting makes me think of logging... > Though I can't think of a better name. It seems reporting key 'event-hook' > and type are duplicated in all reporting types. Is that necessary? > > [name=Ryan Harper] it's related in that we can publish the existing > cloud-init events over the channel; The top-level key is arbitrary, > the type field determines the handler in cloud-init; Channel based > communication is bi-directional; event-hook is more of a callback; > Clouds which don't or won't listen to a result (say like pci hotplug > is today; the hypervisor injects the event but has no way of confirming > that it happened); The event-hook can be configured during first boot > telling cloud-init where to find updated metadata for particular events. > 'endpoint': 'http://169.254.169.254/events/hotplug/disk/serial/config', 'channels': ['/instance-xz/hotplug/disk/serial/config'], } ``` # in-band (for hypervisors with VMCI/vsock capability) ``` reporting: 'event-channel': { 'level': 'DEBUG', 'type': 'event-channel', 'endpoint': 'vsock://events' 'channels': ['/custom/foobar'], } ``` reporting cloud-config will generate configuration for socket-activated event-channel handler. ``` class EventChannelHandler(): """ New Reporting Handler which accepts event messages and dispatches to the appropriate handler, while also publishing events as they occur within cloud-init via the existing Reporting/Events methods. """ ``` StandAloneHandler - socket activated - supports AF_UNIX, AF_INET, AF_INET6, AF_VSOCK - Cloud-init default channel subscriptions: # event-notification/handling /cloudinit/<instance_id>/hotplug/{network, block} /cloudinit/<instance_id>/config/{get,set}/{<modulename>} # boot-time event messages /cloudinit/<instance_id>/init-local/{modulename} /cloudinit/<instance_id>/init-network/{modulename} /cloudinit/<instance_id>/modules-config/{modulename} /cloudinit/<instance_id>/modules-final/ Management Server hotplugs a NIC into an instance ------------------------------------------------- After injecting the hardware hotplug event (ACPI), Linux guests with an udev event handler will capture details from the udev-event and forward them to the local cloud-init socket as message broadcast to the instance. cloud-init will format and publish a message over the hotplug channel. ``` [ { "channel": "/cloudinit/instance-abc/hotplug/network", "clientId": "d92f88b5-487d-473b-880a-96ce4454e012", "data": { "subsystem": "net", "action": "add", "attrs": { "address": "00:11:22:33:44:55" } }, } ] ``` When the mgmt server listening to the instance hotplug channel recieves the message, this is an acknowledgement that the hardware event has occurred. It may optionally validate the information matches what was injected. The mgmt server will now push a config change to configure the newly attached network device by publishing a message to the 'config/set/network' channel (prepending cloudinit/<instance_id>), specifing a new network configuration* ``` [ { "channel": "/cloudinit/instance-abc/config/set/network", "clientId": "d92f88b5-487d-473b-880a-96ce4454e012", "data": {"network": {"version": 2, "ethernets": {"ens0": {"set-name": "ens0", "match": {"macaddress": "00:11:22:33:44:55"}, "dhcp4": true}}}}, } ] ``` * Management server is usually in the best position to determine what configuration has (or will change); therefore instead of sending the entire configuration with the update embeded, leaving the client to do config diff management, instead the client only needs to merge the config with existing settings and apply. When the client recieves a config/set/network message, the config/set/network subscription handler will consume and apply the change to the system. Management Side driven Password change -------------------------------------- A user has requested the Management server to reset a user (possibly root, or otherwise) password. After accepting the required input (username, etc.) The server will publish a message to the config/set/cc_set_password channel as follows: ``` [ { "channel": "/cloudinit/instance-abc/config/set/cc_set_password", "clientId": "d92f88b5-487d-473b-880a-96ce4454e012", "data": {"chpasswd": {"list": ["root:$6$rL..$ej..."]}}, } ] ``` The instance receives the message and invokes the subscription handler for the cc_set_password with the data payload as input. This results in re-running the cc_set_password module and results in an updated password and the following events published from the client: ``` [ { "channel": "/cloudinit/instance-abc/modules-config/cc_set_password", "clientId": "d92f88b5-487d-473b-880a-96ce4454e012", "data": [{"description": "running config-set-passwords with frequency once-per-instance", "event_type": "start", "name": "modules-config/config-set-passwords", "origin": "cloudinit", "timestamp": 1477683507.146 }] }, { "channel": "/cloudinit/instance-abc/modules-config/cc_set_password", "clientId": "d92f88b5-487d-473b-880a-96ce4454e012", "data": [{"description": "config-set-passwords ran successfully", "event_type": "finish", "name": "modules-config/config-set-passwords", "origin": "cloudinit", "result": "SUCCESS", "timestamp": 1477683507.148 }] } ] ``` # /cloudinit/<instance-id>/<stage>/<event> ``` [ { "channel": "/cloudinit/instance-abc/modules-config "clientId": "d92f88b5-487d-473b-880a-96ce4454e012", "data": [{ "description": "attempting to read from cache [check]", "event_type": "start", "name": "init-local/check-cache", "origin": "cloudinit", "timestamp": 1495494121.093 }] }, { "channel": "/cloudinit/instance-abc/modules-config "clientId": "d92f88b5-487d-473b-880a-96ce4454e012", "data": [{ "description": "no cache found", "event_type": "finish", "name": "init-local/check-cache", "origin": "cloudinit", "result": "SUCCESS", "timestamp": 1495494121.094 }] } ] ``` # Hotplug POC I've put together an inital network hotplug POC which works with: - Ubuntu Zesty image - Cloud-init deb built from chad.smith-cloudinit/unify-datasource-get-data but doesn't strictly require it, now that I know that 'networkdata' is available in OpenStack metadata service - A cloudinit-hotplug python program[A] which we'll likely want to integrate as a subcommand, which does the following: - load /var/lib/cloud/instance/obj.pkl - checks datasource for 'network_config' attr (and if it's not None) - calls datasource._get_data() method to pull and crawl metadata - extracts 'network_config' from the datasource - calls cloud.distro.apply_network_config() - exits - This script is called from /lib/udev/ifupdown-hotplug and runs - before calling "ifup" - after calling "ifdown" On an openstack bastion instance you can inject a second nic like so: - neutron port-create <name of one of your neutron subnets> - capture PORT_UUID from above command - launch an instance in the same subnet where the port is available - nova interface-attach --port-id $PORT_UUID $INSTANCE_UUID In the image, after hotplug there should be an updated: /etc/network/interfaces.d/50-cloud-init.cfg And one can query how the interface came up once you know the name (say ens9): systemctl status ifup@ens9 To unplug the nic: nova interface-detach $INSTANCE_UUID $PORT_ID TODOs ----- 1. Datasource classes should include a method for crawling their metadata; currently these are decoupled from the class itself (DataSourceOpenStack as a separate read_metadata_service method which is used inside _get_data() but it's not available on the class itself.) Ideally the hotplug code can generically call: ds.crawl_metadata() and have that return the results dictionary 2. DataSource class should allow for easier access to fetching network-metadata; on OpenStack and EC2, for example, the network metadata is found at a specific URL, so instead of crawling *all* of the metadata, just the network metadata. 3. We likely need a mirror of this w.r.t storage 4. Datasources (OpenStack for example) does not store the raw results of the metadata crawl; for example, 'networkdata' is present in the metadata service, but since it's keys are not utilized when examining DataSourceOpenStack.metadata dictionary, it's not present at all. 5. Figure out reporting/event callbacks to post success 6. Attempt this hotplug hook on alternate clouds (like AWS for ipv6 addr) A. http://paste.ubuntu.com/25516596/ General Issues to deal with --------------------------- - Async Events Hotplug events may occur in parallel (or very nearly) such that the "processing" hooks from a first event may not yet be complete before a second invocation of processing hooks. We have an option of locking(and blocking) or somehow queueing subsequent events for processing. It's possible via queue/dispatch to handle hotplugs in parallel; but that may require some logical analysis of the updated metadata. For example, while not currently feasible in OpenStack to date; the updated network configuration may be a stacked/multi-nic device, like a bridge over 3 nics (which are hotplugged serially). - Locking/Blocking In general, there shouldn't be a restriction on running in parallel since we're ingesting read-only metadata; however, it does imply that the processor of the metadata may need to "validate" the config found in the metadata to determine if subsequent action can be taken, or if it must pass. For example, if an updated configuration depends on a resource that's not yet available, cloud-init may defer any action. If cloud-init chooses to react; any actions that make modifications to the system will need to be protected and provide atomic updates. Storage POC ----------- Instead of modifying openstack layers to hack in metadata service updates when attaching a cinder block; run an instance-local HTTP service which will host the a RESTFUL response to: http://<local ip>/openstack/2017-09-12/storagedata Which would appear under the openstack metadata dict, at the same level that 'networkdata' does. storagedata will be in MAAS's Storage v1 format, defined here[1] (needs schema). In general for each openstack "block" device, we'll emit: - type: disk id: <block_uuid> path: /dev/vdc serial: <serial> model: <device model> size: 10737418240B Optionally, storage config may include additional structures to define what to *do* with the devices: The canonical example[2] which injects, formats, and mounts would look like: storage: version: 1 config: - id: <openstack block uuid> type: disk serial: <first 21 chars of block uuid> name: mysql_disk - id: <openstack block uuid>-fmt type: format fstype: ext4 volume: <openstack block uuid> - id: <openstack block uuid>-mnt type: mount path: /mysql device: <openstack block uuid>-fmt The storage config will be pulled and (for now) passed to an invocation of: curtin --config <storage cfg> block-meta custom This will be missing a few things like: - package deps install (do you have raid tools, the right mkfs) - curtin block-meta needs some help for dir paths that don't exist when called directly (instead of via commands/install.py) - only apply storage-config to items present (we already do this) but allow elements in the storage config to fail and continue to process. This allows a storage-config to apply say one disk config, and ignore config of a second whose disk is not yet present. - idempotency in the case of re-runs; - we could look at the use of preserve flag, which runs through the config, but does not modify the devices - we need to see if the fstab line we want to append is already present (and skip) ## Discussion Notes: * config options in cloud-init to allow cloud-init to subscribing and react to hardware changes for storage and networking. * config modules declare whether they are idemopotent * all idempotent modules will be re-run on metadata/vendordata recrawl * require clouds to implement a long-poll endpoint (like GCE) to allow cloud-init to determine whether metadata/vendordata needs recrawling * add cloud-init sub-command alternative to inject **cloud-init event (network|storage|metadata)-(change|add|delete)** * ## smoser ## * network can get by fairly well with a "update-when" of "never", "per-instance", "boot-change", "boot", "always" * + Hotplug-related maintenance events describing the source generating the event and datasources can setup masks to determine whether or not to react to a specific event class ``` +# the maintenance request. class MaintenanceEvent(object): NONE = 0x0 # React to no maintenance events BOOT = 0x1 # Any system boot or reboot event DEVICE_ADD = 0x2 # Any new device added DEVICE_REMOVE = 0x4 # Any device removed DEVICE_CHANGE = 0x8 # Any device metadata change ANY = 0xF # Match any defined MaintenanceEvents ```

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully