Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/Azure/sonic-utilities int…
Browse files Browse the repository at this point in the history
…o rates

Conflicts:
	scripts/portstat
  • Loading branch information
volodymyrsamotiy committed Oct 20, 2020
2 parents d9adb96 + 13bd06b commit 3b46b03
Show file tree
Hide file tree
Showing 81 changed files with 299,199 additions and 537 deletions.
31 changes: 26 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
# Compiled Python files
*.pyc
scripts/aclshowc
scripts/neighbor_advertiserc
scripts/port2aliasc

# Generated by packaging
*.egg-info/
.eggs/
build/
deb_dist/
dist/
*.egg-info/
*.pyc
.cache
*.tar.gz

# Unit test coverage
.coverage
coverage.xml
htmlcov/

# Ignores for sonic-utilities-data
sonic-utilities-data/debian/*
!sonic-utilities-data/debian/changelog
!sonic-utilities-data/debian/compat
!sonic-utilities-data/debian/control
!sonic-utilities-data/debian/copyright
!sonic-utilities-data/debian/install
!sonic-utilities-data/debian/rules
sonic-utilities-data_*.deb
sonic-utilities-data_*.buildinfo
sonic-utilities-data_*.changes
68 changes: 65 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,73 @@

Command-line utilities for SONiC

## How to run unit test
```python
python2 -m py.test -v
This repository produces two packages, as follows:

### sonic-utilities

A Python wheel package, containing all the Python source code for the command-line utilities

#### Setting up a build/test environment

The sonic-utilities package depends on a number of other packages, many of which are available via PyPI, but some are part of the SONiC codebase. When building/testing the package, setuptools/pip will attempt to install the packages available from PyPI. However, you will need to manually build and install the SONiC dependencies before attempting to build or test the package.

Currently, this list of dependencies is as follows:


- libyang_1.0.73_amd64.deb
- libyang-cpp_1.0.73_amd64.deb
- python2-yang_1.0.73_amd64.deb
- python3-yang_1.0.73_amd64.deb
- redis_dump_load-1.1-py2-none-any.whl
- swsssdk-2.0.1-py2-none-any.whl
- sonic_py_common-1.0-py2-none-any.whl
- sonic_config_engine-1.0-py2-none-any.whl
- sonic_yang_mgmt-1.0-py2-none-any.whl
- sonic_yang_models-1.0-py3-none-any.whl


A convenient alternative is to let the SONiC build system configure a build enviroment for you. This can be done by cloning the [sonic-buildimage](https://github.com/Azure/sonic-buildimage) repo, building the sonic-utilities package inside the Debian Buster slave container, and staying inside the container once the build finishes. During the build process, the SONiC build system will build and install all the necessary dependencies inside the container. After following the instructions to clone and initialize the sonic-buildimage repo, this can be done as follows:

1. Configure the build environment for an ASIC type (any type will do, here we use `generic`)
```
make configure PLATFORM=generic
```

2. Build the sonic-utilities Python wheel package inside the Buster slave container, and tell the build system to keep the container alive when finished
```
make NOJESSIE=1 NOSTRETCH=1 KEEP_SLAVE_ON=yes target/python-wheels/sonic_utilities-1.2-py2-none-any.whl
```

3. When the build finishes, your prompt will change to indicate you are inside the slave container. Change into the `src/sonic-utilities/` directory
```
user@911799f161a0:/sonic$ cd src/sonic-utilities/
```

4. You can now make changes to the sonic-utilities source and build the package or run unit tests with the commands below. When finished, you can exit the container by calling `exit`.

#### To build

```
python2 setup.py bdist_wheel
```

#### To run unit tests

```
python2 setup.py test
```


### sonic-utilities-data

A Debian package, containing data files needed by the utilities (bash_completion files, Jinja2 templates, etc.)

#### To build

Instructions for building the sonic-utilities-data package can be found in [sonic-utilities-data/README.md](https://github.com/Azure/sonic-utilities/blob/master/sonic-utilities-data/README.md)

---

## Contribution guide

All contributors must sign a contribution license agreement (CLA) before contributions can be accepted. This process is now automated via a GitHub bot when submitting new pull request. If the contributor has not yet signed a CLA, the bot will create a comment on the pull request containing a link to electronically sign the CLA.
Expand Down
4 changes: 3 additions & 1 deletion acl_loader/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,9 @@ def convert_l2(self, table_name, rule_idx, rule):
def convert_ip(self, table_name, rule_idx, rule):
rule_props = {}

if rule.ip.config.protocol or rule.ip.config.protocol == 0: # 0 is a valid protocol number
# FIXME: 0 is a valid protocol number, but openconfig seems to use it as a default value,
# so there isn't currently a good way to check if the user defined proto=0 or not.
if rule.ip.config.protocol:
if self.ip_protocol_map.has_key(rule.ip.config.protocol):
rule_props["IP_PROTOCOL"] = self.ip_protocol_map[rule.ip.config.protocol]
else:
Expand Down
162 changes: 162 additions & 0 deletions config/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#!/usr/bin/env python

import click

import utilities_common.cli as clicommon

#
# 'console' group ('config console ...')
#
@click.group('console')
def console():
"""Console-related configuration tasks"""
pass

#
# 'console add' group ('config console add ...')
#
@console.command('add')
@clicommon.pass_db
@click.argument('linenum', metavar='<line_number>', required=True, type=click.IntRange(0, 65535))
@click.option('--baud', '-b', metavar='<baud>', required=True, type=click.INT)
@click.option('--flowcontrol', '-f', metavar='<flow_control>', required=False, is_flag=True)
@click.option('--devicename', '-d', metavar='<device_name>', required=False)
def add_console_setting(db, linenum, baud, flowcontrol, devicename):
"""Add Console-realted configuration tasks"""
config_db = db.cfgdb

table = "CONSOLE_PORT"
dataKey1 = 'baud_rate'
dataKey2 = 'flow_control'
dataKey3 = 'remote_device'

ctx = click.get_current_context()
data = config_db.get_entry(table, linenum)
if data:
ctx.fail("Trying to add console port setting, which is already exists.")
else:
console_entry = { dataKey1: baud }
console_entry[dataKey2] = "1" if flowcontrol else "0"

if devicename:
if isExistingSameDevice(config_db, devicename, table):
ctx.fail("Given device name {} has been used. Please enter a valid device name or remove the existing one !!".format(devicename))
console_entry[dataKey3] = devicename

config_db.set_entry(table, linenum, console_entry)


#
# 'console del' group ('config console del ...')
#
@console.command('del')
@clicommon.pass_db
@click.argument('linenum', metavar='<line_number>', required=True, type=click.IntRange(0, 65535))
def remove_console_setting(db, linenum):
"""Remove Console-related configuration tasks"""
config_db = db.cfgdb

table = "CONSOLE_PORT"

data = config_db.get_entry(table, linenum)
if data:
config_db.mod_entry(table, linenum, None)
else:
ctx = click.get_current_context()
ctx.fail("Trying to delete console port setting, which is not present.")

#
# 'console remote_device' group ('config console remote_device ...')
#
@console.command('remote_device')
@clicommon.pass_db
@click.argument('linenum', metavar='<line_number>', required=True, type=click.IntRange(0, 65535))
@click.argument('devicename', metavar='<device_name>', required=False)
def upate_console_remote_device_name(db, linenum, devicename):
"""Update remote device name for a console line"""
config_db = db.cfgdb
ctx = click.get_current_context()

table = "CONSOLE_PORT"
dataKey = 'remote_device'

data = config_db.get_entry(table, linenum)
if data:
if dataKey in data and devicename == data[dataKey]:
# do nothing if the device name is same with existing configurtion
return
elif not devicename:
# remove configuration key from console setting if user not give a remote device name
data.pop(dataKey, None)
config_db.mod_entry(table, linenum, data)
elif isExistingSameDevice(config_db, devicename, table):
ctx.fail("Given device name {} has been used. Please enter a valid device name or remove the existing one !!".format(devicename))
else:
data[dataKey] = devicename
config_db.mod_entry(table, linenum, data)
else:
ctx.fail("Trying to update console port setting, which is not present.")

#
# 'console baud' group ('config console baud ...')
#
@console.command('baud')
@clicommon.pass_db
@click.argument('linenum', metavar='<line_number>', required=True, type=click.IntRange(0, 65535))
@click.argument('baud', metavar='<baud>', required=True, type=click.INT)
def update_console_baud(db, linenum, baud):
"""Update baud for a console line"""
config_db = db.cfgdb
ctx = click.get_current_context()

table = "CONSOLE_PORT"
dataKey = 'baud_rate'

data = config_db.get_entry(table, linenum)
if data:
baud = str(baud)
if dataKey in data and baud == data[dataKey]:
# do nothing if the baud is same with existing configurtion
return
else:
data[dataKey] = baud
config_db.mod_entry(table, linenum, data)
else:
ctx.fail("Trying to update console port setting, which is not present.")

#
# 'console flow_control' group ('config console flow_control ...')
#
@console.command('flow_control')
@clicommon.pass_db
@click.argument('mode', metavar='<mode>', required=True, type=click.Choice(["enable", "disable"]))
@click.argument('linenum', metavar='<line_number>', required=True, type=click.IntRange(0, 65535))
def update_console_flow_control(db, mode, linenum):
"""Update flow control setting for a console line"""
config_db = db.cfgdb
ctx = click.get_current_context()

table = "CONSOLE_PORT"
dataKey = 'flow_control'

innerMode = "1" if mode == "enable" else "0"

data = config_db.get_entry(table, linenum)
if data:
if dataKey in data and innerMode == data[dataKey]:
# do nothing if the flow control setting is same with existing configurtion
return
else:
data[dataKey] = innerMode
config_db.mod_entry(table, linenum, data)
else:
ctx.fail("Trying to update console port setting, which is not present.")

def isExistingSameDevice(config_db, deviceName, table):
"""Check if the given device name is conflict with existing device"""
settings = config_db.get_table(table)
for key,values in settings.items():
if "remote_device" in values and deviceName == values["remote_device"]:
return True

return False
53 changes: 43 additions & 10 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@


import aaa
import console
import feature
import kube
import mlnx
Expand Down Expand Up @@ -750,6 +751,10 @@ def _restart_services(config_db):

execute_systemctl(services_to_restart, SYSTEMCTL_ACTION_RESTART)

# Reload Monit configuration to pick up new hostname in case it changed
click.echo("Reloading Monit configuration ...")
clicommon.run_command("sudo monit reload")


def interface_is_in_vlan(vlan_member_table, interface_name):
""" Check if an interface is in a vlan """
Expand Down Expand Up @@ -876,6 +881,7 @@ def config(ctx):
# Add groups from other modules
config.add_command(aaa.aaa)
config.add_command(aaa.tacacs)
config.add_command(console.console)
config.add_command(feature.feature)
config.add_command(kube.kubernetes)
config.add_command(nat.nat)
Expand Down Expand Up @@ -1101,7 +1107,7 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart):
client.set(config_db.INIT_INDICATOR, 1)

# Migrate DB contents to latest version
db_migrator='/usr/bin/db_migrator.py'
db_migrator='/usr/local/bin/db_migrator.py'
if os.path.isfile(db_migrator) and os.access(db_migrator, os.X_OK):
if namespace is None:
command = "{} -o migrate".format(db_migrator)
Expand Down Expand Up @@ -1182,13 +1188,10 @@ def load_minigraph(db, no_service_restart):
clicommon.run_command(command, display_cmd=True)
client.set(config_db.INIT_INDICATOR, 1)

# get the device type
device_type = _get_device_type()

# These commands are not run for host on multi asic platform
if num_npus == 1 or namespace is not DEFAULT_NAMESPACE:
if device_type != 'MgmtToRRouter':
clicommon.run_command('{}pfcwd start_default'.format(ns_cmd_prefix), display_cmd=True)
# get the device type
device_type = _get_device_type()
if device_type != 'MgmtToRRouter':
clicommon.run_command("pfcwd start_default", display_cmd=True)

# Update SONiC environmnet file
update_sonic_environment()
Expand All @@ -1200,7 +1203,7 @@ def load_minigraph(db, no_service_restart):
clicommon.run_command("config qos reload", display_cmd=True)

# Write latest db version string into db
db_migrator='/usr/bin/db_migrator.py'
db_migrator='/usr/local/bin/db_migrator.py'
if os.path.isfile(db_migrator) and os.access(db_migrator, os.X_OK):
for namespace in namespace_list:
if namespace is DEFAULT_NAMESPACE:
Expand Down Expand Up @@ -1236,8 +1239,38 @@ def hostname(new_hostname):
except SystemExit as e:
click.echo("Restarting hostname-config service failed with error {}".format(e))
raise

# Reload Monit configuration to pick up new hostname in case it changed
click.echo("Reloading Monit configuration ...")
clicommon.run_command("sudo monit reload")

click.echo("Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.")

#
# 'synchronous_mode' command ('config synchronous_mode ...')
#
@config.command('synchronous_mode')
@click.argument('sync_mode', metavar='<enable|disable>', required=True)
def synchronous_mode(sync_mode):
""" Enable or disable synchronous mode between orchagent and syncd \n
swss restart required to apply the configuration \n
Options to restart swss and apply the configuration: \n
1. config save -y \n
config reload -y \n
2. systemctl restart swss
"""

if sync_mode == 'enable' or sync_mode == 'disable':
config_db = ConfigDBConnector()
config_db.connect()
config_db.mod_entry('DEVICE_METADATA' , 'localhost', {"synchronous_mode" : sync_mode})
click.echo("""Wrote %s synchronous mode into CONFIG_DB, swss restart required to apply the configuration: \n
Option 1. config save -y \n
config reload -y \n
Option 2. systemctl restart swss""" % sync_mode)
else:
raise click.BadParameter("Error: Invalid argument %s, expect either enable or disable" % sync_mode)

#
# 'portchannel' group ('config portchannel ...')
#
Expand Down Expand Up @@ -2598,7 +2631,7 @@ def bind(ctx, interface_name, vrf_name):
state_db = SonicV2Connector(use_unix_socket_path=True, namespace=ctx.obj['namespace'])
state_db.connect(state_db.STATE_DB, False)
_hash = '{}{}'.format('INTERFACE_TABLE|', interface_name)
while state_db.get(state_db.STATE_DB, _hash, "state") == "ok":
while state_db.get_all(state_db.STATE_DB, _hash) != None:
time.sleep(0.01)
state_db.close(state_db.STATE_DB)
config_db.set_entry(table_name, interface_name, {"vrf_name": vrf_name})
Expand Down
Loading

0 comments on commit 3b46b03

Please sign in to comment.