def shell_cmd(verbose, with_req_context):
    try:
        from IPython.terminal.ipapp import TerminalIPythonApp
    except ImportError:
        click.echo(
            cformat(
                '%{red!}You need to `pip install ipython` to use the SNMS shell'
            ))
        sys.exit(1)

    current_app.config['REPL'] = True  # disables e.g. memoize_request
    request_stats_request_started()
    context, info = _make_shell_context()
    banner = cformat('%{yellow!}SNMS v{} is ready for your commands').format(
        snms.__version__)
    if verbose:
        banner = '\n'.join(info + ['', banner])
    ctx = current_app.make_shell_context()
    ctx.update(context)
    # clearCache()
    stack = ExitStack()
    if with_req_context:
        stack.enter_context(
            current_app.test_request_context(base_url=config.BASE_URL))
    with stack:
        ipython_app = TerminalIPythonApp.instance(user_ns=ctx,
                                                  display_banner=False)
        ipython_app.initialize(argv=[])
        ipython_app.shell.show_banner(banner)
        ipython_app.start()
def create(grant_admin, create_company):
    """Creates a new user"""
    user_type = 'user' if not grant_admin else 'super_admin'
    while True:
        email = prompt_email()
        if email is None:
            return
        email = email.lower()
        if not User.query.filter(User.email == email).count():
            break
        print(cformat('%{red}Email already exists'))
    name = click.prompt("Name").strip()
    print()
    password = prompt_pass()
    if password is None:
        return

    user = User(uid=generate_uid(), email=email, name=name, role=user_type, password=generate_password_hash(password), is_verified = True)

    if create_company:
        company_name = click.prompt("Company Name").strip()
        company = Company(name=company_name, uid=generate_uid(), key=generate_key())
        user_company = CompanyUserAssociation(role=ROLE_ADMIN)
        user_company.user = user
        company.users.append(user_company)
        user.own_companies.append(company)
        db.session.add(company)

    _print_user_info(user)

    if click.confirm(cformat("%{yellow}Create the new {}?").format(user_type), default=True):
        db.session.add(user)
        db.session.commit()
        print(cformat("%{green}New {} created successfully with ID: %{green!}{}").format(user_type, user.uid))
Example #3
0
def celery_cmd(args):
    # remove the celery shell command
    next(funcs for group, funcs, _ in command_classes if group == 'Main').remove('shell')
    del CeleryCommand.commands['shell']

    if args and args[0] == 'flower':
        # Somehow flower hangs when executing it using CeleryCommand() so we simply exec it directly.
        # It doesn't really need the celery config anyway (besides the broker url)

        try:
            import flower
        except ImportError:
            print(cformat('%{red!}Flower is not installed'))
            sys.exit(1)

        auth_args = ['[email protected]', '--auth_provider=flower.views.auth.GithubLoginHandler']
        auth_env = {'FLOWER_OAUTH2_KEY': '',
                    'FLOWER_OAUTH2_SECRET': '',
                    'FLOWER_OAUTH2_REDIRECT_URI': 'http://localhost:5555/login'
                    }

        args = ['celery', '-b', config.CELERY_BROKER] + args
        env = dict(os.environ, **auth_env)
        os.execvpe('celery', args, env)
    elif args and args[0] == 'shell':
        print(cformat('%{red!}Please use `snms shell`.'))
        sys.exit(1)
    else:
        CeleryCommand(celery).execute_from_commandline(['snms celery'] + args)
Example #4
0
def _require_encoding(encoding):
    cur_encoding = db.engine.execute("SELECT current_setting('server_encoding')").scalar()
    if cur_encoding >= encoding:
        return True
    print(cformat('%{red}Database encoding must be {}; got {}').format(encoding, cur_encoding))
    print(cformat('%{yellow}Recreate your database using `createdb -E {} -T template0 ...`').format(encoding))
    return False
Example #5
0
def _require_extensions(*names):
    missing = sorted(name for name in names if not has_extension(db.engine, name))
    if not missing:
        return True
    print(cformat('%{red}Required Postgres extensions missing: {}').format(', '.join(missing)))
    print(cformat('%{yellow}Create them using these SQL commands (as a Postgres superuser):'))
    for name in missing:
        print(cformat('%{white!}  CREATE EXTENSION {};').format(name))
    return False
def create_all_tables(db, verbose=False, add_initial_data=True):
    """Create all tables and required initial objects"""
    from snms.modules.settings.operations import add_default_settings
    if verbose:
        print(cformat('%{green}Creating tables'))
    db.create_all()
    if verbose:
        print(cformat('%{green}Tables, created'))
    if add_initial_data:
        if verbose:
            print(cformat('%{green}Adding default settings'))
        add_default_settings()
Example #7
0
def purge():
    """Remove deleted companies, sensors, and other data."""
    print(cformat('%{yellow!}*** DANGER'))
    print(cformat('%{yellow!}***%{reset} '
                  '%{red!}This operation will %{yellow!}PERMANENTLY ERASE %{red!} data!%{reset}'))
    from snms.modules.sensors import Sensor, SensorType
    deleted_sensors = Sensor.find(Sensor.deleted).all()
    tabledata = [['ID', 'Name', 'Created On']]
    for sensor in deleted_sensors:
        tabledata.append([str(sensor.id), sensor.name, sensor.created_at])
    table = AsciiTable(tabledata, cformat('%{white!}Deleted Sensors%{reset}'))
    print(table.table)
def _add_to_context(namespace,
                    info,
                    element,
                    name=None,
                    doc=None,
                    color='green'):
    if not name:
        name = element.__name__
    namespace[name] = element
    if doc:
        info.append(
            cformat('+ %%{%s}{}%%{white!} ({})' % color).format(name, doc))
    else:
        info.append(cformat('+ %%{%s}{}' % color).format(name))
def revoke_admin(user_id):
    """Revokes administration rights from a given user"""
    user = User.get(user_id)
    if user is None:
        print(cformat("%{red}This user does not exist"))
        return
    _print_user_info(user)
    if not user.is_admin:
        print(cformat("%{yellow}This user does not have administration rights"))
        return
    if click.confirm(cformat("%{yellow}Revoke administration rights from this user?")):
        user.role = 'user'
        db.session.commit()
        print(cformat("%{green}Administration rights revoked successfully"))
def grant_admin(user_id):
    """Grants administration rights to a given user"""
    user = User.get(user_id)
    if user is None:
        print(cformat("%{red}This user does not exist"))
        return
    _print_user_info(user)
    if user.role == ROLE_ADMIN:
        print(cformat("%{yellow}This user already has administration rights"))
        return
    if click.confirm(cformat("%{yellow}Grant administration rights to this user?")):
        user.role = ROLE_ADMIN
        db.session.commit()
        print(cformat("%{green}Administration rights granted successfully"))
def _add_to_context_multi(namespace,
                          info,
                          elements,
                          names=None,
                          doc=None,
                          color='green'):
    if not names:
        names = [x.__name__ for x in elements]
    for name, element in zip(names, elements):
        namespace[name] = element
    if doc:
        info.append(
            cformat('+ %%{white!}{}:%%{reset} %%{%s}{}' % color).format(
                doc, ', '.join(names)))
    else:
        info.append(cformat('+ %%{%s}{}' % color).format(', '.join(names)))
Example #12
0
def _require_pg_version(version):
    # convert version string such as '9.4.10' to `90410` which is the
    # format used by server_version_num
    req_version = sum(segment * 10**(4 - 2*i) for i, segment in enumerate(map(int, version.split('.'))))
    cur_version = db.engine.execute("SELECT current_setting('server_version_num')::int").scalar()
    if cur_version >= req_version:
        return True
    print(cformat('%{red}Postgres version too old; you need at least {} (or newer)').format(version))
    return False
Example #13
0
def prepare_db(empty=False, root_path=None, verbose=True):
    """Initialize an empty database (create tables, set alembic rev to HEAD)."""
    if not _require_pg_version('9.2'):
        return
    # if not _require_encoding('UTF8'):
    #     return
    # if not _require_extensions('unaccent', 'pg_trgm'):
    #     return
    root_path = root_path or current_app.root_path
    tables = get_all_tables(db)
    if 'alembic_version' not in tables['public']:
        if verbose:
            print(cformat('%{green}Setting the alembic version to HEAD'))
        stamp(directory=os.path.join(root_path, 'migrations'), revision='heads')
        PluginScriptDirectory.dir = os.path.join(root_path, 'core', 'plugins', 'alembic')
        alembic.command.ScriptDirectory = PluginScriptDirectory
        plugin_msg = cformat("%{cyan}Setting the alembic version of the %{cyan!}{}%{reset}%{cyan} "
                             "plugin to HEAD%{reset}")
        # for plugin in plugin_engine.get_active_plugins().values():
        #     if not os.path.exists(plugin.alembic_versions_path):
        #         continue
        #     if verbose:
        #         print(plugin_msg.format(plugin.name))
        #     with plugin.plugin_context():
        #         stamp(revision='heads')
        # Retrieve the table list again, just in case we created unexpected tables
        tables = get_all_tables(db)

    tables['public'] = [t for t in tables['public'] if not t.startswith('alembic_version')]
    if any(tables.values()):
        if verbose:
            print(cformat('%{red}Your database is not empty!'))
            print(cformat('%{yellow}If you just added a new table/model, create an alembic revision instead!'))
            print()
            print('Tables in your database:')
            for schema, schema_tables in sorted(tables.items()):
                for t in schema_tables:
                    print(cformat('  * %{cyan}{}%{reset}.%{cyan!}{}%{reset}').format(schema, t))
        return
    create_all_tables(db, verbose=verbose, add_initial_data=(not empty))
def _make_shell_context():
    context = {}
    info = [cformat('%{white!}Available objects')]
    add_to_context = partial(_add_to_context, context, info)
    add_to_context_multi = partial(_add_to_context_multi, context, info)
    add_to_context_smart = partial(_add_to_context_smart, context, info)
    # Common stdlib modules
    info.append(cformat('*** %{magenta!}stdlib%{reset} ***'))
    DATETIME_ATTRS = ('date', 'time', 'datetime', 'timedelta')
    ORM_ATTRS = ('joinedload', 'defaultload', 'contains_eager', 'lazyload',
                 'noload', 'subqueryload', 'undefer', 'undefer_group',
                 'load_only')
    add_to_context_multi([getattr(datetime, attr) for attr in DATETIME_ATTRS] +
                         [getattr(sqlalchemy.orm, attr)
                          for attr in ORM_ATTRS] + [itertools, re, sys, os],
                         color='yellow')
    # Models
    info.append(cformat('*** %{magenta!}Models%{reset} ***'))
    models = [
        cls for name, cls in sorted(db.Model._decl_class_registry.items(),
                                    key=itemgetter(0))
        if hasattr(cls, '__table__')
    ]
    add_to_context_smart(models)
    # Tasks
    info.append(cformat('*** %{magenta!}Tasks%{reset} ***'))
    tasks = [
        task for task in celery.tasks.values()
        if not task.name.startswith('celery.')
    ]
    add_to_context_smart(tasks,
                         get_name=lambda x: x.name.replace('.', '_'),
                         color='blue!')
    # Plugins
    info.append(cformat('*** %{magenta!}Plugins%{reset} ***'))

    # Utils
    info.append(cformat('*** %{magenta!}Misc%{reset} ***'))
    add_to_context(celery, 'celery', doc='celery app', color='blue!')
    add_to_context(db, 'db', doc='sqlalchemy db interface', color='cyan!')
    add_to_context(now_utc,
                   'now_utc',
                   doc='get current utc time',
                   color='cyan!')
    add_to_context(config, 'config', doc='snms config')
    add_to_context(current_app, 'app', doc='flask app')
    add_to_context(lambda *a, **kw: server_to_utc(datetime.datetime(*a, **kw)),
                   'dt',
                   doc='like datetime() but converted from localtime to utc')
    # Stuff from plugins
    # signals.plugin.shell_context.send(add_to_context=add_to_context, add_to_context_multi=add_to_context_multi)
    return context, info
Example #15
0
def _safe_downgrade(*args, **kwargs):
    func = kwargs.pop('_func')
    print(cformat('%{yellow!}*** DANGER'))
    print(cformat('%{yellow!}***%{reset} '
                  '%{red!}This operation may %{yellow!}PERMANENTLY ERASE %{red!}some data!%{reset}'))
    if current_app.debug:
        skip_confirm = os.environ.get('SNMS_ALWAYS_DOWNGRADE', '').lower() in ('1', 'yes')
        print(cformat('%{yellow!}***%{reset} '
                      "%{green!}Debug mode is active, so you probably won't destroy valuable data"))
    else:
        skip_confirm = False
        print(cformat('%{yellow!}***%{reset} '
                      "%{red!}Debug mode is NOT ACTIVE, so make sure you are on the right machine!"))
    if not skip_confirm and input(cformat('%{yellow!}***%{reset} '
                                              'To confirm this, enter %{yellow!}YES%{reset}: ')) != 'YES':
        print(cformat('%{green}Aborted%{reset}'))
        sys.exit(1)
    else:
        return func(*args, **kwargs)