PageSourceSearch

https://terragraph.com/assets/js/c19619aa.9b2b0a09.js

js terragraph.com collected 2026-10-03 21:39:52 UTC 49,457 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4268],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>c});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function o(e,t){if(null==e)return{};var n,a,r=function(e,t){if(null==e)return{};var n,a,r={},i=Object.keys(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var d=a.createContext({}),s=function(e){var t=a.useContext(d),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=s(e.components);return a.createElement(d.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,d=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),u=s(n),c=r,g=u["".concat(d,".").concat(c)]||u[c]||m[c]||i;return n?a.createElement(g,l(l({ref:t},p),{},{components:n})):a.createElement(g,l({ref:t},p))}));function c(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,l=new Array(i);l[0]=u;var o={};for(var d in t)hasOwnProperty.call(t,d)&&(o[d]=t[d]);o.originalType=e,o.mdxType="string"==typeof e?e:r,l[1]=o;for(var s=2;s<i;s++)l[s]=n[s];return a.createElement.apply(null,l)}return a.createElement.apply(null,n)}u.displayName="MDXCreateElement"},6493:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>o,toc:()=>s});var a=n(7462),r=(n(7294),n(3905));const i={},l="Configuration Management",o={unversionedId:"developer/Configuration_Management",id:"developer/Configuration_Management",title:"Configuration Management",description:"This document describes the configuration management system.",source:"@site/../docs/developer/Configuration_Management.md",sourceDirName:"developer",slug:"/developer/Configuration_Management",permalink:"/docs/developer/Configuration_Management",draft:!1,editUrl:"https://github.com/terragraph/meta-terragraph/edit/main/docs/../docs/developer/Configuration_Management.md",tags:[],version:"current",frontMatter:{},sidebar:"developerManualSidebar",previous:{title:"Software Upgrade",permalink:"/docs/developer/Software_Upgrade"},next:{title:"Scans",permalink:"/docs/developer/Scans"}},d={},s=[{value:"Architecture",id:"architecture",level:2},{value:"Operations",id:"operations",level:3},{value:"Syncing Configuration",id:"syncing-configuration",level:3},{value:"Staged Rollout",id:"staged-rollout",level:3},{value:"Interoperability",id:"interoperability",level:3},{value:"Layered Configuration Model",id:"layered-configuration-model",level:2},{value:"Config Metadata",id:"config-metadata",level:2},{value:"Data Types",id:"data-types",level:3},{value:"Post-Config Actions",id:"post-config-actions",level:3},{value:"Structure",id:"structure",level:3},{value:"Deprecation",id:"deprecation",level:3},{value:"Read-only",id:"read-only",level:3},{value:"Preprocessing",id:"preprocessing",level:3},{value:"E2E Configuration",id:"e2e-configuration",level:2},{value:"Config Metadata",id:"config-metadata-1",level:3},{value:"Tags",id:"tags",level:4},{value:"Sync",id:"sync",level:4},{value:"Software Version Strings",id:"software-version-strings",level:2},{value:"Adding New Configuration",id:"adding-new-configuration",level:2},{value:"New Hardware",id:"new-hardware",level:3},{value:"New Configuration Fields",id:"new-configuration-fields",level:3},{value:"New Command-Line Flags",id:"new-command-line-flags",level:3},{value:"Breaking Changes",id:"breaking-changes",level:3},{value:"Resources",id:"resources",level:2}],p={toc:s};
1function m(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"configuration-management"},"Configuration Management"),(0,r.kt)("p",null,"This document describes the configuration management system."),(0,r.kt)("h2",{id:"architecture"},"Architecture"),(0,r.kt)("p",null,"Terragraph implements a centralized configuration management architecture. The\nE2E controller serves as the point of truth for all node configurations, and is\nresponsible for keeping all nodes' local copies in sync with its own view. All\nconfig changes are applied using the controller."),(0,r.kt)("p",{align:"center"},(0,r.kt)("img",{src:"/figures/config_architecture.svg",width:"500"})),(0,r.kt)("p",null,"Node configuration is a JSON-serialized Thrift structure (",(0,r.kt)("inlineCode",{parentName:"p"},"thrift::NodeConfig"),"),\nand is generally manipulated using ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/facebook/folly/blob/master/folly/docs/Dynamic.md"},"folly::dynamic"),' objects. A node\'s full\nconfiguration is computed by applying several "override" layers on top of the\nbase configuration for its software version. All configuration files are stored\nunder the ',(0,r.kt)("inlineCode",{parentName:"p"},"/data/cfg/")," directory on disk for both the E2E controller and\nTerragraph node."),(0,r.kt)("h3",{id:"operations"},"Operations"),(0,r.kt)("p",null,"The controller's ",(0,r.kt)("inlineCode",{parentName:"p"},"ConfigApp")," accepts the following ",(0,r.kt)("em",{parentName:"p"},"user operations")," on the node\nconfigurations:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"User Operation"),(0,r.kt)("th",{parentName:"tr",align:null},"Commands"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Get Node Config"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_PATHS_REQ"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Get Config Layers"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_BASE_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_FIRMWARE_BASE_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_HARDWARE_BASE_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_AUTO_NODE_OVERRIDES_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_NETWORK_OVERRIDES_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_NODE_OVERRIDES_REQ"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Set Config Overrides"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"SET_CTRL_CONFIG_NETWORK_OVERRIDES_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"SET_CTRL_CONFIG_NODE_OVERRIDES_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"MODIFY_CTRL_CONFIG_NETWORK_OVERRIDES_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"MODIFY_CTRL_CONFIG_NODE_OVERRIDES_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"CLEAR_AUTO_NODE_OVERRIDES_CONFIG"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Get Config Metadata"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_METADATA_REQ"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Get Config Actions"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_NETWORK_OVERRIDES_ACTIONS_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_NODE_OVERRIDES_ACTIONS_REQ"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"GET_CTRL_CONFIG_ACTIONS_RESULTS_REQ"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Perform Optimizations"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"TRIGGER_POLARITY_OPTIMIZATION"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"TRIGGER_GOLAY_OPTIMIZATION"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"TRIGGER_CONTROL_SUPERFRAME_OPTIMIZATION"),(0,r.kt)("br",null),(0,r.kt)("inlineCode",{parentName:"td"},"TRIGGER_CHANNEL_OPTIMIZATION"))))),(0,r.kt)("p",null,"All ",(0,r.kt)("inlineCode",{parentName:"p"},"SET")," operations on the controller are validated using the ",(0,r.kt)("em",{parentName:"p"},"config\nmetadata"),", which is a separate JSON file describing every configuration\nparameter. All ",(0,r.kt)("inlineCode",{parentName:"p"},"GET")," operations are served locally except for getting\npost-config actions, which sends out ",(0,r.kt)("inlineCode",{parentName:"p"},"GET_MINION_CONFIG_ACTIONS_REQ")," messages to\nminions to request the actions that they would take for a given configuration\nchange; responses can then be polled from the controller. Additionally, the\ncontroller handles the ",(0,r.kt)("inlineCode",{parentName:"p"},"EDIT_NODE")," command from ",(0,r.kt)("inlineCode",{parentName:"p"}
1,"TopologyApp")," to migrate a\nnode's configuration upon a node name change."),(0,r.kt)("p",null,"The controller pushes the full node configuration to a minion's ",(0,r.kt)("inlineCode",{parentName:"p"},"ConfigApp"),"\nthrough the ",(0,r.kt)("inlineCode",{parentName:"p"},"SET_MINION_CONFIG_REQ")," command. The minion updates its copy of\nthe configuration on disk (",(0,r.kt)("inlineCode",{parentName:"p"},"/data/cfg/node_config.json"),") and takes any required\npost-config actions. These actions are determined by taking the\n",(0,r.kt)("em",{parentName:"p"},"JSON difference")," between the current and new node configurations, then applying\nall actions defined in the local metadata file."),(0,r.kt)("p",null,"Note that much of the functionality described above is implemented within helper\nclasses:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"ConfigHelper")," (",(0,r.kt)("em",{parentName:"li"},"e2e-controller"),") manages reading, modifying, and writing to\nall separate configuration layers."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"NodeConfigWrapper")," (",(0,r.kt)("em",{parentName:"li"},"e2e-common"),") manages reading and writing the full node\nconfiguration."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"ConfigMetadata")," (",(0,r.kt)("em",{parentName:"li"},"e2e-common"),") manages reading the config metadata file,\nvalidating configuration, and extracting post-config actions."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"ConfigUtil")," (",(0,r.kt)("em",{parentName:"li"},"e2e-common"),") contains miscellaneous static utilities, such as\nversion string parsing.")),(0,r.kt)("h3",{id:"syncing-configuration"},"Syncing Configuration"),(0,r.kt)("p",null,"To keep config in sync, nodes send an MD5 hash of their local configuration to\nthe controller in their periodic status reports (",(0,r.kt)("inlineCode",{parentName:"p"},"STATUS_REPORT"),") from\n",(0,r.kt)("inlineCode",{parentName:"p"},"StatusApp"),". The controller's ",(0,r.kt)("inlineCode",{parentName:"p"},"ConfigApp")," periodically iterates over all\nreceived status reports and looks for mismatches with its computed\nconfiguration. For each mismatch, the controller will push a new node\nconfiguration to the minion, as described above. There is a fixed interval\nbetween consecutive pushes to the same node, as a protective measure in case the\nnew configuration causes a fatal error on the node; this delay is reset when the\ncontroller receives a configuration change."),(0,r.kt)("p",null,'The automatic config sync can be disabled by "un-managing" the network or\nspecific nodes via a special boolean configuration field\n',(0,r.kt)("inlineCode",{parentName:"p"},"sysParams.managedConfig"),". This may be needed temporarily for testing purposes."),(0,r.kt)("h3",{id:"staged-rollout"},"Staged Rollout"),(0,r.kt)("p",null,"The controller's ",(0,r.kt)("inlineCode",{parentName:"p"},"ConfigApp")," contains an optional scheduling algorithm, similar\nto that of ",(0,r.kt)("inlineCode",{parentName:"p"},"UpgradeApp"),", to roll out configuration changes that affect multiple\nnodes in batches (e.g. when changing network overrides). This protects against\nnetwork-wide service disruption and network isolation, but will delay the\npropagation of configuration changes."),(0,r.kt)("p",null,"To enable this staging algorithm, set the ",(0,r.kt)("inlineCode",{parentName:"p"},"config_staged_rollout_enabled")," flag\non the controller; by default, all config updates to nodes are sent\nsimultaneously."),(0,r.kt)("h3",{id:"interoperability"},"Interoperability"),(0,r.kt)("p",null,"When the controller finds a node with an unrecognized hardware board ID and\nversion, it will disable config updates for that node because unknown\nhardware-specific configs may exist."),(0,r.kt)("p",null,"Additionally, if the ",(0,r.kt)("inlineCode",{parentName:"p"},"unknown_hw_queries_enabled")," flag is set (default on), the\ncontroller will send ",(0,r.kt)("inlineCode",{parentName:"p"},"GET_MINION_BASE_CONFIG")," queries to these nodes\n(rate-limited per node and board ID). In response, each minion will return a\n",(0,r.kt)("inlineCode",{parentName:"p"},"MINION_BASE_CONFIG")," message containing its latest base configuration and\nmetadata. The controller merges these minion structures into its existing config\ndata (non-persistently), and can afterwards re-enable config updates for all\nnodes with the same hardware board ID. Any conflicting metadata entries are\ndropped, and unknown post-config actions are parsed but ignored."),(0,r.kt)("h2",{id:"layered-configuration-model"},"Layered Configuration Model"),(0,r.kt)("p",null,'Terragraph follows a layered configuration model, with a node\'s "full"\nconfiguration computed as the union of all layers in the following order:'),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"Base configuration")," - The default configuration, which is tied to a\nspecific software version and is included as part of the image. The controller\nfinds the closest match for a node's software version string, and falls back\nto the latest if no match was found.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/etc/e2e_config/base_versions/<version>.json\n")),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"Firmware-specific base configuration")," - The default configuration tied to a\nspecific firmware version, which is also included as part of the image. Values\nare applied on top of the initial base configuration layer.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/etc/e2e_config/base_versions/fw_versions/<version>.json\n")),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"Hardware-specific base configuration")," - The default configuration tied to a\nspecific hardware type, which is also included as part of the image. Each\nhardware type supplies configuration that changes with software versions.\nValues are applied on top of the firmware base configuration layer. The\nmapping between hardware types and hardware board IDs is defined in\n",(0,r.kt)("inlineCode",{parentName:"li"}
1,"/etc/e2e_config/base_versions/hw_versions/hw_types.json"),".")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/etc/e2e_config/base_versions/hw_versions/<hw_type>/<version>.json\n")),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"Automated node overrides")," - Contains any config parameters for specific\nnodes that were automatically set by the E2E controller.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/data/cfg/auto_node_config_overrides.json\n")),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"Network overrides")," - Contains any config parameters that should be\nuniformly overridden across the entire network. This takes precedence over the\nbase configuration and automatic overrides.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/data/cfg/network_config_overrides.json\n")),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"Node overrides")," - Contains any config parameters that should be overridden\nonly on specific nodes (e.g. PoP nodes). This takes precedence over the\nnetwork overrides.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/data/cfg/node_config_overrides.json\n")),(0,r.kt)("p",null,"The E2E controller manages and stores the separate config layers. Terragraph\nnodes have no knowledge of these layers, ",(0,r.kt)("em",{parentName:"p"},"except")," the base configuration on the\nimage. The nodes will copy the latest base version (via natural sort order) if\nthe configuration file on disk is missing or corrupt."),(0,r.kt)("h2",{id:"config-metadata"},"Config Metadata"),(0,r.kt)("p",null,"A separate config metadata file (",(0,r.kt)("inlineCode",{parentName:"p"},"/etc/e2e_config/config_metadata.json"),") is used\nfor per-config documentation, verification, and actions. Every configuration\nfield has a corresponding entry in this file."),(0,r.kt)("h3",{id:"data-types"},"Data Types"),(0,r.kt)("p",null,"Whereas node configuration is a JSON-serialized Thrift structure, the metadata\nis freeform JSON that is recursively parsed into C++ structures defined in\n",(0,r.kt)("inlineCode",{parentName:"p"},"ConfigMetadata.h"),"."),(0,r.kt)("p",null,"Each root metadata entry is parsed into the ",(0,r.kt)("inlineCode",{parentName:"p"},"CfgParamMetadata")," structure, which\ninherits the ",(0,r.kt)("inlineCode",{parentName:"p"},"CfgRecursiveParam"),' base type. A root entry is identified by the\npresence of its required properties ("desc", "type", "action"). Depending on the\nassociated configuration field\'s data type, the root structure may or must also\ncontain a type-specific sub-structure (e.g. required for maps and objects). The\ntable below shows the association between all possible data types (defined in\nthe Thrift enum ',(0,r.kt)("inlineCode",{parentName:"p"},"thrift::CfgParamType"),"), the name of their type-specific field,\nand the C++ structure for the field:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Type"),(0,r.kt)("th",{parentName:"tr",align:null},"Field"),(0,r.kt)("th",{parentName:"tr",align:null},"Sub-Structure"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"INTEGER")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("em",{parentName:"td"},"intVal")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"CfgIntegerParam"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"FLOAT")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("em",{parentName:"td"},"floatVal")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"CfgFloatParam"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"STRING")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("em",{parentName:"td"},"strVal")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"CfgStringParam"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"BOOLEAN")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("em",{parentName:"td"},"boolVal")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"CfgBooleanParam"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"MAP")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("em",{parentName:"td"},"mapVal")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"CfgMapParam"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"OBJECT")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("em",{parentName:"td"},"objVal")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"CfgObjectParam"))))),(0,r.kt)("p",null,"Note that these data types constitute all JSON types ",(0,r.kt)("em",{parentName:"p"},"except arrays"),". Arrays are\nnot supported in the configuration model due to ambiguity in layering. Also\nnote that map keys must be strings, in accordance with JSON specifications."),(0,r.kt)("h3",{id:"post-config-actions"},"Post-Config Actions"),(0,r.kt)("p",null,"A post-config action is associated with each metadata entry, determining what\nthe minion should do when the configuration field is changed. These are defined\nin the Thrift enum ",(0,r.kt)("inlineCode",{parentName:"p"},"thrift::CfgAction"),":"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Action"),(0,r.kt)("th",{parentName:"tr",align:null},"Description"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"NO_ACTION")),(0,r.kt)("td",{parentName:"tr",align:null},"Do nothing")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"REBOOT")),(0,r.kt)("td",{parentName:"tr",align:null},"Reboot the node")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_MINION")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart e2e_minion")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_STATS_AGENT")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart stats_agent")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_LOGTAIL")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart logtail")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_ROUTING")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart openr and pop_config")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_SQUIRE")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart squire")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"REDO_POP_CONFIG")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart pop_config and fib_nss")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_FLUENTD_AGENT")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart fluent-bit")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RELOAD_DNS_SERVERS")),(0,r.kt)("td",{parentName:"tr",align:null},"Reload nameservers listed in /etc/resolv.conf")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RELOAD_NTP_CONFIG")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart NTP service with updated config")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RELOAD_RSYSLOG_CONFIG")),(0,r.kt)("td",{parentName:"tr",align:null},"Rewrite rsyslog.conf and restart rsyslogd")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RELOAD_SSHD_CA_KEYS")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart sshd with updated CA keys file")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_KEA")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart kea (dhcp)")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"SYNC_LINK_MONITOR")),(0,r.kt)("td",{parentName:"tr",align:null},"Sync link metrics with openr LinkMonitor")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"INJECT_KVSTORE_KEYS")),(0,r.kt)("td",{parentName:"tr",align:null},"Sync keys with openr KvStore")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"UPDATE_FIREWALL")),(0,r.kt)("td",{parentName:"tr",align:null},"Rewrite firewall(ip6tables) rules")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"UPDATE_LINK_METRICS")),(0,r.kt)("td",{parentName:"tr",align:null},"Update MCS-based link metric config for openr")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"UPDATE_GLOG_LEVEL")),(0,r.kt)("td",{parentName:"tr",align:null},"Update glog's VLOG level")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RELOAD_FIRMWARE")),(0,r.kt)("td",{parentName:"tr",align:null},"Reload firmware, usually by restarting e2e_minion")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"SET_FW_PARAMS")),(0,r.kt)("td",{parentName:"tr",align:null},"Dynamically change this firmware parameter")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"SET_FW_PARAMS_SYNC_OR_RELOAD_FIRMWARE")),(0,r.kt)("td",{parentName:"tr",align:null},"Dynamically change this firmware parameter at a specific BWGD index, or reload firmware if not possible")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"SET_FW_STATS_CONFIG")),(0,r.kt)("td",{parentName:"tr",align:null},"Set firmware stats config")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"SET_AIRTIME_PARAMS")),(0,r.kt)("td",{parentName:"tr",align:null},"Dynamically change link airtime allocation")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_UDP_PING_SERVER")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart UDP ping server")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RELOAD_SSHD")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart SSH daemon")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"UPDATE_CHANNEL_ASSIGNMENT")),(0,r.kt)("td",{parentName:"tr",align:null},"Reassign channels across topology")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_SNMP")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart NET-SNMP and TG snmp agent daemons")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_WEBUI")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart WebUI (HTTP server)")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RELOAD_TUNNEL_CONFIG")),(0,r.kt)("td",{parentName:"tr",align:null}
1,"Reload tunnel configuration")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RELOAD_VPP_CONFIG_AND_MONITOR")),(0,r.kt)("td",{parentName:"tr",align:null},"Run vpp_chaperone to re-apply VPP config and restart monitor services")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"UPDATE_ZONE")),(0,r.kt)("td",{parentName:"tr",align:null},"Redo POP config if required or do nothing")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RELOAD_TOPOLOGY_NAMES")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart stats_agent and fluent-bit services")))),(0,r.kt)("p",null,"It is possible that multiple actions will be triggered for a single parameter,\ne.g. if an object property defines an action different from the root entry. The\nobject property action does ",(0,r.kt)("em",{parentName:"p"},"not")," simply replace the root entry action. Note\nthat ",(0,r.kt)("inlineCode",{parentName:"p"},"SET_FW_PARAMS")," and related actions are a special case and will ",(0,r.kt)("em",{parentName:"p"},"only")," take\neffect when set on an object property (not on a root entry)."),(0,r.kt)("p",null,"The code handling each action is located in the minion's\n",(0,r.kt)("inlineCode",{parentName:"p"},"ConfigApp::performNodeActions()")," method. The minion is responsible for applying\nactions in a sensible order, e.g. rebooting or restarting minion last."),(0,r.kt)("h3",{id:"structure"},"Structure"),(0,r.kt)("p",null,"Each root metadata entry must include these fields:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"desc:")," A string description of the field"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"action:")," The post-config action (",(0,r.kt)("inlineCode",{parentName:"li"},"thrift::CfgAction"),")"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"type:")," The data type of the field (",(0,r.kt)("inlineCode",{parentName:"li"},"thrift::CfgParamType"),")")),(0,r.kt)("p",null,"Type-specific constraints can be defined using the previously-listed\nsub-structures:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"intVal"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"allowedRanges:")," An array of allowed ranges, where each range is a\ntwo-element integer array ","[min, max]"," (inclusive)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"allowedValues:")," An array of allowed integer values, separate from\nallowedRanges (e.g. for special values like -1)"))),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"floatVal"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"allowedRanges:")," An array of allowed ranges, where each range is a\ntwo-element floating-point array ","[min, max]"," (inclusive)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"allowedValues:")," An array of allowed floating-point values, separate\nfrom allowedRanges"))),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"strVal"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"regexMatches:")," A regular expression string which the value must match"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"intRanges:")," An array of allowed integer ranges (like above); the string\nvalue is parsed as an integer"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"floatRanges:")," An array of allowed floating-point ranges (like above);\nthe string value is parsed as a floating-point number"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"allowedValues:")," An array of allowed string values, separate from\nregexMatches"))),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"boolVal"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"n/a")))),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"objVal"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"properties:")," A map of string property names to objects with the\nfollowing fields:",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"}
1,"desc:")," A string description of the object property"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"action:")," An ",(0,r.kt)("em",{parentName:"li"},"additional")," post-config action (",(0,r.kt)("inlineCode",{parentName:"li"},"thrift::CfgAction"),")"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"required:")," Whether defining this property is required (default\nfalse)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"type:")," The data type of the property value (",(0,r.kt)("inlineCode",{parentName:"li"},"thrift::CfgParamType"),")"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"intVal, floatVal, strVal, boolVal, objVal, mapVal")))))),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"mapVal"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"type:")," The data type of the map values (enum, see above)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"intVal, floatVal, strVal, boolVal, objVal, mapVal"))))),(0,r.kt)("h3",{id:"deprecation"},"Deprecation"),(0,r.kt)("p",null,'A configuration field can be marked as "deprecated" by setting the optional\nproperty ',(0,r.kt)("inlineCode",{parentName:"p"},'"deprecated": true')," on any root or object-property metadata entry.\nDeprecated configuration fields will still be initially read from disk, but only\nallow ",(0,r.kt)("inlineCode",{parentName:"p"},"GET")," operations during runtime."),(0,r.kt)("h3",{id:"read-only"},"Read-only"),(0,r.kt)("p",null,'A configuration field can be marked as "read-only" by setting the optional\nproperty ',(0,r.kt)("inlineCode",{parentName:"p"},'"readOnly": true')," on any root or object-property metadata entry.\nRead-only configuration fields will still be initially read from disk, but only\nallow ",(0,r.kt)("inlineCode",{parentName:"p"},"GET")," operations during runtime."),(0,r.kt)("h3",{id:"preprocessing"},"Preprocessing"),(0,r.kt)("p",null,'The config metadata parser supports a simple form of macro expansion. Macro\nexpansion blocks are denoted with a "copy-block" marker (',(0,r.kt)("inlineCode",{parentName:"p"},"__copy_block__"),"),\nwhich is a normal JSON property that takes as a string value the absolute\nlocation of the target block to copy (with all properties delimited by a\nperiod). The target JSON value will be copied into the source location (i.e.\nwhere copy-block was used). Any other properties defined within the original\nobject with copy-block will ",(0,r.kt)("em",{parentName:"p"},"override")," those same properties in the copied\ntarget block."),(0,r.kt)("p",null,'In the example below, the "mcs" block under "radioParamsBase" will be copied\ninto "linkParamsBase", but with the action "SET_FW_PARAMS" instead of\n"RESTART_MINION":'),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-json"},'{\n  "linkParamsBase": {\n    "desc": "Link parameters",\n    "action": "NO_ACTION",\n    "type": "OBJECT",\n    "objVal": {\n      "properties": {\n        "fwParams": {\n          "desc": "Firmware parameters for links",\n          "action": "RESTART_MINION",\n          "type": "OBJECT",\n          "objVal": {\n            "properties": {\n              "mcs": {\n                "__copy_block__": "radioParamsBase.objVal.properties.fwParams.objVal.properties.mcs",\n                "action": "SET_FW_PARAMS"\n              }\n            }\n          }\n        }\n      }\n    }\n  },\n  "radioParamsBase": {\n    "desc": "Radio parameters",\n    "action": "NO_ACTION",\n    "type": "OBJECT",\n    "objVal": {\n      "properties": {\n        "fwParams": {\n          "desc": "Firmware parameters for radios",\n          "action": "RESTART_MINION",\n          "type": "OBJECT",\n          "objVal": {\n            "properties": {\n              "mcs": {\n                "desc": "MCS used by transmitter (1-12: Static MCS, 35: Joint LA-TPC)",\n                "type": "INTEGER",\n                "intVal": {\n                  "allowedRanges": [[1, 12]],\n                  "allowedValues": [35]\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n')),(0,r.kt)("p",null,"The current implementation handles circular- and self-references (throw error),\nas well as dependencies (parse with no order requirement)."),(0,r.kt)("h2",{id:"e2e-configuration"},"E2E Configuration"),(0,r.kt)("p",null,"The controller and aggregator are configured using separate configuration files\nwith a 
1similar overall design as the node configuration, and are managed by the\n",(0,r.kt)("inlineCode",{parentName:"p"},"E2EConfigWrapper"),' class. The configuration model does not have any layers (or\n"base" configuration), but the user operations on these configurations are\notherwise the same as for nodes.'),(0,r.kt)("p",null,"These E2E configuration files are shown in the table below."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Thrift Structure"),(0,r.kt)("th",{parentName:"tr",align:null},"Configuration File"),(0,r.kt)("th",{parentName:"tr",align:null},"Metadata File"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"thrift::ControllerConfig")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"/data/cfg/controller_config.json")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"/etc/e2e_config/controller_config_metadata.json"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"thrift::AggregatorConfig")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"/data/cfg/aggregator_config.json")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"/etc/stats_config/aggregator_config_metadata.json"))))),(0,r.kt)("p",null,"The following actions are supported in the E2E configuration files:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Action"),(0,r.kt)("th",{parentName:"tr",align:null},"Description"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"NO_ACTION")),(0,r.kt)("td",{parentName:"tr",align:null},"Do nothing")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"REBOOT")),(0,r.kt)("td",{parentName:"tr",align:null},"Send SIGTERM to the process")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"RESTART_STATS_AGENT")),(0,r.kt)("td",{parentName:"tr",align:null},"Restart stats_agent")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"UPDATE_GLOG_LEVEL")),(0,r.kt)("td",{parentName:"tr",align:null},"Update glog's VLOG level")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"UPDATE_GFLAG")),(0,r.kt)("td",{parentName:"tr",align:null},"Reload associated gflag value")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"UPDATE_SCAN_CONFIG")),(0,r.kt)("td",{parentName:"tr",align:null},"Update scan configuration")))),(0,r.kt)("p",null,'The E2E configuration structures contain a special "flags" map, which defines\ncommand-line flags to be passed into the E2E controller and NMS aggregator on\nstartup. The systemd service scripts will pass the flags, and utilize the\n',(0,r.kt)("inlineCode",{parentName:"p"},"config_print_flags")," script which reads a configuration file and returns the\ncommand-line flags as a formatted string."),(0,r.kt)("h3",{id:"config-metadata-1"},"Config Metadata"),(0,r.kt)("p",null,"Certain metadata properties are specific to E2E configuration, and are described\nin the sections below."),(0,r.kt)("h4",{id:"tags"},"Tags"),(0,r.kt)("p",null,"Descriptive tags can be set on any root or object-property metadata entry via\nthe optional ",(0,r.kt)("inlineCode",{parentName:"p"},'"tag"')," string property. Tags are purely informational."),(0,r.kt)("h4",{id:"sync"},"Sync"),(0,r.kt)("p",null,"Controller configuration is partially synced between peers under High\nAvailability mode (refer to ",(0,r.kt)("a",{parentName:"p",href:"/docs/developer/High_Availability"},"High Availability"),' for\nfurther details). Configuration fields must be marked "syncable" by setting the\noptional property ',(0,r.kt)("inlineCode",{parentName:"p"},'"sync": true')," on any root or object-property metadata entry.\nIn general, only network-level configuration is synced, and host-level\nconfiguration is not synced."),(0,r.kt)("h2",{id:"software-version-strings"},"Software Version Strings"),(0,r.kt)("p",null,"Selection of the correct ",(0,r.kt)("em",{parentName:"p"},"base configuration")," and ",(0,r.kt)("em",{parentName:"p"},"hardware-specific base\nconfiguration")," is determined by each node's software version string, contained\nin the file ",(0,r.kt)("inlineCode",{parentName:"p"},"/etc/tgversion"),"."),(0,r.kt)("p",null,'Optionally, "major" and "minor" version numbers are extracted from version\nstrings that contain one of the following substrings:'),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"[...] RELEASE_M<major> [...]\n[...] RELEASE_M<major>_<minor> [...]\n")),(0,r.kt)("p",null,"For example, the string below represents major version 60, minor version 7:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"Facebook Terragraph Release RELEASE_M60_7-0-g5bec98eb9 michaelcallahan@devvm1112 2020-12-04T15:49:48\n")),(0,r.kt)("p",null,"If major and minor versions are present, they are used to compare different\nsoftware versions. Otherwise, string-type comparisons are done on the full\nversion string. Order is important when determining the latest known\nconfiguration, for example during node startup where the initial node\nconfiguration file is generated if absent (via ",(0,r.kt)("inlineCode",{parentName:"p"},"/usr/sbin/config_read_env"),').\nThere is an assumption that the "latest" base configuration file present on a\nnode\'s filesystem is the one it should use locally.'),(0,r.kt)("h2",{id:"adding-new-configuration"},"Adding New Configuration"),(0,r.kt)("p",null,"The sections below describe the steps required to write new configuration."),(0,r.kt)("h3",{id:"new-hardware"},"New Hardware"),(0,r.kt)("p",null,"Hardware-specific configuration is applied based on a ",(0,r.kt)("em",{parentName:"p"},"hardware board ID"),",\n",(0,r.kt)("em",{parentName:"p"},"hardware type"),", and ",(0,r.kt)("em",{parentName:"p"},"software version"),"."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"The ",(0,r.kt)("em",{parentName:"li"},"hardware board ID")," is defined by the value of ",(0,r.kt)("inlineCode",{parentName:"li"},"HW_BOARD_ID")," in the node\ninfo file (",(0,r.kt)("inlineCode",{parentName:"li"},"/var/run/node_info"),"). This file is generated by a startup script\n",(0,r.kt)("inlineCode",{parentName:"li"},"gen_node_info_file.sh")," which must be modified for new hardware platforms. See\n",(0,r.kt)("a",{parentName:"li",href:"/docs/developer/Service_Scripts#service-scripts-environment-variables"},"Service Scripts"),"\nfor more details."),(0,r.kt)("li",{parentName:"ul"},"The ",(0,r.kt)("em",{parentName:"li"},"hardware type")," is defined in ",(0,r.kt)("inlineCode",{parentName:"li"},"hw_types.json")," (described above),\nwhich is a mapping between board IDs and subdirectory names (within\n",(0,r.kt)("inlineCode",{parentName:"li"}
1,"./base_versions/hw_versions/"),"). One hardware type can represent multiple\nboard IDs (e.g. for related platforms with identical configuration)."),(0,r.kt)("li",{parentName:"ul"},"The ",(0,r.kt)("em",{parentName:"li"},"software version")," is used to match both the base configuration and\nhardware-specific configuration. If any hardware-specific configuration should\nbe applied, then a file should exist for each supported software version in\nboth ",(0,r.kt)("inlineCode",{parentName:"li"},"./base_versions/<version>.json")," and\n",(0,r.kt)("inlineCode",{parentName:"li"},"./base_versions/hw_versions/<hw_type>/<version>.json"),".")),(0,r.kt)("p",null,(0,r.kt)("em",{parentName:"p"},"For interoperability, vendors ",(0,r.kt)("strong",{parentName:"em"},"must")," define a new, unique hardware board ID\nfor each product.")),(0,r.kt)("h3",{id:"new-configuration-fields"},"New Configuration Fields"),(0,r.kt)("p",null,"All configuration fields must have the following parts:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"Definition")," - Within the appropriate Thrift structure (i.e. ",(0,r.kt)("inlineCode",{parentName:"li"},"NodeConfig"),",\n",(0,r.kt)("inlineCode",{parentName:"li"},"ControllerConfig"),", or ",(0,r.kt)("inlineCode",{parentName:"li"},"AggregatorConfig"),"), as described above."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"Metadata")," - Within the corresponding metadata file (i.e.\n",(0,r.kt)("inlineCode",{parentName:"li"},"controller_metadata.json"),", ",(0,r.kt)("inlineCode",{parentName:"li"},"controller_config_metadata.json"),", or\n",(0,r.kt)("inlineCode",{parentName:"li"},"aggregator_config_metadata.json"),")."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"Default value")," - Within the base configuration JSON file. For\nhardware-specific fields, the default value should be placed in the\nhardware-specific base configuration file instead; in these cases, an empty\nvalue is usually added to the base configuration as well (for visibility).")),(0,r.kt)("p",null,(0,r.kt)("em",{parentName:"p"},"For interoperability, vendors should follow these guidelines:")),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Choose obviously non-conflicting key names for custom fields. For example,\ninclude the company name in every field name (for top-level structures)."),(0,r.kt)("li",{parentName:"ul"},"Pick indices for new Thrift fields within unique ranges to avoid serialization\nand/or merge conflicts. For example, start at 20000 (random number) instead of\nthe next consecutive integer.")),(0,r.kt)("h3",{id:"new-command-line-flags"},"New Command-Line Flags"),(0,r.kt)("p",null,"When adding new command-line flags to the E2E controller or NMS aggregator, it\nis recommended (but not required) to add an entry to corresponding metadata\nfile. This provides better visibility (e.g. in the NMS), enables input\nvalidation, and allows post-config actions to be applied."),(0,r.kt)("p",null,"Flags for other services (e.g. E2E minion) are not supported here; consider\nusing explicit configuration fields instead."),(0,r.kt)("a",{id:"configuration-management-breaking-changes"}),(0,r.kt)("h3",{id:"breaking-changes"},"Breaking Changes"),(0,r.kt)("p",null,"For configuration changes which are not backwards compatible (e.g.\nmoving/renaming fields), it is recommended to support automatic migration\nbetween the old and new configuration versions to avoid the need for user\nintervention. This migration procedure is implemented in the script\n",(0,r.kt)("inlineCode",{parentName:"p"},"src/terragraph-e2e/lua/migrate_e2e_data.lua"),", which should be run automatically\nduring software upgrades (e.g. when using the NMS installer) and will modify\ndata files as needed. Follow instructions in this script to add additional\nmigration functions."),(0,r.kt)("h2",{id:"resources"},"Resources"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/facebook/folly/blob/master/folly/docs/Dynamic.md"},"folly::dynamic")," - Meta's C++ dynamically-typed value abstraction")))}m.isMDXComponent=!0}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.