dragonflow/dragonflow/cli/df_db.py

387 lines
12 KiB
Python

# 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
#
# http://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.
import argparse
from jsonmodels import errors
import socket
import sys
from dragonflow.cli import utils as cli_utils
from dragonflow.common import exceptions as df_exceptions
from dragonflow.common import utils as df_utils
from dragonflow.db import api_nb
from dragonflow.db import db_common
from dragonflow.db import model_framework
from dragonflow.db.models import all # noqa
from dragonflow.db.models import l2
db_tables = list(model_framework.iter_tables()) + [db_common.UNIQUE_KEY_TABLE]
nb_api = None
def _get_model_or_exit(table):
"""
Return the model by table name. If no such model is found, raise
SystemExit, which (in general should) exit the process.
"""
try:
return model_framework.get_model(table)
except KeyError:
print('Table not found: ' + table)
raise SystemExit(1)
def _print_list(columns, values, first_label=None):
"""
Print the given columns from the given values. You can override the label
of the first column with 'first_label'.
:param columns: The columns to print from values
:type columns: List of strings
:param values: The values to print
:type values: List of dict
:param first_label: The label of the first column
:type first_label: String
"""
labels, formatters = \
cli_utils.get_list_table_columns_and_formatters(columns, values)
if first_label:
labels[0] = first_label
cli_utils.print_list(values, columns, formatters=formatters,
field_labels=labels)
def print_tables():
columns = ['table']
tables = [{'table': table} for table in db_tables]
_print_list(columns, tables, 'DB Tables')
def print_table(table):
if table == db_common.UNIQUE_KEY_TABLE:
keys = nb_api.driver.get_all_keys(table)
values = [{'id': key} for key in keys]
_print_list(['id'], values)
return
model = _get_model_or_exit(table)
instances = nb_api.get_all(model)
if not instances:
print('Table is empty: ' + table)
return
keys = [{'key': instance.id} for instance in instances]
_print_list(['key'], keys, 'Keys for table')
def print_whole_table(table):
if table == db_common.UNIQUE_KEY_TABLE:
keys = nb_api.driver.get_all_keys(table)
values = [{'id': key, table: int(nb_api.driver.get_key(table, key))}
for key in keys]
columns = ['id', table]
_print_list(columns, values)
return
model = _get_model_or_exit(table)
instances = nb_api.get_all(model)
values = [instance.to_struct() for instance in instances]
if not values:
print('Table is empty: ' + table)
return
columns = values[0].keys()
_print_list(columns, values)
def print_key(table, key):
if table == db_common.UNIQUE_KEY_TABLE:
value = nb_api.driver.get_key(table, key)
value_dict = {'id': key, table: int(value)}
cli_utils.print_dict(value_dict)
return
model = _get_model_or_exit(table)
try:
value = nb_api.get(model(id=key))
except df_exceptions.DBKeyNotFound:
print('Key not found: ' + table)
return
cli_utils.print_dict(value.to_struct())
def bind_port_to_localhost(port_id):
lport = nb_api.get(l2.LogicalPort(id=port_id))
chassis_name = socket.gethostname()
lport.binding = l2.PortBinding(type=l2.BINDING_CHASSIS,
chassis=chassis_name)
nb_api.update(lport)
def clean_whole_table(table):
if table == db_common.UNIQUE_KEY_TABLE:
keys = nb_api.driver.get_all_keys(table)
for key in keys:
try:
nb_api.driver.delete_key(table, key)
except df_exceptions.DBKeyNotFound:
print('Unique key not found: ' + key)
return
model = _get_model_or_exit(table)
values = nb_api.get_all(model)
for value in values:
try:
nb_api.delete(value)
except df_exceptions.DBKeyNotFound:
print('Instance not found: ' + value)
def drop_table(table):
try:
nb_api.driver.delete_table(table)
except df_exceptions.DBKeyNotFound:
print('Table not found: ' + table)
def create_table(table):
nb_api.driver.create_table(table)
print('Table %s is created.' % table)
def remove_record(table, key):
if table == db_common.UNIQUE_KEY_TABLE:
try:
nb_api.driver.delete_key(table, key)
except df_exceptions.DBKeyNotFound:
print('Key %s is not found in table %s.' % (key, table))
return
model = _get_model_or_exit(table)
try:
nb_api.delete(model(id=key))
except df_exceptions.DBKeyNotFound:
print('Key %s is not found in table %s.' % (key, table))
def add_object_from_json(json_str, table):
"""add a new object that described by json
string to dragonflow db.
:param json_str: json string that describes the object to be added
:param table: table name where object should be added
:return: None
"""
nb_api = api_nb.NbApi.get_instance(False, True)
try:
model = model_framework.get_model(table)
except KeyError:
print("Model {} is not found in models list".format(table))
return
try:
obj = model.from_json(json_str)
except ValueError:
print("Json {} is not valid".format(json_str))
return
except TypeError:
print("Json {} is not applicable to {}".format(json_str, table))
return
try:
nb_api.create(obj)
except errors.ValidationError:
print("Json {} is not applicable to {}".format(json_str, table))
def add_object_from_file(file_path, table):
"""add a new object that described by json
file to dragonflow db.
:param file_path: path to the file
:param table: table name where object should be added
:return:
"""
try:
with open(file_path, 'rb') as f:
json_str = f.read()
add_object_from_json(json_str, table)
except IOError:
print("Can't read data from file " + file_path)
def _check_valid_table(parser, table_name):
if table_name not in db_tables:
parser.exit(
status=2,
message="<table> must be one of the following:\n %s\n" % db_tables)
def add_table_command(subparsers):
def handle(args):
print_tables()
sub_parser = subparsers.add_parser('tables', help="Print all the db "
"tables.")
sub_parser.set_defaults(handle=handle)
def add_ls_command(subparsers):
def handle(args):
table = args.table
print_table(table)
sub_parser = subparsers.add_parser('ls', help="Print all the keys for "
"specific table.")
sub_parser.add_argument('table', help='The name of the table.')
sub_parser.set_defaults(handle=handle)
def add_get_command(subparsers):
def handle(args):
table = args.table
key = args.key
print_key(table, key)
sub_parser = subparsers.add_parser('get', help="Print value for specific "
"key.")
sub_parser.add_argument('table', help='The name of the table.')
sub_parser.add_argument('key', help='The key of the resource.')
sub_parser.set_defaults(handle=handle)
def add_dump_command(subparsers):
def handle(args):
for table in db_tables:
print_whole_table(table)
sub_parser = subparsers.add_parser('dump', help="Dump content of all "
"tables.")
sub_parser.set_defaults(handle=handle)
def add_bind_command(subparsers):
def handle(args):
port_id = args.port_id
bind_port_to_localhost(port_id)
sub_parser = subparsers.add_parser('bind', help="Bind a port to "
"localhost.")
sub_parser.add_argument('port_id', help='The ID of the port.')
sub_parser.set_defaults(handle=handle)
def add_clean_command(subparsers):
def handle(args):
for table in db_tables:
clean_whole_table(table)
sub_parser = subparsers.add_parser('clean', help="Clean up all keys.")
sub_parser.set_defaults(handle=handle)
def add_rm_command(subparsers):
def handle(args):
table = args.table
key = args.key
remove_record(table, key)
sub_parser = subparsers.add_parser('rm', help="Remove the specified DB "
"record.")
sub_parser.add_argument('table', help='The name of the table.')
sub_parser.add_argument('key', help='The key of the resource.')
sub_parser.set_defaults(handle=handle)
def add_init_command(subparsers):
def handle(args):
for table in db_tables:
create_table(table)
sub_parser = subparsers.add_parser('init', help="Initialize all tables.")
sub_parser.set_defaults(handle=handle)
def add_dropall_command(subparsers):
def handle(args):
for table in db_tables:
drop_table(table)
sub_parser = subparsers.add_parser('dropall', help="Drop all tables.")
sub_parser.set_defaults(handle=handle)
def add_create_command(subparsers):
def handle(args):
table = args.table
if args.file:
file_path = args.file
add_object_from_file(file_path, table)
elif args.json:
json_str = args.json
add_object_from_json(json_str, table)
else:
print("JSON or file argument must be supplied "
"(use '-h' for details)")
sub_parser = subparsers.add_parser(
'add', help="Add new record to table",
description="Adds a new record to a table in the db"
" (from JSON string or file). The record MUST "
"match the table data-model as defined by Dragonflow "
"(the data-model definition could be viewed by the "
"utility \"df-model\" ,run 'df-model -h' for "
"more details)."
)
sub_parser.add_argument('table', help='The name of the table.')
group = sub_parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'-j', '--json', help="Object represented by JSON string")
group.add_argument(
'-f', '--file', help="Path to file with object json representation")
sub_parser.set_defaults(handle=handle)
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title='subcommands',
description='valid subcommands')
add_table_command(subparsers)
add_ls_command(subparsers)
add_dump_command(subparsers)
add_get_command(subparsers)
add_bind_command(subparsers)
add_clean_command(subparsers)
add_rm_command(subparsers)
add_init_command(subparsers)
add_dropall_command(subparsers)
add_create_command(subparsers)
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
df_utils.config_parse()
global nb_api
nb_api = api_nb.NbApi.get_instance(False)
args.handle(args)
if __name__ == "__main__":
main()