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