Netomata Config Generator (NCG) template files (which are commonly named with the extension .ncg) are used by ncg as templates for generating config files (or subsections of those files) for various network devices and services, using the Embedded Ruby (ERB) mechanism (which is sometimes also referred to as "eRuby").
To use ERB, you embed Ruby language syntax into your text file, and mark the Ruby language segments like this:
<% ... Ruby code ... %>so that the ERB interpreter can recognize them and apply them.
The ERB interpreter as used for ncg files recognizes the following variations of these tags:
The Ruby code is executed, but nothing is output.
The Ruby code is executed, and its resulting value is output in place of the whole <%= Ruby code %> block.
The block is treated as a comment and ignored. (This can be very useful in testing, to temporarily disable a certain block of Ruby code.)
Replaced in the output with '<%' or '%>', respectively.
<%
my_hostid = 17
(0..3).each do |i|
-%>
auto eth<%= i %>
interface eth<%= i %> inet static
address 192.168.<%= i %>.<%= my_hostid %>
netmask 255.255.255.0
<% if (i == 0) then -%>
gateway 192.168.<%= i %>.1
<% end -%>
broadcast 192.168.<%= i %>.255
<%# blank line %>
<% end -%>
When this template is processed by ERB, the result is:
auto eth0
interface eth0 inet static
address 192.168.0.17
netmask 255.255.255.0
gateway 192.168.0.1
broadcast 192.168.0.255
auto eth1
interface eth1 inet static
address 192.168.1.17
netmask 255.255.255.0
broadcast 192.168.1.255
auto eth2
interface eth2 inet static
address 192.168.2.17
netmask 255.255.255.0
broadcast 192.168.2.255
auto eth3
interface eth3 inet static
address 192.168.3.17
netmask 255.255.255.0
broadcast 192.168.3.255
Data is passed to templates through Ruby instance variables; in Ruby, instance variable names begin with a single "@" symbol).
The instance variables may be of any class, but are usually of either class "Netomata::Node" or the fundamental Ruby "String" class.
Currently, templates must agree with whatever invokes them on what variables are passed, and what the class of each of those variables is. There is no automatic checking of variable presence or type. If a template requires a variable by a particular name and/or of a particular class, then the template needs to explicitly check that it has been passed what it expects, and raise an exception if it discovers a problem.
For example, here is a template to configure an interface on a Cisco router/switch. It expects that it has been passed a variable named @interface, which is a Netomata::Node and has subkeys of "name", "target", and "active" which are strings.
<%
template_arg("@interface")
template_arg_node_key("@interface", "name")
template_arg_node_key("@interface", "target")
template_arg_node_key("@interface", "active")
-%>
interface <%= @interface["name"] %>
description IPMI <%= @interface["target"] %>
switchport access vlan 48
switchport mode access
<%= apply_idiom("shutdown", @interface, {"@active" => @interface["active"]}) %>
!
In the above example, you can also see how to apply other templates ("idioms" are templates that the ncg program expects to find somewhere in (...)!idioms relative to the current node; see the documentation for apply_idiom() elsewhere in this document) from within a template, and how to pass arguments to these other templates.
In addition to the standard Ruby library classes and methods, there are a handful of "helper" methods available for use within ncg files.
Looks for a node named node[(...)!idioms!idiom] (i.e., checks node and each of its ancestor nodes in turn for a sub-node named "idioms" which itself has a sub-sub-node named "idiom"), and applies it with optional vars passed as variables.
Variables to pass are specified as a Ruby hash, with the following syntax:
{ "@name" => @var [, ...] }
So, for example, each of the following would be a valid vars argument to
apply_idiom() (assuming the referenced variables exist):
{"@vlan" => @vlan}
{"@active" => @vlan["active"]}
{"@target" => @target, "@vlan" => @vlan}
Returns a key built by combining all the partN arguments, using "!" as a separator. For example:
buildkey("a", "b", "c")
returns
"a!b!c"
Compares two interface- or version-style names, and determines what order they should sort in. This is similar to the Ruby core String#<=> operator, but it works in a way that is more useful for comparing multi-part strings such as interface and subinterface names, version numbers, and so forth.
Returns:
To compare the arguments a and b, they are first broken into parts. Each part, in turn, is the longest-possible string of either:
Then, the corresponding parts of each argument are compared in turn, until an answer is determined, subject to the following rules:
If the corresponding parts of both arguments are equivalent by these rules, then the next pair of corresponding parts is considered, and so forth, until an answer is determined.
For example, consider a list of interfaces; sorted normally (not using compare_parts, their order would be:
compare_parts would break each of these down into the following parts (shown by spaces) for comparison:
So, sorted using compare_parts for the comparison, the list would be:
This is a more "natural" order for a list of interfaces and subinterfaces.
Returns a string that is a multi-line header suitable for placing as a comment at the beginning of a generated config file, describing what tool was used to generate the config, what target key it was generated for, what date it was generated on, what user, hostname, and directory name it was generated for, and what arguments the generating program was invoked with.
prefix, if defined, is prepended to every line of the generated header. It should be whatever marks the rest of the line as a comment in the generated config file.
before, if defined, is used as the very first line of the generated header. It should be whatever marks the beginning of a multi-line comment in the generated config file. prefix is not applied to before, so before should include prefix if necessary.
after, if defined, is used as the very last line of the generated header. It should be whatever marks the end of a multi-line comment in the generated config file. prefix is not applied to after, so after should include prefix if necessary.
For example, to generate headers suitable for a shell script, you would do
emit_header("# ", nil, nil)
which would return a header of the form
# line 1 # line 2 # ...
To generate headers for a C-style config, you would do
emit_header(" * ", "/*", " */")
which would return a header of the form
/* * line 1 * line 2 * ... */
Converts filename to a key, by
filename_to_key("../a/b/c/./d")
returns
"(..)!a!b!c!(.)!d"
ip_a, ip_b, and the result are all IP addresses represented as strings. The result is the bitwise OR of the two arguments ip_a and ip_b. For example:
ip_union("10.5.0.0", "0.0.16.34") => "10.5.16.34"
ip_union("10.5.1.0", "0.0.16.34") => "10.5.17.34"
Expands file name filename relative to another file or directory name basename, returning the expanded filename. For example:
relative_filename("/a/b/c", "d") => "/a/b/d"
relative_filename("/a/b/c", "d/e") => "/a/b/d/e"
Note: as a special case, if filename begins with "/", it is treated as an absolute filename and returned as-is, not relative to basename. For example:
relative_filename("/a/b/c", "/d/e") => "/d/e"
This method can be used at the beginning of a template to check whether a needed argument has been passed to the template as an instance variable (see the section elsewhere in this document on Passing Data to Templates). Raises a Ruby ArgumentError (which typically aborts the program) if name (which is passed to this method as a quoted string, i.e. "@interface") doesn't exist. Also raises a Ruby ArgumentError if the variable exists, but isn't of the expected Ruby class (Netomata::Node, by default).
For example, the following would check that a node named "@interfaces" had been passed into a template, and raise an error if it hadn't:
<%
template_arg("@interfaces")
-%>
This method can be used at the beginning of a template to check whether a needed argument key is defined within a node that has been passed to the template as an instance variable (see the section elsewhere in this document on Passing Data to Templates). Raises a Ruby ArgumentError (which typically aborts the program) if any of the following tests fail:
For example, the following would check that a node named "@vlan" had been passed into a template, and that it had a key named "id" of Ruby class String, raising an error if it hadn't:
<%
template_arg_node_key("@vlan", "id")
-%>
Ruby class Netomata::IPAddr is an extension of the standard Ruby IPAddr class. In addition to all the methods defined in the Ruby IPAddr class, the Netomata::IPAddr class adds a set of methods for obtaining IP netmasks in various formats.
Returns ipaddr's netmask as a string in canonical format (dotted-quad for IPv4, and ':'-separated hex for IPv6).
For example:
Returns the number of significant bits in the netmask of ipaddr.
For example:
Returns the number of significant bits in netmask, which is expressed in any of the following forms:
For example:
Returns the netmask of ipaddr as a string in CIDR notation.
For example:
Returns a netmask as a string in CIDR notation, from input netmask, which is expressed in any of the following forms:
For example:
Returns the netmask of ipaddr as a string in "dotted quad" (IPv4) notation.
For example:
Returns a netmask as a string in CIDR notation, from input netmask, which is expressed in any of the following forms:
netmask must be an IPv4 netmask (i.e., 0 to 32 bits). If netmask is not a valid IPv4 netmask, then an error is raised.
For example:
foo <% 3.times do %> x <% end %> bazThis will result in the following output, with what many would consider to be extraneous newlines:
foo x x x bazTo eliminate the extraneous newlines, simply change the closing tag for the embedded Ruby blocks from '%>' to '-%>'. That will cause the ERB interpreter to suppress the newline that would otherwise immediately follow the closing tag:
foo <% 3.times do -%> x <% end -%> bazResulting in the following output, without the extraneous newlines:
foo x x x baz
Some ERB documentation speaks of a syntax whereby lines begining with '%' are also treated as Ruby code (i.e., a line '% Ruby code' is treated the same as a line '<% Ruby code %>'). This syntax is not supported in ncg files.
ncg was originally written in 2008 by Brent Chapman of Netomata, Inc.
Please report any you find by email to bugs@netomata.com.
Brent Chapman of Netomata, Inc.
Copyright (C) 2009, 2010 Netomata, Inc. All Rights Reserved.
ncg program, neto file format, neto_table file format.
This documentation is for ncg version 0.10.x.
Netomata network description files (which are commonly named with the extension .neto) are used as input by Netomata tools such as ncg, and describe a particular network configuration.
A neto file yields a tree-structured description of a network. The tree is made up of nodes, keys, values, and strings.
A node contains a list of key/value pairs.
A value can either be a node or a string.
A key is a string that provides the name for a particular value. keys consist of a string of any printable character except whitespace, '#' (which denotes a comment), "!" (which is the separator for composite keys), "(" or ")" (which are used to mark special keys, as described below), and "{" or "}" (which are used to mark metadata keys, as described below).
A string is any string of printable characters.
A string containing [%= ... %] is treated as an ERB (Embedded Ruby) block. See the full discussion of ERB blocks below.
(Note that parsing in general, and string parsing in particular, is currently very simple-minded; for example, anything from '#' to end-of-line is going to be ignored, as is leading and trailing whitespace. A future release will include quoting and escape mechanisms to work around these limitations.)
keys may be simple keys (i.e., not containing a "!" separator), or composite keys (a series of simple keys separated by "!").
composite keys that begin with a "!" are absolute keys that are interpreted relative to the root of the current tree of nodes. composite keys that don't begin with a "!" are relative keys that are interepreted relative to the current node in the tree.
simple keys of the form ( selector ) are selector keys which do various magic with the tree of nodes and keys. The following selector keys are available:
Maps to the current node (just like "." as part of a UNIX file path). So, an absolute key of the form "!a!b!c!(.)" is the same as "!a!b!c"; if your tree is set up such that "!a!b!c" is a valid path, and the current node is "!a!b!c", then (.) relative to the current node will map to node "!a!b!c". Note that "!a!(.)!b!c", "!a!b!(.)!c", and "!a!(.)!b!(.)!c" will also all map to "!a!b!c".
Maps to the parent node of the current node (just like ".." as part of a UNIX file path). So, an absolute key of the form "!a!b!c!(..)" is the same as "!a!b"; if your tree is set up such that "!a!b!c" is a valid path, and the current node is "!a!b!c", then (..) relative to the current node will map to node "!a!b"
Maps to the ancestor of the current node which has keys that match whatever follows to the right of (...). For example, if you have a tree with nodes having keys:
! !a !a!b !a!b!c !a!b!c!d !a!b!c!d!e !a!b!c!x !a!b!c!x!y !a!b!c!x!y!z
and you are at node !a!b!c!x!y!z, then the "(...)" in relative key (...)!d!e is going to refer to node !a!b!c (which is the closest ancestor to !a!b!c!x!y!z that has sub-nodes !d!e), and thus (...)!d!e is going to refer to !a!b!c!d!e
Maps to the child node of the current node having key/value pairs that match all of the key/value pairs specified in the selector. For example, if you have a tree with nodes and keys like so:
! (node) !a (node) !a!b1 (node) !a!b1!k = "v1" (value) !a!b1!m = "x1" (value) !a!b2 (node) !a!b2!k = "v2" (value) !a!b2!m = "x2" (value)
then "!a!(k=v1)" is going to map to node "!a!b1" and "!a!(k=v2)" is going to map to node "!a!b2". Similarly, "!a!(k=v1)!m" is going to map to the string "x1" (same as "!a!b1!m"), and "!a!(k=v2)!m" is going to map to the string "x2" (same as "!a!b2!m").
In addition to the selector keys defined above, there are also special selector keys which create and operate on anonymous keys. Anonymous keys are simply numbered keys which the software creates as requested, for example when building a list of items. Anonymous keys are all of the form "@NNNNNNNNN" (i.e., "@" followed by a number).
The following selector keys operate only on anonymous keys:
Maps to the minimum anonymous key of the node. I.e., if the node has anonymous keys "@000000001", "@000000002", and "@000000003" (regardless of any non-anonymous keys it may have), then (<) will map to "@000000001".
Maps to the maximum anonymous key of the node. I.e., if the node has anonymous keys "@000000001", "@000000002", and "@000000003" (regardless of any non-anonymous keys it may have), then (>) will map to "@000000003".
Maps to the successor anonymous key of the node, which is whatever would come next after the node's current maximum anonymous key of the node. I.e., if the node has anonymous keys "@000000001", "@000000002", and "@000000003" (regardless of any non-anonymous keys it may have), then (+) will map to "@000000004". This is often used to create a new node with a new anonymous key.
All of the regular selector keys defined above also operate on anonymous keys.
You should never refer to an item directly by an anonymous key, as the anonymous key for that object might be different the next time the software runs; instead, you should use selector keys such as (key=value) to identify the node by some relevant characteristic of it. For example, if you have a list of VLANs stored under !vlans, each with an "id" and "name" attribute defined, you could identify the node for VLAN 3 (the "Lab" VLAN) by "!vlans!(id=3)" or "!vlans!(name=Lab)", rather than "!vlans!@000000009", because the "@000000009" might not refer to the same VLAN entry the next time the program runs.
Metadata keys are simple keys (i.e., not containing a "!" separator) of the form "{METADATA_KEY}". They may not be a part of a composite key (a series of simple keys separated by "!").
The following metadata keys are available:
Maps to the full name of the file currently being processed.
Maps to the basename of the file currently being processed (i.e., the last part of the filename, with all the preceding directory names removed; for example, if {FILENAME} is "sample/templates/cisco.neto", then {BASENAME} will be simply "cisco.neto").
Maps to the directory name of the file currently being processed (i.e., everything except for the last part of the filename; for example, if {FILENAME} is "sample/templates/cisco.neto", then {DIRNAME} will be "sample/templates").
A string containing [%= ... %] is treated as an ERB (Embedded Ruby) block. The block is processed through the ERB interpreter as if it were a small file containing just the block with [%= ... %] changed to <%= ... %>. The value placed in the tree for such a string is the ERB interpreter's output, not the ERB block itself.
When the ERB block is executed, the following arguments are made available to the running code as Ruby instance variables:
Note that each [%= ... %] block is run through ERB separately, as if each was a separate little file. This is not how ERB is normally used; ERB is normally applied on a whole-file basis, essentially as a preprocessor for the entire file. This difference from normal behavior is why these blocks are marked with [%= ... %] rather than the usual ERB <%= ... %>.
For more information about ERB, as well as about helper methods that are available, see the ncg file format documentation. Note that ERB files, as discussed in that documentation, are a mix of Ruby code (identified in <% ... %> segments) intermixed with template text; ERB blocks, on the other hand, are independent, self-contained blocks of Ruby code, without any template text. Keep in mind that each [%= ... %] ERB block is treated as if it were a separate file, and thus, some of the complex examples in the ncg file format documentation that show Ruby control logic spread across multiple <%= ... %> statements aren't applicable for ERB blocks.
A neto file consists of a series of lines. Lines that end with '\' are treated as if they continued on the next line; before processing continues, the current line is joined with the next, and the '\' and embedded newline characters are removed. Comments begin with a '#' symbol, and proceed to the end of the line. Leading and trailing whitespace on a line are ignored, as are blank lines. Once stripped of comments, leading whitespace, and trailing whitespace, each line must match one of the following formats:
Sets key to be string in the current node. (If string is empty or "-", then key will not be defined; this provides a way for an ERB block to say "never mind, don't set this key", by returning an empty string or "-" as its result.)
Creates a new (empty) sub-node of the current node, names the new sub-node key, and makes the new sub-node the current node (so that further lines are processed relative to the sub-node, until the matching "}"
Creates a copy of the node identified by source_key as a new sub-node of the current node, names the new sub-node key, and makes the new sub-node the current node (so that further lines are processed relative to the sub-node, until the matching "}"
Ends processing of the current sub-node, and makes the current node whatever it was before processing of the current sub-node began.
Incorporates a file (or set of files) in neto format into the tree at the current location, as if its contents appeared here in the original file. Each filename can name a specific file, or can be a filename glob pattern (in Ruby Dir.glob format, which is very similar to shell filename glob format), which will be expanded to a sorted list of filenames. If multiple filename arguments are given (either directly, or via a filename glob pattern that expands to multiple filenames), the software behaves as if all those named files had been concatenated together for processing. All filename arguments (whether specified directly or determined as a result of filename glob expansion) are interpreted relative to the current filename (the file in which the include statement appears).
Incorporates a file (or set of files) in neto_table format into the tree at the current location. Each filename can name a specific file, or can be a filename glob pattern (in Ruby Dir.glob format, which is very similar to shell filename glob format), which will be expanded to a sorted list of filenames. If multiple filename arguments are given (either directly, or via a filename glob pattern that expands to multiple filenames), the software behaves as if all those named files had been concatenated together for processing. All filename arguments (whether specified directly or determined as a result of filename glob expansion) are interpreted relative to the current filename (the file in which the include statement appears).
template directives are used to populate the tree with pointers to ERB template files that are referenced elsewhere, and used when ncg begins its config generation pass.
The directive defines two keys within the current node:
filename.ncg is interpreted relative to the current filename (the file in which the template statement appears). An error is raised if filename.ncg doesn't exist or isn't a file.
Because template works on the current node, rather than creating a new node, you cannot specify multiple filename.ncg arguments (or wildcard arguments) to a template directive; each successive file would simply end up overwriting the name and basename keys defined in the current node. To incorporate multiple templates into the tree, either use multiple template directives or use the template_dir directive.
Creates a sub-tree of nodes in the tree at the current location, based on the contents of directory dirname. Depending on the name and type of each entry in directory dirname, the following actions are taken:
filename {
template filename.ncg
}
subdir {
template_dir subdir
}
base_ip = 10.5.0.0
admin_ip = [%= ip_union(@target["(...)!base_ip"], "0.0.16.0") %]
syslog_ip = [%= ip_union(@target["(...)!admin_ip"], "0.0.0.26") %]
syslog_facility = local5
snmp_ip = [%= ip_union(@target["(...)!admin_ip"], "0.0.0.27") %]
snmp_community = public
domain = example.com
table vlans.neto_table
!templates {
template_dir templates
}
!templates!devices!routers!cisco {
# add to Cisco router device template
model = 4948-10G
default_route = [%= ip_union(@target["(...)!base_ip"], "0.0.4.9") %]
domain = [%= "mgmt." + @target["(...)!domain"] %]
}
devices!(+) < !templates!devices!routers!cisco {
name = switch-1
role = primary
# can't put this in the template, because "name" isn't defined there
ncg_output = [%= File.join(@target["{DIRNAME}"], \
"configs", (@target["name"] + ".config")) %]
}
devices!(+) < !templates!devices!routers!cisco {
name = switch-2
role = secondary
# can't put this in the template, because "name" isn't defined there
ncg_output = [%= File.join(@target["{DIRNAME}"], \
"configs", (@target["name"] + ".config")) %]
}
table interfaces.neto_table
ncg was originally written in 2008 by Brent Chapman of Netomata, Inc.
Please report any you find by email to bugs@netomata.com.
Brent Chapman of Netomata, Inc.
Copyright (C) 2009, 2010 Netomata, Inc. All Rights Reserved.
ncg program, ncg file format, neto_table file format.
This documentation is for ncg version 0.10.x.
Netomata tabular network description files (which are commonly named with the extension .neto_table) are used as input by Netomata tools such as ncg, and describe parts of a particular network configuration. They are a specialized form of input for descriptions that make most sense when represented in tabular form (for example, lists of interfaces on a particular device, and associated parameters for each interface).
Anything in a neto_table file could be expressed in a neto file instead; a neto_table file is simply a more concise and natural form of expressing some types of data.
A neto_table file consists of three types of lines: comments, header lines, and data lines. Comments begin with '#', and extend to the end of the line. Header lines are lines that begin with special characters (either '@', '+', '<', or '%'). Data lines are any lines that are neither header lines nor comments.
Header lines define a set of actions, which are applied to each data line in turn; the header lines, taken as a set, describe how to convert a given data line into entries in the Netomata tree-structured description of the network.
Lines that end with '\' are treated as if they continued on the next line; before processing continues, the current line is joined with the next, and the '\' and embedded newline characters are removed.
Data lines are broken into fields by strings of one or more tabs. All data lines must have the same number of fields, which must match the number of fields in the '%' header line (which specifies the name of each field). This has some important consequences:
Header lines take one of the following forms:
A '%' line defines the names of the fields in the data lines. As with data lines, the field names are separated by strings of one or more tabs. Each neto_table file must have exactly one '%' line. The number of fields in each data line must match the number of fields named on the '%' line. The name of each field must be unique.
Creates a new (empty) node, and names it key.
Creates a copy of the node named by source_key as new node, and names it key.
Sets key to be value. (If value is a single dash, "-", then key will not be set.)
Processes the neto format file(s) named by filename ... in the context of key. This is equivalent to the neto format construct of
key {
include filename [ ... ]
}
@ !vlans!(+)!id = %{id}
@ !vlans!(id=%{id})!active = %{active}
@ !vlans!(id=%{id})!type = %{type}
@ !vlans!(id=%{id})!vlan_ip = [%= ip_union(@target["(...)!base_ip"], "0.0.%{id}.0") %]
@ !vlans!(id=%{id})!netmask = %{netmask}
@ !vlans!(id=%{id})!name = %{name}
@ !vlans!(id=%{id})!description = %{description}
#
% id active type netmask name description
# -- ------ ---- ------- ---- -----------
2 yes switch 255.255.255.0 ISP-FW ISP-to-Firewall
3 yes switch 255.255.255.0 FW-LB Firewall-to-LoadBal
4 yes admin 255.255.255.0 LB-Rtr LoadBal-to-Router
16 yes admin 255.255.240.0 Management Management
32 yes admin 255.255.240.0 Bulk Bulk
48 yes admin 255.255.240.0 IPMI IPMI
81 yes env 255.255.255.0 Production Production
82 yes env 255.255.255.0 Pre-Prod Pre-Prod
83 yes env 255.255.255.0 Demo Demo
84 yes env 255.255.255.0 Dev Dev
85 yes env 255.255.255.0 QA QA
86 yes env 255.255.255.0 Test Test
128 yes switch 255.255.255.0 Corporate Corporate
This is equivalent to the following
neto file:
# first line of data from table !vlans!(+)!id = 2 !vlans!(id=2)!active = yes !vlans!(id=2)!type = switch !vlans!(id=2)!vlan_ip = [%= ip_union(@target["(...)!base_ip"], "0.0.2.0") %] !vlans!(id=2)!netmask = 255.255.255.0 !vlans!(id=2)!name = ISP-FW !vlans!(id=2)!description = ISP-to-Firewall # second line of data from table !vlans!(+)!id = 3 !vlans!(id=3)!active = yes !vlans!(id=3)!type = switch !vlans!(id=3)!vlan_ip = [%= ip_union(@target["(...)!base_ip"], "0.0.3.0") %] !vlans!(id=3)!netmask = 255.255.255.0 !vlans!(id=3)!name = FW-LB !vlans!(id=3)!description = Firewall-to-LoadBal # third line of data from table !vlans!(+)!id = 4 !vlans!(id=4)!active = yes !vlans!(id=4)!type = admin !vlans!(id=4)!vlan_ip = [%= ip_union(@target["(...)!base_ip"], "0.0.4.0") %] !vlans!(id=4)!netmask = 255.255.255.0 !vlans!(id=4)!name = LB-Rtr !vlans!(id=4)!description = LoadBal-to-Router # fourth line of data from table !vlans!(+)!id = 16 !vlans!(id=16)!active = yes !vlans!(id=16)!type = admin !vlans!(id=16)!vlan_ip = [%= ip_union(@target["(...)!base_ip"], "0.0.16.0") %] !vlans!(id=16)!netmask = 255.255.240.0 !vlans!(id=16)!name = Management !vlans!(id=16)!description = Management # and so forth ...
+ !devices!(name=switch-1)!interfaces!(+) < \
(...)!templates!interfaces!(name=%{type})
@ !devices!(name=switch-1)!interfaces!(>)!name = %{name}
@ !devices!(name=switch-1)!interfaces!(name=%{name})!type = %{type}
@ !devices!(name=switch-1)!interfaces!(name=%{name})!target = %{target1}
@ !devices!(name=switch-1)!interfaces!(name=%{name})!active = %{active1}
#
+ !devices!(name=switch-2)!interfaces!(+) < \
(...)!templates!interfaces!(name=%{type})
@ !devices!(name=switch-2)!interfaces!(>)!name = %{name}
@ !devices!(name=switch-2)!interfaces!(name=%{name})!type = %{type}
@ !devices!(name=switch-2)!interfaces!(name=%{name})!target = %{target2}
@ !devices!(name=switch-2)!interfaces!(name=%{name})!active = %{active2}
#
% name type target1 active1 target2 active2
# ---- ---- ------ ------- ------ -------
Gig1/1 host host-1a yes host-1b yes
Gig1/2 host host-2a yes host-2b yes
Gig1/3 host host-3a yes host-3b yes
Gig1/4 host host-4a yes host-4b yes
Gig1/25 ipmi host-1m yes host-2m yes
Gig1/26 ipmi host-3m yes host-4m yes
This is equivalent to the following
neto file:
# first data line, first set of header lines (for switch-1)
!devices!(name=switch-1)!interfaces!(+) < (...)!templates!interfaces!(name=host) {
}
!devices!(name=switch-1)!interfaces!(>)!name = Gig1/1
!devices!(name=switch-1)!interfaces!(name=Gig1/1)!type = host
!devices!(name=switch-1)!interfaces!(name=Gig1/1)!target = host-1a
!devices!(name=switch-1)!interfaces!(name=Gig1/1)!active = yes
# first data line, second set of header lines (for switch-2)
!devices!(name=switch-2)!interfaces!(+) < (...)!templates!interfaces!(name=host) {
}
!devices!(name=switch-2)!interfaces!(>)!name = Gig1/1
!devices!(name=switch-2)!interfaces!(name=Gig1/1)!type = host
!devices!(name=switch-2)!interfaces!(name=Gig1/1)!target = host-1b
!devices!(name=switch-2)!interfaces!(name=Gig1/1)!active = yes
# second data line, first set of header lines (for switch-1)
!devices!(name=switch-1)!interfaces!(+) < (...)!templates!interfaces!(name=host) {
}
!devices!(name=switch-1)!interfaces!(>)!name = Gig1/2
!devices!(name=switch-1)!interfaces!(name=Gig1/2)!type = host
!devices!(name=switch-1)!interfaces!(name=Gig1/2)!target = host-2a
!devices!(name=switch-1)!interfaces!(name=Gig1/2)!active = yes
# second data line, second set of header lines (for switch-2)
!devices!(name=switch-2)!interfaces!(+) < (...)!templates!interfaces!(name=host) {
}
!devices!(name=switch-2)!interfaces!(>)!name = Gig1/2
!devices!(name=switch-2)!interfaces!(name=Gig1/2)!type = host
!devices!(name=switch-2)!interfaces!(name=Gig1/2)!target = host-2b
!devices!(name=switch-2)!interfaces!(name=Gig1/2)!active = yes
# third data line, first set of header lines (for switch-1)
!devices!(name=switch-1)!interfaces!(+) < (...)!templates!interfaces!(name=host) {
}
!devices!(name=switch-1)!interfaces!(>)!name = Gig1/3
!devices!(name=switch-1)!interfaces!(name=Gig1/3)!type = host
!devices!(name=switch-1)!interfaces!(name=Gig1/3)!target = host-3a
!devices!(name=switch-1)!interfaces!(name=Gig1/3)!active = yes
# third data line, second set of header lines (for switch-2)
!devices!(name=switch-2)!interfaces!(+) < (...)!templates!interfaces!(name=host) {
}
!devices!(name=switch-2)!interfaces!(>)!name = Gig1/3
!devices!(name=switch-2)!interfaces!(name=Gig1/3)!type = host
!devices!(name=switch-2)!interfaces!(name=Gig1/3)!target = host-3b
!devices!(name=switch-2)!interfaces!(name=Gig1/3)!active = yes
# fourth data line, first set of header lines (for switch-1)
!devices!(name=switch-1)!interfaces!(+) < (...)!templates!interfaces!(name=host) {
}
!devices!(name=switch-1)!interfaces!(>)!name = Gig1/4
!devices!(name=switch-1)!interfaces!(name=Gig1/4)!type = host
!devices!(name=switch-1)!interfaces!(name=Gig1/4)!target = host-4a
!devices!(name=switch-1)!interfaces!(name=Gig1/4)!active = yes
# fourth data line, second set of header lines (for switch-2)
!devices!(name=switch-2)!interfaces!(+) < (...)!templates!interfaces!(name=host) {
}
!devices!(name=switch-2)!interfaces!(>)!name = Gig1/4
!devices!(name=switch-2)!interfaces!(name=Gig1/4)!type = host
!devices!(name=switch-2)!interfaces!(name=Gig1/4)!target = host-4b
!devices!(name=switch-2)!interfaces!(name=Gig1/4)!active = yes
# fifth data line, first set of header lines (for switch-1)
!devices!(name=switch-1)!interfaces!(+) < (...)!templates!interfaces!(name=ipmi) {
}
!devices!(name=switch-1)!interfaces!(>)!name = Gig1/25
!devices!(name=switch-1)!interfaces!(name=Gig1/25)!type = ipmi
!devices!(name=switch-1)!interfaces!(name=Gig1/25)!target = host-1m
!devices!(name=switch-1)!interfaces!(name=Gig1/25)!active = yes
# fifth data line, second set of header lines (for switch-2)
!devices!(name=switch-2)!interfaces!(+) < (...)!templates!interfaces!(name=ipmi) {
}
!devices!(name=switch-2)!interfaces!(>)!name = Gig1/25
!devices!(name=switch-2)!interfaces!(name=Gig1/25)!type = ipmi
!devices!(name=switch-2)!interfaces!(name=Gig1/25)!target = host-2m
!devices!(name=switch-2)!interfaces!(name=Gig1/25)!active = yes
# sixth data line, first set of header lines (for switch-1)
!devices!(name=switch-1)!interfaces!(+) < (...)!templates!interfaces!(name=ipmi) {
}
!devices!(name=switch-1)!interfaces!(>)!name = Gig1/26
!devices!(name=switch-1)!interfaces!(name=Gig1/26)!type = ipmi
!devices!(name=switch-1)!interfaces!(name=Gig1/26)!target = host-3m
!devices!(name=switch-1)!interfaces!(name=Gig1/26)!active = yes
# sixth data line, second set of header lines (for switch-2)
!devices!(name=switch-2)!interfaces!(+) < (...)!templates!interfaces!(name=ipmi) {
}
!devices!(name=switch-2)!interfaces!(>)!name = Gig1/26
!devices!(name=switch-2)!interfaces!(name=Gig1/26)!type = ipmi
!devices!(name=switch-2)!interfaces!(name=Gig1/26)!target = host-4m
!devices!(name=switch-2)!interfaces!(name=Gig1/26)!active = yes
ncg was originally written in 2008 by Brent Chapman of Netomata, Inc.
Please report any you find by email to bugs@netomata.com.
Brent Chapman of Netomata, Inc.
Copyright (C) 2009, 2010 Netomata, Inc. All Rights Reserved.
ncg program, ncg file format, neto file format.
This documentation is for ncg version 0.10.x.