| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- import code
- import platform
- from collections import defaultdict
- from types import SimpleNamespace
- from colorama import Fore, Style
- from django import get_version
- from django.apps import apps
- from django.conf import settings
- from django.core.management.base import BaseCommand
- from django.utils.module_loading import import_string
- from netbox.constants import CORE_APPS
- from netbox.plugins.utils import get_installed_plugins
- def color(color: str, text: str):
- return getattr(Fore, color.upper()) + text + Style.RESET_ALL
- def bright(text: str):
- return Style.BRIGHT + text + Style.RESET_ALL
- def get_models(app_config):
- """
- Return a list of all non-private models within an app.
- """
- return [
- model for model in app_config.get_models()
- if not getattr(model, '_netbox_private', False)
- ]
- def get_constants(app_config):
- """
- Return a dictionary mapping of all constants defined within an app.
- """
- try:
- constants = import_string(f'{app_config.name}.constants')
- except ImportError:
- return {}
- return {
- name: value for name, value in vars(constants).items()
- }
- class Command(BaseCommand):
- help = "Start the Django shell with all NetBox models already imported"
- django_models = {}
- def add_arguments(self, parser):
- parser.add_argument(
- '-c', '--command',
- help='Python code to execute (instead of starting an interactive shell)',
- )
- def _lsapps(self):
- for app_label in self.django_models.keys():
- app_name = apps.get_app_config(app_label).verbose_name
- print(f'{app_label} - {app_name}')
- def _lsmodels(self, app_label=None):
- """
- Return a list of all models within each app.
- Args:
- app_label: The name of a specific app
- """
- if app_label:
- if app_label not in self.django_models:
- print(f"No models listed for {app_label}")
- return
- app_labels = [app_label]
- else:
- app_labels = self.django_models.keys() # All apps
- for app_label in app_labels:
- app_name = apps.get_app_config(app_label).verbose_name
- print(f'{app_name}:')
- for model in self.django_models[app_label]:
- print(f' {app_label}.{model}')
- def get_namespace(self):
- namespace = defaultdict(SimpleNamespace)
- # Iterate through all core apps & plugins to compile namespace of models and constants
- for app_name in [*CORE_APPS, *get_installed_plugins().keys()]:
- app_config = apps.get_app_config(app_name)
- # Populate models
- if models := get_models(app_config):
- for model in models:
- setattr(namespace[app_name], model.__name__, model)
- self.django_models[app_name] = sorted([
- model.__name__ for model in models
- ])
- # Populate constants
- for const_name, const_value in get_constants(app_config).items():
- setattr(namespace[app_name], const_name, const_value)
- return {
- **namespace,
- 'lsapps': self._lsapps,
- 'lsmodels': self._lsmodels,
- }
- @staticmethod
- def get_banner_text():
- lines = [
- '{title} ({hostname})'.format(
- title=bright('NetBox interactive shell'),
- hostname=platform.node(),
- ),
- '{python} | {django} | {netbox}'.format(
- python=color('green', f'Python v{platform.python_version()}'),
- django=color('green', f'Django v{get_version()}'),
- netbox=color('green', settings.RELEASE.name),
- ),
- ]
- if installed_plugins := get_installed_plugins():
- plugin_list = ', '.join([
- color('cyan', f'{name} v{version}') for name, version in installed_plugins.items()
- ])
- lines.append(
- 'Plugins: {plugin_list}'.format(
- plugin_list=plugin_list
- )
- )
- lines.append(
- 'lsapps() & lsmodels() will show available models. Use help(<model>) for more info.'
- )
- return '\n'.join([
- f'### {line}' for line in lines
- ])
- def handle(self, **options):
- namespace = self.get_namespace()
- # If Python code has been passed, execute it and exit.
- if options['command']:
- exec(options['command'], namespace)
- return
- # Try to enable tab-complete
- try:
- import readline
- import rlcompleter
- except ModuleNotFoundError:
- pass
- else:
- readline.set_completer(rlcompleter.Completer(namespace).complete)
- readline.parse_and_bind('tab: complete')
- # Run interactive shell
- return code.interact(banner=self.get_banner_text(), local=namespace)
|