-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drivenets: DNOS CLI support for ondatra framework
Signed-off-by: Marius Ionescu <[email protected]>
- Loading branch information
1 parent
fc41e32
commit 09240f9
Showing
20 changed files
with
48,384 additions
and
47,357 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
# Drivenets Binding | ||
|
||
Drivenets Binding is an implementation of the Ondatra binding interface with support | ||
limited to [Config](https://pkg.go.dev/github.com/openconfig/ondatra/config) interface. | ||
|
||
|
||
## Flags | ||
|
||
Drivenets Binding integration tests requires a `--config` flag be passed that specifies device information. | ||
Running Drivenets Binding tests requires passing device credential by using `--node_cres` flag. | ||
|
||
|
||
### Device Credentials | ||
|
||
An example of credentials flags: | ||
|
||
``` | ||
--node_creds=hostname/user/pass | ||
``` | ||
|
||
An example of yaml configuration file where `id` needs to match testbed `id`: | ||
|
||
``` | ||
nodes: | ||
- id: testbed_id | ||
hostname: foo | ||
credentials: | ||
username: name | ||
password: pass | ||
``` | ||
|
||
|
||
## Running the Integration Test | ||
|
||
To execute the test, you must update config.yaml with your Drivenets device details | ||
and pass both the testbed and config files as flags to the test: | ||
|
||
``` | ||
go test github.com/openconfig/ondatra/dnbind/integration --testbed=testbed.textproto --config=config.yaml | ||
``` | ||
|
||
This repo includes an | ||
[example integration test](integration/integration_test.go) that uses the Drivenets | ||
binding, a [testbed file](integration/testbed.textproto) for that test, and a | ||
[mock configuration file](integration/config.yaml) that is matched by the | ||
testbed. | ||
|
||
## Session idle timeout | ||
|
||
CLI might disconnect if idle for prolonged period of time. | ||
One can disable session timeout be configuring: ```system login session-timeout 0```. | ||
This does not affect active CLI connection, so for intendeed behaviour one must reconnect. | ||
|
||
Sample usage: | ||
```golang | ||
dut := ondatra.DUT(t, "dut") | ||
// disable session timeout | ||
dut.Config().New(). | ||
WithDrivenetsText( | ||
`system login session-timeout 0 | ||
`). | ||
Append(t) | ||
dut := ondatra.DUT(t, "dut") | ||
/* your code goes here with disabled session timeout */ | ||
``` | ||
|
||
|
||
## Interactive Commands | ||
|
||
Operational commands requiring confirmation or user input of any kind are not supported by this API. | ||
|
||
### Disable Show commands pagination (Recommended) | ||
|
||
By running ```set cli-terminal-length 0``` as an initial step or apending ``` | no-more ``` to all show commands. | ||
Paginated output leads to command getting stuck waiting for prompt. | ||
|
||
### List of unsupported commands | ||
|
||
- ‘run monitor ...’ | ||
- Any command followed by ```‘| monitor interval’``` | ||
|
||
*Commands that exit from CLI:* | ||
- ‘exit’ | ||
- ‘quit’ | ||
|
||
*Interactive commands that initiate outbound connection and require user input:* | ||
- ‘run ssh ...’ | ||
- ‘run ipmi ...’ | ||
- ‘run start shell ...’ | ||
|
||
*Commands that require confirmation:* | ||
- GI: | ||
- ‘request system delete’ | ||
- ‘request system deploy’ | ||
- ‘request system install’ | ||
- ‘request system revert-stack’ | ||
- ‘request system target-stack ...’ | ||
- ‘request system tech-support ...’ ***can bypass using ‘force’ keyword*** | ||
|
||
- DNOS: | ||
- ‘load override golden-config' | ||
- ‘request system tech-support ...’ ***can bypass using ‘force’ keyword*** | ||
- ‘request system restart factory-default' | ||
- ‘request file copy’ | ||
- ‘request file delete’ | ||
- ‘request interface management <xxx> access-list' | ||
- ‘request system delete’ | ||
- ‘request system generate golden-config' | ||
- ‘request system ncc switchover’ | ||
- ‘request system container restart’ | ||
- ‘request system process restart’ | ||
- ‘request system process stop’ | ||
- ‘request system restart <xxx>’ | ||
- ‘request system revert-stack' | ||
- ‘request system target-stack' | ||
|
||
- Interactive configuration commands: | ||
- ‘system profile’ | ||
|
||
- Configuration commands that take plain-text password value. | ||
Using plain-text option is interactive. | ||
If you want to configure password value, pass the encrypted password value instead | ||
- ‘system aaa-server radius server password’ | ||
- ‘system login ipmi user password’ | ||
- ‘system login ncm user password’ | ||
- ‘system login user password’ | ||
- ‘system ntp authentication key-id' | ||
- ‘system snmp user authentication password’ | ||
- ‘protocols mpls traffic-engineering pcep authentication enabled password’ | ||
- ‘protocols ldp authentication md5 password’ | ||
- ‘protocols ldp neighbor <> authentication md5 password’ | ||
- ‘protocols ospf area <> interface <> authentication-key md5 key-id <> password’ | ||
- ‘protocols ospfv3 area <> authentication ipsec spi <> md5 password’ | ||
- ‘protocols ospfv3 area <> interface <> authentication ‘ | ||
- ‘system aaa-server tacacs server priority <> address <> password’ | ||
- ‘protocols bgp <> neighbor <> authentication md5 password’ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Copyright 2021 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package dnbind | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/openconfig/ondatra/dnbind/creds" | ||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
type Node struct { | ||
Id string `yaml:"id"` | ||
Hostname string `yaml:"hostname"` | ||
Vendor string `yaml:"vendor,omitempty"` | ||
HardwareModel string `yaml:"model,omitempty"` | ||
SoftwareVersion string `yaml:"version,omitempty"` | ||
Credentials creds.UserPass `yaml:"credentials,omitempty"` | ||
} | ||
|
||
// Config contains parameters to configure the Drivenets binding. | ||
type Config struct { | ||
Credentials *creds.Credentials | ||
Nodes []Node `yaml:"nodes"` | ||
} | ||
|
||
func (c *Config) String() string { | ||
return fmt.Sprintf("%+v", *c) | ||
} | ||
|
||
func (c *Config) Lookup(id string) *Node { | ||
for _, node := range c.Nodes { | ||
if node.Id == id { | ||
return &node | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// ParseCredFile parses a yaml file containing a serialized Config. | ||
func ParseConfigFile(credFile string) (*Config, error) { | ||
data, err := os.ReadFile(credFile) | ||
if err != nil { | ||
return nil, fmt.Errorf("error reading config file: %w", err) | ||
} | ||
c := &Config{ | ||
Credentials: &creds.Credentials{ | ||
Node: make(map[string]*creds.UserPass), | ||
}, | ||
} | ||
if err := yaml.Unmarshal(data, c); err != nil { | ||
return nil, fmt.Errorf("error unmarshalling config YAML: %w", err) | ||
} | ||
for _, node := range c.Nodes { | ||
c.Credentials.Node[node.Hostname] = &node.Credentials | ||
} | ||
|
||
return c, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package creds contains logic for DNOS credentials. | ||
package creds | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
// UserPass is a username/password combination. | ||
type UserPass struct { | ||
Username string `yaml:"username"` | ||
Password string `yaml:"password"` | ||
} | ||
|
||
func (up *UserPass) String() string { | ||
return fmt.Sprintf("%s/%s", up.Username, up.Password) | ||
} | ||
|
||
type Credentials struct { | ||
Node map[string]*UserPass | ||
} | ||
|
||
// Lookup returns the username/password to use for the given node name and vendor. | ||
// Returns nil if no such combination exists. This method is nil-tolerant. | ||
func (c *Credentials) Lookup(node string) *UserPass { | ||
if c != nil { | ||
if up, ok := c.Node[node]; ok { | ||
return up | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (c *Credentials) String() string { | ||
return fmt.Sprintf("%+v", *c) | ||
} | ||
|
||
// DefineFlags defines flags for allowing user-specified credentials. | ||
func DefineFlags() *Flags { | ||
flags := new(Flags) | ||
multiStringVar(&flags.nodeCreds, "node_creds", | ||
"Repeated node-specific credentials in the form 'nodeName/username/password', e.g., 'n1/admin/hunter2'") | ||
return flags | ||
} | ||
|
||
func multiStringVar(v *[]string, name, usage string) { | ||
flag.Var((*multiStringVal)(v), name, usage) | ||
} | ||
|
||
type multiStringVal []string | ||
|
||
func (v *multiStringVal) Get() any { | ||
return []string(*v) | ||
} | ||
|
||
func (v *multiStringVal) Set(s string) error { | ||
*v = append(*v, s) | ||
return nil | ||
} | ||
|
||
func (*multiStringVal) TypeDescription() string { | ||
return "repeated string" | ||
} | ||
|
||
func (v *multiStringVal) String() string { | ||
if len(*v) == 0 { | ||
return "" | ||
} | ||
return fmt.Sprint(*v) | ||
} | ||
|
||
// Flags are a collection of credentials flags. | ||
type Flags struct { | ||
nodeCreds []string | ||
} | ||
|
||
// Parse parses the flags into Credentials. | ||
func (f *Flags) Parse() (*Credentials, error) { | ||
c := new(Credentials) | ||
if len(f.nodeCreds) > 0 { | ||
c.Node = make(map[string]*UserPass, len(f.nodeCreds)) | ||
for _, nc := range f.nodeCreds { | ||
parts := strings.SplitN(nc, "/", 3) | ||
if len(parts) != 3 { | ||
return nil, fmt.Errorf("invalid node credentials: %q", nc) | ||
} | ||
c.Node[parts[0]] = &UserPass{Username: parts[1], Password: parts[2]} | ||
} | ||
} | ||
return c, nil | ||
} |
Oops, something went wrong.