Add add-device subcommand.
This commit is contained in:
152
iottb/commands/add_device.py
Normal file
152
iottb/commands/add_device.py
Normal file
@@ -0,0 +1,152 @@
|
||||
import json
|
||||
|
||||
import click
|
||||
from pathlib import Path
|
||||
import logging
|
||||
import re
|
||||
|
||||
from iottb import definitions
|
||||
from iottb.contexts import DeviceMetadata, IottbConfig
|
||||
from iottb.definitions import CFG_FILE_PATH
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def add_device_guided(ctx, cn, db):
|
||||
click.echo('TODO: Implement')
|
||||
logger.info('Adding device interactively')
|
||||
#logger.debug(f'Parameters: {params}. value: {value}')
|
||||
|
||||
@click.command('add-device', help='Add a device to a database')
|
||||
@click.option('--dev', '--device-name', type=str, required=True,
|
||||
help='The name of the device to be added. If this string contains spaces or other special characters \
|
||||
normalization is performed to derive a canonical name')
|
||||
@click.option('--db', '--database', type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
|
||||
envvar='IOTTB_DB', show_envvar=True,
|
||||
help='Database in which to add this device. If not specified use default from config.')
|
||||
@click.option('--guided', is_flag=True, default=False, show_default=True, envvar='IOTTB_GUIDED_ADD', show_envvar=True,
|
||||
help='Add device interactively')
|
||||
def add_device(dev, db, guided):
|
||||
"""Add a new device to a database
|
||||
|
||||
Device name must be supplied unless in an interactive setup. Database is taken from config by default.
|
||||
"""
|
||||
logger.info('add-device invoked')
|
||||
|
||||
# Step 1: Load Config
|
||||
# Dependency: Config file must exist
|
||||
config = IottbConfig(Path(CFG_FILE_PATH))
|
||||
logger.debug(f'Config loaded: {config}')
|
||||
|
||||
# Step 2: Load database
|
||||
# dependency: Database folder must exist
|
||||
if db:
|
||||
database = db
|
||||
path = config.db_path_dict
|
||||
logger.debug(f'Resolved (path, db) {path}, {database}')
|
||||
else:
|
||||
path = config.default_db_location
|
||||
database = config.default_database
|
||||
logger.debug(f'Default (path, db) {path}, {database}')
|
||||
click.secho(f'Using database {database}')
|
||||
full_db_path = Path(path) / database
|
||||
if not full_db_path.is_dir():
|
||||
logger.warning(f'No database at {database}')
|
||||
click.echo(f'Could not find a database.')
|
||||
click.echo(f'You need to initialize the testbed before before you add devices!')
|
||||
click.echo(f'To initialize the testbed in the default location run "iottb init-db"')
|
||||
click.echo('Exiting...')
|
||||
exit()
|
||||
|
||||
# Step 3: Check if device already exists in database
|
||||
# dependency: DeviceMetadata object
|
||||
device_metadata = DeviceMetadata(device_name=dev)
|
||||
device_dir = full_db_path / device_metadata.canonical_name
|
||||
|
||||
# Check if device is already registered
|
||||
if device_dir.exists():
|
||||
logger.warning(f'Device directory {device_dir} already exists.')
|
||||
click.echo(f'Device {dev} already exists in the database.')
|
||||
click.echo('Exiting...')
|
||||
exit()
|
||||
try:
|
||||
device_dir.mkdir()
|
||||
except OSError as e:
|
||||
logger.error(f'Error trying to create device {e}')
|
||||
click.echo('Exiting...')
|
||||
exit()
|
||||
|
||||
# Step 4: Save metadata into device_dir
|
||||
metadata_path = device_dir / definitions.DEVICE_METADATA_FILE_NAME
|
||||
with metadata_path.open('w') as metadata_file:
|
||||
json.dump(device_metadata.__dict__, metadata_file, indent=4)
|
||||
click.echo(f'Successfully added device {dev} to database')
|
||||
logger.debug(f'Added device {dev} to database {database}. Full path of metadata {metadata_path}')
|
||||
logger.info(f'Metadata for {dev} {device_metadata.print_attributes()}')
|
||||
|
||||
|
||||
# @click.command('add-device', help='Add a device to a database')
|
||||
# @click.option('-d', '--dev', '--device-name', type=str, required=True,
|
||||
# help='The name of the device to be added. If this string contains spaces or other special characters \
|
||||
# normalization is performed to derive a canonical name')
|
||||
# @click.option('--db', '--database', type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
|
||||
# envvar='IOTTB_DB', show_envvar=True,
|
||||
# help='Name of in which to add this device. If not specified use default from config.')
|
||||
# @click.option('--guided', is_flag=True, default=False, show_default=True, envvar='IOTTB_GUIDED_ADD', show_envvar=True,
|
||||
# help='Add device interactively')
|
||||
# # @click.option('-m', '--model', default="", help='Model of the device')
|
||||
# # @click.option('--manufacturer', default="", help='Manufacturer of the device')
|
||||
# # @click.option('--firmware-version', default="", help='Current firmware version of the device')
|
||||
# # @click.option('--device-type', default="", help='Type of the device')
|
||||
# # @click.option('-i', '--interfaces', default="", help='Supported interfaces of the device')
|
||||
# # @click.option('--apps', '--companion-applications', default="", help='Companion applications of the device')
|
||||
# # @click.option('--desc', '--description', default="", help='Description of the device')
|
||||
# @click.pass_context
|
||||
# def add_device_inactive(ctx, dev, db, guided):
|
||||
# """Add a new device to a database
|
||||
#
|
||||
# Device name must be supplied unless in an interactive setup. Database is taken from config by default.
|
||||
# """
|
||||
# logger.info('add-device invoked')
|
||||
# config = ctx.obj['CONFIG']
|
||||
# logger.debug(f'{str(config)}')
|
||||
# database = None
|
||||
# # Setep1: Determine the current db
|
||||
# if db:
|
||||
# db_path = str(config.get_database_path)
|
||||
# database = str(config.get_full_default_path())
|
||||
# logger.debug(f'database: {database}. variable type: {type(database)}')
|
||||
# click.echo(f'No db specified, using default database')
|
||||
# if not Path(database).is_dir():
|
||||
# logger.warning(f'No database at {database}')
|
||||
# click.echo(f'Could not find a database.')
|
||||
# click.echo(f'You need to initialize the testbed before before you add devices!')
|
||||
# click.echo(f'To initialize the testbed in the default location run "iottb init-db"')
|
||||
# exit()
|
||||
#
|
||||
# # Step 2: Check if device already exists
|
||||
# if guided:
|
||||
# add_device_guided(ctx, dev, database)
|
||||
# else:
|
||||
# device_metadata = DeviceMetadata(device_name=dev)
|
||||
# device_metadata.print_attributes()
|
||||
# click.echo('TODO: Unguided device add path')
|
||||
#
|
||||
# logger.info(f'Device {dev} added.')
|
||||
|
||||
|
||||
def normalize_device_name(name):
|
||||
"""Normalizes the device name to get a shorter representation which is easier to use at the command line.
|
||||
|
||||
This function derives a device name which can be more easily specified at the command line.
|
||||
The first two occurrences of white space are turned into a dash, the rest of the name is dropped.
|
||||
Name should be an ASCII string.
|
||||
"""
|
||||
logger.info(f'Normalizing name {name}')
|
||||
chars_to_replace = definitions.REPLACEMENT_SET_CANONICAL_DEVICE_NAMES
|
||||
pattern = re.compile('|'.join(re.escape(char) for char in chars_to_replace))
|
||||
norm_name = pattern.sub('-', name, count=2)
|
||||
logger.debug(f'Name after first subst: {norm_name}')
|
||||
norm_name = pattern.sub('', norm_name)
|
||||
logger.debug(f'Fully normalized name: {norm_name}')
|
||||
return norm_name
|
||||
@@ -10,8 +10,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option('-d', '--dest', type=Path, help='Location to put (new) iottb database')
|
||||
@click.option('--name', default=DB_NAME, type=str, help='Name of new database.')
|
||||
@click.option('-d', '--dest', type=click.Path(), help='Location to put (new) iottb database')
|
||||
@click.option('-n', '--name', default=DB_NAME, type=str, help='Name of new database.')
|
||||
@click.option('--update-default/--no-update-default', default=True, help='If new db should be set as the new default')
|
||||
@click.pass_context
|
||||
def init_db(ctx, dest, name, update_default):
|
||||
@@ -20,25 +20,81 @@ def init_db(ctx, dest, name, update_default):
|
||||
logger.debug(f'str(config)')
|
||||
# Use the default path from config if dest is not provided
|
||||
known_dbs = config.get_known_databases()
|
||||
logger.debug(f'Known databases: {known_dbs}')
|
||||
if name in known_dbs:
|
||||
click.echo(f'A database {name} already exists.')
|
||||
logger.info(f'Exiting...')
|
||||
exit()
|
||||
dest = config.get_database_location(name)
|
||||
if Path(dest).joinpath(name).is_dir():
|
||||
click.echo(f'A database {name} already exists.')
|
||||
logger.debug(f'DB {name} exists in {dest}')
|
||||
click.echo(f'Exiting...')
|
||||
exit()
|
||||
logger.debug(f'DB name {name} registered but does not exist.')
|
||||
if not dest:
|
||||
logger.info('No dest set, choosing default destination.')
|
||||
dest = Path(config.default_path).parent
|
||||
dest = Path(config.default_db_location).parent
|
||||
|
||||
db_path = dest / name
|
||||
logger.debug(f'Full path for db {db_path}')
|
||||
db_path = Path(dest).joinpath(name)
|
||||
logger.debug(f'Full path for db {str(db_path)}')
|
||||
# Create the directory if it doesn't exist
|
||||
db_path.mkdir(parents=True, exist_ok=True)
|
||||
logger.info(f"Created directory {db_path.parent} for the database")
|
||||
logger.info(f"mkdir {db_path} successful")
|
||||
click.echo(f'Created {db_path}')
|
||||
|
||||
# Update configuration
|
||||
config.set_database_location(name, str(dest))
|
||||
if update_default:
|
||||
config.set_default_database(name, str(dest))
|
||||
config.save_config()
|
||||
logger.info(f"Updated configuration with database {name} at {db_path}")
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option('-d', '--dest', type=click.Path(), help='Location to put (new) iottb database')
|
||||
@click.option('-n', '--name', default=DB_NAME, type=str, help='Name of new database.')
|
||||
@click.option('--update-default/--no-update-default', default=True, help='If new db should be set as the new default')
|
||||
@click.pass_context
|
||||
def init_db_inactive(ctx, dest, name, update_default):
|
||||
logger.info('init-db invoked')
|
||||
config = ctx.obj['CONFIG']
|
||||
logger.debug(f'str(config)')
|
||||
|
||||
# Retrieve known databases
|
||||
known_dbs = config.get_known_databases()
|
||||
|
||||
# Determine destination path
|
||||
if name in known_dbs:
|
||||
dest = Path(config.get_database_location(name))
|
||||
if dest.joinpath(name).is_dir():
|
||||
click.echo(f'A database {name} already exists.')
|
||||
logger.debug(f'DB {name} exists in {dest}')
|
||||
click.echo(f'Exiting...')
|
||||
exit()
|
||||
logger.debug(f'DB name {name} registered but does not exist.')
|
||||
elif not dest:
|
||||
logger.info('No destination set, using default path from config.')
|
||||
dest = Path(config.default_db_location).parent
|
||||
|
||||
# Ensure destination path is absolute
|
||||
dest = dest.resolve()
|
||||
|
||||
# Combine destination path with database name
|
||||
db_path = dest / name
|
||||
logger.debug(f'Full path for database: {str(db_path)}')
|
||||
|
||||
# Create the directory if it doesn't exist
|
||||
try:
|
||||
db_path.mkdir(parents=True, exist_ok=True)
|
||||
logger.info(f'Directory {db_path} created successfully.')
|
||||
click.echo(f'Created {db_path}')
|
||||
except Exception as e:
|
||||
logger.error(f'Failed to create directory {db_path}: {e}')
|
||||
click.echo(f'Failed to create directory {db_path}: {e}', err=True)
|
||||
exit(1)
|
||||
|
||||
# Update configuration
|
||||
config.set_database_location(name, str(db_path))
|
||||
if update_default:
|
||||
config.set_default_database(name, str(db_path))
|
||||
config.save_config()
|
||||
logger.info(f"Updated configuration with database {name} at {db_path}")
|
||||
|
||||
|
||||
logger.info(f'Updated configuration with database {name} at {db_path}')
|
||||
click.echo(f'Updated configuration with database {name} at {db_path}')
|
||||
|
||||
Reference in New Issue
Block a user