Skip to content

Commit

Permalink
Merge pull request #586 from jbemmel/jvb-keep-modified-startup-config
Browse files Browse the repository at this point in the history
make node's existing config take preference over startup-config + `enforce-startup-config` flag
  • Loading branch information
hellt authored Aug 20, 2021
2 parents 1486e6e + 9dabfaf commit 7162e33
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 39 deletions.
2 changes: 2 additions & 0 deletions clab/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ func (c *CLab) createNodeCfg(nodeName string, nodeDef *types.NodeDefinition, idx
if err != nil {
return nil, err
}
nodeCfg.EnforceStartupConfig = c.Config.Topology.GetNodeEnforceStartupConfig(nodeCfg.ShortName)

// initialize license field
nodeCfg.License, err = c.Config.Topology.GetNodeLicense(nodeCfg.ShortName)
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions docs/manual/kinds/ceos.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ cEOS nodes have a dedicated [`config`](../conf-artifacts.md#identifying-a-lab-di
used as a startup config instead.

#### Default node configuration
When a node is defined without `config` statement present, containerlab will generate an empty config from [this template](https://github.com/srl-labs/containerlab/blob/master/nodes/ceos/ceos.cfg) and copy it to the config directory of the node.
When a node is defined without `startup-config` statement present, containerlab will generate an empty config from [this template](https://github.com/srl-labs/containerlab/blob/master/nodes/ceos/ceos.cfg) and copy it to the config directory of the node.

```yaml
# example of a topo file that does not define a custom config
Expand Down Expand Up @@ -138,7 +138,7 @@ topology:
startup-config: myconfig.conf
```

When a config file is passed via `startup-config` parameter, it will override any configuration that may have left upon lab destroy.
When a config file is passed via `startup-config` parameter it will be used during an initial lab deployment. However, a config file that might be in the lab directory of a node takes precedence over the startup-config[^3].

With such topology file containerlab is instructed to take a file `myconfig.conf` from the current working directory, copy it to the lab directory for that specific node under the `/flash/startup-config` name and mount that dir to the container. This will result in this config to act as a startup config for the node.

Expand Down Expand Up @@ -247,3 +247,4 @@ Consult your distribution's documentation for details regarding configuring cgro
[^1]: https://eos.arista.com/ceos-lab-topo/
[^2]: do not remove the template variables from the `Management0` interface, otherwise the nodes will not apply the IP address from docker IPAM service.
[^3]: if startup config needs to be enforced, either deploy a lab with `--reconfigure` flag, or use [`enforce-startup-config`](../nodes.md#enforce-startup-config) setting.
5 changes: 5 additions & 0 deletions docs/manual/nodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ Some containerized NOSes require a license to operate or can leverage a license
### startup-config
For some kinds it's possible to pass a path to a config file that a node will use on start instead of a bare config. Check documentation for a specific kind to see if `startup-config` element is supported.

Note, that if a config file exists in the lab directory for a given node, then it will take preference over the startup config passed with this setting. If it is desired to discard the previously saved config and use the startup config instead, use the `enforce-startup-config` setting or deploy a lab with the [`reconfigure`](../cmd/deploy.md#reconfigure) flag.

### enforce-startup-config
By default, containerlab will use the config file that is available in the lab directory for a given node even if the `startup config` parameter points to another file. To make a node to boot with the config set with `startup-config` parameter no matter what, set the `enforce-startup-config` to `true`.

### binds
In order to expose host files to the containerized nodes a user can leverage the bind mount capability.

Expand Down
1 change: 1 addition & 0 deletions nodes/srl/srl.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ func createSRLFiles(nodeCfg *types.NodeConfig) error {
utils.CreateDirectory(path.Join(nodeCfg.LabDir, "config"), 0777)
dst = filepath.Join(nodeCfg.LabDir, "config", "config.json")
if nodeCfg.StartupConfig != "" {
log.Debugf("GenerateConfig reading startup-config %s", nodeCfg.StartupConfig )
c, err := os.ReadFile(nodeCfg.StartupConfig)
if err != nil {
return err
Expand Down
26 changes: 17 additions & 9 deletions types/node_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ const (

// NodeDefinition represents a configuration a given node can have in the lab definition file
type NodeDefinition struct {
Kind string `yaml:"kind,omitempty"`
Group string `yaml:"group,omitempty"`
Type string `yaml:"type,omitempty"`
StartupConfig string `yaml:"startup-config,omitempty"`
Config *ConfigDispatcher `yaml:"config,omitempty"`
Image string `yaml:"image,omitempty"`
License string `yaml:"license,omitempty"`
Position string `yaml:"position,omitempty"`
Cmd string `yaml:"cmd,omitempty"`
Kind string `yaml:"kind,omitempty"`
Group string `yaml:"group,omitempty"`
Type string `yaml:"type,omitempty"`
StartupConfig string `yaml:"startup-config,omitempty"`
EnforceStartupConfig bool `yaml:"enforce-startup-config,omitempty"`
Config *ConfigDispatcher `yaml:"config,omitempty"`
Image string `yaml:"image,omitempty"`
License string `yaml:"license,omitempty"`
Position string `yaml:"position,omitempty"`
Cmd string `yaml:"cmd,omitempty"`
// list of bind mount compatible strings
Binds []string `yaml:"binds,omitempty"`
// list of port bindings
Expand Down Expand Up @@ -77,6 +78,13 @@ func (n *NodeDefinition) GetStartupConfig() string {
return n.StartupConfig
}

func (n *NodeDefinition) GetEnforceStartupConfig() bool {
if n == nil {
return false
}
return n.EnforceStartupConfig
}

func (n *NodeDefinition) GetConfigDispatcher() *ConfigDispatcher {
if n == nil {
return nil
Expand Down
13 changes: 13 additions & 0 deletions types/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,19 @@ func (t *Topology) GetNodeStartupConfig(name string) (string, error) {
return cfg, nil
}

func (t *Topology) GetNodeEnforceStartupConfig(name string) bool {
if ndef, ok := t.Nodes[name]; ok {
if ndef.GetEnforceStartupConfig() {
return true
}
if t.GetKind(t.GetNodeKind(name)).GetEnforceStartupConfig() {
return true
}
return t.GetDefaults().GetEnforceStartupConfig()
}
return false
}

func (t *Topology) GetNodeLicense(name string) (string, error) {
var license string
if ndef, ok := t.Nodes[name]; ok {
Expand Down
60 changes: 32 additions & 28 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,29 +53,30 @@ type MgmtNet struct {

// NodeConfig is a struct that contains the information of a container element
type NodeConfig struct {
ShortName string // name of the Node inside topology YAML
LongName string // containerlab-prefixed unique container name
Fqdn string
LabDir string // LabDir is a directory related to the node, it contains config items and/or other persistent state
Index int
Group string
Kind string
StartupConfig string // path to config template file that is used for startup config generation
ResStartupConfig string // path to config file that is actually mounted to the container and is a result of templation
Config *ConfigDispatcher
ResConfig string // path to config file that is actually mounted to the container and is a result of templation
NodeType string
Position string
License string
Image string
Sysctls map[string]string
User string
Entrypoint string
Cmd string
Env map[string]string
Binds []string // Bind mounts strings (src:dest:options)
PortBindings nat.PortMap // PortBindings define the bindings between the container ports and host ports
PortSet nat.PortSet // PortSet define the ports that should be exposed on a container
ShortName string // name of the Node inside topology YAML
LongName string // containerlab-prefixed unique container name
Fqdn string
LabDir string // LabDir is a directory related to the node, it contains config items and/or other persistent state
Index int
Group string
Kind string
StartupConfig string // path to config template file that is used for startup config generation
EnforceStartupConfig bool // when set to true will enforce the use of startup-config, even when config is present in the lab directory
ResStartupConfig string // path to config file that is actually mounted to the container and is a result of templation
Config *ConfigDispatcher
ResConfig string // path to config file that is actually mounted to the container and is a result of templation
NodeType string
Position string
License string
Image string
Sysctls map[string]string
User string
Entrypoint string
Cmd string
Env map[string]string
Binds []string // Bind mounts strings (src:dest:options)
PortBindings nat.PortMap // PortBindings define the bindings between the container ports and host ports
PortSet nat.PortSet // PortSet define the ports that should be exposed on a container
// container networking mode. if set to `host` the host networking will be used for this node, else bridged network
NetworkMode string
MgmtNet string // name of the docker network this node is connected to with its first interface
Expand Down Expand Up @@ -105,12 +106,15 @@ type NodeConfig struct {
// GenerateConfig generates configuration for the nodes
// out of the templ based on the node configuration and saves the result to dst
func (node *NodeConfig) GenerateConfig(dst, templ string) error {
// if startup config is not set, and the config file is already present in the node dir
// we do not regenerate the config, since we will take what was saved from the previous run
// in other words, the startup config set by a user takes preference and will trigger config generation
if utils.FileExists(dst) && (node.StartupConfig == "") {
log.Debugf("config file '%s' for node '%s' already exists and will not be generated", dst, node.ShortName)

// If the config file is already present in the node dir
// we do not regenerate the config unless EnforceStartupConfig is explicitly set to true and startup-config points to a file
// this will persist the changes that users make to a running config when booted from some startup config
if utils.FileExists(dst) && (node.StartupConfig == "" || !node.EnforceStartupConfig) {
log.Infof("config file '%s' for node '%s' already exists and will not be generated/reset", dst, node.ShortName)
return nil
} else if node.EnforceStartupConfig {
log.Infof("Startup config for '%s' node enforced: '%s'", node.ShortName, dst)
}
log.Debugf("generating config for node %s from file %s", node.ShortName, node.StartupConfig)
tpl, err := template.New(filepath.Base(node.StartupConfig)).Parse(templ)
Expand Down

0 comments on commit 7162e33

Please sign in to comment.