diff --git a/openwisp_network_topology/__init__.py b/openwisp_network_topology/__init__.py index 60e34a65..fb6d25f3 100644 --- a/openwisp_network_topology/__init__.py +++ b/openwisp_network_topology/__init__.py @@ -1,18 +1,18 @@ -VERSION = (1, 2, 0, 'alpha') +VERSION = (1, 2, 0, "alpha") __version__ = VERSION # alias def get_version(): - version = '%s.%s' % (VERSION[0], VERSION[1]) + version = "%s.%s" % (VERSION[0], VERSION[1]) if VERSION[2]: - version = '%s.%s' % (version, VERSION[2]) - if VERSION[3:] == ('alpha', 0): - version = '%s pre-alpha' % version + version = "%s.%s" % (version, VERSION[2]) + if VERSION[3:] == ("alpha", 0): + version = "%s pre-alpha" % version else: - if VERSION[3] != 'final': + if VERSION[3] != "final": try: rev = VERSION[4] except IndexError: rev = 0 - version = '%s%s%s' % (version, VERSION[3][0:1], rev) + version = "%s%s%s" % (version, VERSION[3][0:1], rev) return version diff --git a/openwisp_network_topology/admin.py b/openwisp_network_topology/admin.py index 6b54f1e4..1dfc15af 100644 --- a/openwisp_network_topology/admin.py +++ b/openwisp_network_topology/admin.py @@ -20,9 +20,9 @@ from .contextmanagers import log_failure from .visualizer import GraphVisualizerUrls -Link = swapper.load_model('topology', 'Link') -Node = swapper.load_model('topology', 'Node') -Topology = swapper.load_model('topology', 'Topology') +Link = swapper.load_model("topology", "Link") +Node = swapper.load_model("topology", "Node") +Topology = swapper.load_model("topology", "Topology") class TimeStampedEditableAdmin(ModelAdmin): @@ -31,7 +31,7 @@ class TimeStampedEditableAdmin(ModelAdmin): """ def __init__(self, *args, **kwargs): - self.readonly_fields += ('created', 'modified') + self.readonly_fields += ("created", "modified") super().__init__(*args, **kwargs) @@ -40,21 +40,21 @@ class BaseAdmin(TimeStampedEditableAdmin): class Media: css = { - 'all': [ - 'netjsongraph/css/src/netjsongraph.css', - 'netjsongraph/css/src/netjsongraph-theme.css', - 'netjsongraph/css/lib/jquery-ui.min.css', - 'netjsongraph/css/style.css', - 'netjsongraph/css/admin.css', + "all": [ + "netjsongraph/css/src/netjsongraph.css", + "netjsongraph/css/src/netjsongraph-theme.css", + "netjsongraph/css/lib/jquery-ui.min.css", + "netjsongraph/css/style.css", + "netjsongraph/css/admin.css", ] } js = [ - 'admin/js/jquery.init.js', - 'netjsongraph/js/lib/jquery-ui.min.js', - 'netjsongraph/js/src/netjsongraph.min.js', - 'netjsongraph/js/strategy-switcher.js', - 'netjsongraph/js/topology-history.js', - 'netjsongraph/js/visualize.js', + "admin/js/jquery.init.js", + "netjsongraph/js/lib/jquery-ui.min.js", + "netjsongraph/js/src/netjsongraph.min.js", + "netjsongraph/js/strategy-switcher.js", + "netjsongraph/js/topology-history.js", + "netjsongraph/js/visualize.js", ] @@ -64,53 +64,53 @@ class TopologyAdmin( ): model = Topology list_display = [ - 'label', - 'organization', - 'parser', - 'strategy', - 'published', - 'created', - 'modified', + "label", + "organization", + "parser", + "strategy", + "published", + "created", + "modified", ] readonly_fields = [ - 'uuid', - 'protocol', - 'version', - 'revision', - 'metric', - 'receive_url', + "uuid", + "protocol", + "version", + "revision", + "metric", + "receive_url", ] - list_filter = ['parser', 'strategy', MultitenantOrgFilter] - search_fields = ['label', 'id'] - actions = ['update_selected', 'unpublish_selected', 'publish_selected'] + list_filter = ["parser", "strategy", MultitenantOrgFilter] + search_fields = ["label", "id"] + actions = ["update_selected", "unpublish_selected", "publish_selected"] fields = [ - 'label', - 'organization', - 'parser', - 'strategy', - 'url', - 'uuid', - 'key', - 'expiration_time', - 'receive_url', - 'published', - 'protocol', - 'version', - 'revision', - 'metric', - 'created', + "label", + "organization", + "parser", + "strategy", + "url", + "uuid", + "key", + "expiration_time", + "receive_url", + "published", + "protocol", + "version", + "revision", + "metric", + "created", ] - receive_url_name = 'receive_topology' + receive_url_name = "receive_topology" receive_url_urlconf = app_settings.TOPOLOGY_API_URLCONF receive_url_baseurl = app_settings.TOPOLOGY_API_BASEURL - change_form_template = 'admin/topology/topology/change_form.html' + change_form_template = "admin/topology/topology/change_form.html" def get_fields(self, request, obj=None): fields = list(super().get_fields(request, obj)) if not obj: # Receive URL cannot be created without an object. # Hence, remove the "receive_url" field. - fields.remove('receive_url') + fields.remove("receive_url") return fields def get_actions(self, request): @@ -118,29 +118,29 @@ def get_actions(self, request): move delete action to last position """ actions = super().get_actions(request) - if 'delete_selected' in actions: - delete = actions['delete_selected'] - del actions['delete_selected'] - actions['delete_selected'] = delete + if "delete_selected" in actions: + delete = actions["delete_selected"] + del actions["delete_selected"] + actions["delete_selected"] = delete return actions - def change_view(self, request, object_id, form_url='', extra_context=None): + def change_view(self, request, object_id, form_url="", extra_context=None): extra_context = extra_context or {} - prefix = 'admin:{0}_{1}'.format( + prefix = "admin:{0}_{1}".format( self.opts.app_label, self.model.__name__.lower() ) - text = _('View topology graph') + text = _("View topology graph") extra_context.update( { - 'additional_buttons': [ + "additional_buttons": [ { - 'type': 'button', - 'url': reverse( - '{0}_visualize'.format(prefix), args=[object_id] + "type": "button", + "url": reverse( + "{0}_visualize".format(prefix), args=[object_id] ), - 'class': 'visualizelink', - 'value': text, - 'title': '{0} (ALT+P)'.format(text), + "class": "visualizelink", + "value": text, + "title": "{0} (ALT+P)".format(text), } ] } @@ -148,40 +148,40 @@ def change_view(self, request, object_id, form_url='', extra_context=None): return super().change_view(request, object_id, form_url, extra_context) def get_urls(self): - options = getattr(self.model, '_meta') - url_prefix = '{0}_{1}'.format(options.app_label, options.model_name) + options = getattr(self.model, "_meta") + url_prefix = "{0}_{1}".format(options.app_label, options.model_name) return [ re_path( - r'^visualize/(?P[^/]+)/$', + r"^visualize/(?P[^/]+)/$", self.admin_site.admin_view(self.visualize_view), - name='{0}_visualize'.format(url_prefix), + name="{0}_visualize".format(url_prefix), ) ] + super().get_urls() def _message(self, request, rows, suffix, level=messages.SUCCESS): if rows == 1: - prefix = _('1 {0} was'.format(self.model._meta.verbose_name)) + prefix = _("1 {0} was".format(self.model._meta.verbose_name)) else: # pragma: nocover prefix = _( - '{0} {1} were'.format(rows, self.model._meta.verbose_name_plural) + "{0} {1} were".format(rows, self.model._meta.verbose_name_plural) ) - self.message_user(request, '{0} {1}'.format(prefix, suffix), level=level) + self.message_user(request, "{0} {1}".format(prefix, suffix), level=level) @admin.action( - description=_('Update selected topologies (FETCH strategy only)'), - permissions=['change'], + description=_("Update selected topologies (FETCH strategy only)"), + permissions=["change"], ) def update_selected(self, request, queryset): items = list(queryset) failed = [] ignored = [] for item in items: - if item.strategy == 'fetch': + if item.strategy == "fetch": try: item.update() except Exception as e: - failed.append('{0}: {1}'.format(item.label, str(e))) - with log_failure('update topology admin action', item): + failed.append("{0}: {1}".format(item.label, str(e))) + with log_failure("update topology admin action", item): raise e else: ignored.append(item) @@ -193,23 +193,23 @@ def update_selected(self, request, queryset): successes = len(items) - failures total_ignored = len(ignored) if successes > 0: - self._message(request, successes, _('successfully updated')) + self._message(request, successes, _("successfully updated")) if failures > 0: - message = _('not updated. %s') % '; '.join(failed) + message = _("not updated. %s") % "; ".join(failed) self._message(request, failures, message, level=messages.ERROR) if total_ignored > 0: message = _("ignored (not using FETCH strategy)") self._message(request, total_ignored, message, level=messages.WARNING) - @admin.action(description=_('Publish selected topologies'), permissions=['change']) + @admin.action(description=_("Publish selected topologies"), permissions=["change"]) def publish_selected(self, request, queryset): rows_updated = queryset.update(published=True) - self._message(request, rows_updated, _('successfully published')) + self._message(request, rows_updated, _("successfully published")) - @admin.action(description=_('Unpublish selected items'), permissions=['change']) + @admin.action(description=_("Unpublish selected items"), permissions=["change"]) def unpublish_selected(self, request, queryset): rows_updated = queryset.update(published=False) - self._message(request, rows_updated, _('successfully unpublished')) + self._message(request, rows_updated, _("successfully unpublished")) def visualize_view(self, request, pk): graph_url, history_url = self.get_graph_urls(request, pk) @@ -217,20 +217,20 @@ def visualize_view(self, request, pk): opts = self.model._meta context.update( { - 'is_popup': True, - 'opts': opts, - 'change': False, - 'media': self.media, - 'graph_url': graph_url, - 'history_url': history_url, + "is_popup": True, + "opts": opts, + "change": False, + "media": self.media, + "graph_url": graph_url, + "history_url": history_url, } ) - return TemplateResponse(request, 'admin/topology/visualize.html', context) + return TemplateResponse(request, "admin/topology/visualize.html", context) class UserPropertiesForm(forms.ModelForm): class Meta: - widgets = {'user_properties': FlatJsonWidget} + widgets = {"user_properties": FlatJsonWidget} class NodeLinkMixin(MultitenantAdminMixin): @@ -239,69 +239,69 @@ class NodeLinkMixin(MultitenantAdminMixin): other extensions to override the admin queryset """ - readonly_fields = ['readonly_properties'] + readonly_fields = ["readonly_properties"] def get_fields(self, request, obj): fields = super().get_fields(request, obj) if obj: return fields fields_copy = list(fields) - fields_copy.remove('organization') + fields_copy.remove("organization") return fields_copy def get_queryset(self, request): return self.model.get_queryset(super().get_queryset(request)) def readonly_properties(self, obj): - output = '' + output = "" for key, value in obj.properties.items(): - key = key.replace('_', ' ').capitalize() - output += f'

{key}: {value}

' + key = key.replace("_", " ").capitalize() + output += f"

{key}: {value}

" return mark_safe(output) - readonly_properties.short_description = _('Properties') + readonly_properties.short_description = _("Properties") class TopologyFilter(MultitenantRelatedOrgFilter): - field_name = 'topology' - parameter_name = 'topology_id' - title = _('topology') + field_name = "topology" + parameter_name = "topology_id" + title = _("topology") @admin.register(Node) class NodeAdmin(NodeLinkMixin, BaseAdmin): model = Node form = UserPropertiesForm - change_form_template = 'admin/topology/node/change_form.html' - list_display = ['get_name', 'organization', 'topology', 'addresses'] - search_fields = ['addresses', 'label', 'properties'] + change_form_template = "admin/topology/node/change_form.html" + list_display = ["get_name", "organization", "topology", "addresses"] + search_fields = ["addresses", "label", "properties"] list_filter = [ (MultitenantOrgFilter), (TopologyFilter), ] - multitenant_shared_relations = ('topology',) - autocomplete_fields = ('topology',) + multitenant_shared_relations = ("topology",) + autocomplete_fields = ("topology",) fields = [ - 'topology', - 'organization', - 'label', - 'addresses', - 'readonly_properties', - 'user_properties', - 'created', - 'modified', + "topology", + "organization", + "label", + "addresses", + "readonly_properties", + "user_properties", + "created", + "modified", ] - def change_view(self, request, object_id, form_url='', extra_context=None): + def change_view(self, request, object_id, form_url="", extra_context=None): extra_context = extra_context or {} link_model = self.model.source_link_set.field.model - admin_url = 'admin:{0}_link_change'.format(self.opts.app_label) + admin_url = "admin:{0}_link_change".format(self.opts.app_label) extra_context.update( { - 'node_links': link_model.objects.select_related('source', 'target') - .only('source__label', 'target__label', 'cost', 'status') + "node_links": link_model.objects.select_related("source", "target") + .only("source__label", "target__label", "cost", "status") .filter(Q(source_id=object_id) | Q(target_id=object_id)), - 'admin_url': admin_url, + "admin_url": admin_url, } ) return super().change_view(request, object_id, form_url, extra_context) @@ -311,39 +311,39 @@ def change_view(self, request, object_id, form_url='', extra_context=None): class LinkAdmin(NodeLinkMixin, BaseAdmin): model = Link form = UserPropertiesForm - raw_id_fields = ['source', 'target'] + raw_id_fields = ["source", "target"] search_fields = [ - 'source__label', - 'target__label', - 'source__addresses', - 'target__addresses', - 'properties', + "source__label", + "target__label", + "source__addresses", + "target__addresses", + "properties", ] list_display = [ - '__str__', - 'organization', - 'topology', - 'status', - 'cost', - 'cost_text', + "__str__", + "organization", + "topology", + "status", + "cost", + "cost_text", ] list_filter = [ - 'status', + "status", (MultitenantOrgFilter), (TopologyFilter), ] - multitenant_shared_relations = ('topology', 'source', 'target') - autocomplete_fields = ('topology',) + multitenant_shared_relations = ("topology", "source", "target") + autocomplete_fields = ("topology",) fields = [ - 'topology', - 'organization', - 'status', - 'source', - 'target', - 'cost', - 'cost_text', - 'readonly_properties', - 'user_properties', - 'created', - 'modified', + "topology", + "organization", + "status", + "source", + "target", + "cost", + "cost_text", + "readonly_properties", + "user_properties", + "created", + "modified", ] diff --git a/openwisp_network_topology/api/filters.py b/openwisp_network_topology/api/filters.py index e77fdc14..c9a2159c 100644 --- a/openwisp_network_topology/api/filters.py +++ b/openwisp_network_topology/api/filters.py @@ -2,24 +2,24 @@ from openwisp_users.api.filters import OrganizationManagedFilter -Node = load_model('topology', 'Node') -Link = load_model('topology', 'Link') -Topology = load_model('topology', 'Topology') +Node = load_model("topology", "Node") +Link = load_model("topology", "Link") +Topology = load_model("topology", "Topology") class NetworkCollectionFilter(OrganizationManagedFilter): class Meta(OrganizationManagedFilter.Meta): model = Topology - fields = OrganizationManagedFilter.Meta.fields + ['strategy', 'parser'] + fields = OrganizationManagedFilter.Meta.fields + ["strategy", "parser"] class NodeFilter(OrganizationManagedFilter): class Meta(OrganizationManagedFilter.Meta): model = Node - fields = OrganizationManagedFilter.Meta.fields + ['topology'] + fields = OrganizationManagedFilter.Meta.fields + ["topology"] class LinkFilter(OrganizationManagedFilter): class Meta(OrganizationManagedFilter.Meta): model = Link - fields = OrganizationManagedFilter.Meta.fields + ['topology', 'status'] + fields = OrganizationManagedFilter.Meta.fields + ["topology", "status"] diff --git a/openwisp_network_topology/api/parsers.py b/openwisp_network_topology/api/parsers.py index 07d05e19..2dd90fb3 100644 --- a/openwisp_network_topology/api/parsers.py +++ b/openwisp_network_topology/api/parsers.py @@ -9,13 +9,13 @@ class TextParser(JSONParser): Dummy TextParser accepting any text (used in ReceiveTopologyView) """ - media_type = 'text/plain' + media_type = "text/plain" def parse(self, stream, media_type=None, parser_context=None): parser_context = parser_context or {} - encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) + encoding = parser_context.get("encoding", settings.DEFAULT_CHARSET) try: return stream.read().decode(encoding) except ValidationError as e: # pragma: nocover - raise ParseError('text/plain parse error - %s' % str(e)) + raise ParseError("text/plain parse error - %s" % str(e)) diff --git a/openwisp_network_topology/api/serializers.py b/openwisp_network_topology/api/serializers.py index 31ccebde..674552fe 100644 --- a/openwisp_network_topology/api/serializers.py +++ b/openwisp_network_topology/api/serializers.py @@ -8,9 +8,9 @@ from openwisp_users.api.mixins import FilterSerializerByOrgManaged from openwisp_utils.api.serializers import ValidatedModelSerializer -Node = swapper.load_model('topology', 'Node') -Link = swapper.load_model('topology', 'Link') -Topology = swapper.load_model('topology', 'Topology') +Node = swapper.load_model("topology", "Node") +Link = swapper.load_model("topology", "Link") +Topology = swapper.load_model("topology", "Topology") class NetworkCollectionSerializer(serializers.ListSerializer): @@ -25,15 +25,15 @@ def data(self): def to_representation(self, data): return OrderedDict( ( - ('type', 'NetworkCollection'), - ('collection', super().to_representation(data)), + ("type", "NetworkCollection"), + ("collection", super().to_representation(data)), ) ) def get_receive_url(pk, key): - path = reverse('receive_topology', args=[pk]) - url = '{0}?key={1}'.format(path, key) + path = reverse("receive_topology", args=[pk]) + url = "{0}?key={1}".format(path, key) return url @@ -50,24 +50,24 @@ def get_representation_data(obj): nodes.append(node.json(dict=True, original=False)) netjson = OrderedDict( ( - ('type', 'NetworkGraph'), - ('protocol', obj.protocol), - ('version', obj.version), - ('metric', obj.metric), - ('label', obj.label), - ('id', str(obj.id)), - ('parser', obj.parser), - ('organization', str(obj.organization.id) if obj.organization else None), - ('strategy', obj.strategy), - ('url', obj.url), - ('key', obj.key), - ('expiration_time', obj.expiration_time), - ('receive_url', get_receive_url(obj.pk, obj.key)), - ('published', obj.published), - ('created', obj.created), - ('modified', obj.modified), - ('nodes', nodes), - ('links', links), + ("type", "NetworkGraph"), + ("protocol", obj.protocol), + ("version", obj.version), + ("metric", obj.metric), + ("label", obj.label), + ("id", str(obj.id)), + ("parser", obj.parser), + ("organization", str(obj.organization.id) if obj.organization else None), + ("strategy", obj.strategy), + ("url", obj.url), + ("key", obj.key), + ("expiration_time", obj.expiration_time), + ("receive_url", get_receive_url(obj.pk, obj.key)), + ("published", obj.published), + ("created", obj.created), + ("modified", obj.modified), + ("nodes", nodes), + ("links", links), ) ) return netjson @@ -80,8 +80,8 @@ def to_representation(self, obj): of Topology object. """ topology_data = get_representation_data(obj) - topology_data['receive_url'] = self.context['request'].build_absolute_uri( - topology_data['receive_url'] + topology_data["receive_url"] = self.context["request"].build_absolute_uri( + topology_data["receive_url"] ) return topology_data @@ -89,7 +89,7 @@ def to_representation(self, obj): class TopologySerializer(NetworkGraphRepresentation, ValidatedModelSerializer): class Meta: model = Topology - fields = '__all__' + fields = "__all__" class BaseSerializer(FilterSerializerByOrgManaged, ValidatedModelSerializer): @@ -102,9 +102,9 @@ class NetworkGraphSerializer(BaseSerializer): """ def to_representation(self, obj): - if self.context['request'].method == 'POST': + if self.context["request"].method == "POST": serializer = TopologySerializer( - self.instance, context={'request': self.context['request']} + self.instance, context={"request": self.context["request"]} ) return serializer.data return obj.json(dict=True) @@ -112,44 +112,44 @@ def to_representation(self, obj): class Meta: model = Topology fields = ( - 'label', - 'organization', - 'parser', - 'strategy', - 'key', - 'expiration_time', - 'url', - 'published', + "label", + "organization", + "parser", + "strategy", + "key", + "expiration_time", + "url", + "published", ) list_serializer_class = NetworkCollectionSerializer - extra_kwargs = {'published': {'initial': True}} + extra_kwargs = {"published": {"initial": True}} class NetworkGraphUpdateSerializer(NetworkGraphRepresentation, BaseSerializer): class Meta: model = Topology fields = ( - 'label', - 'organization', - 'parser', - 'strategy', - 'key', - 'expiration_time', - 'url', - 'published', + "label", + "organization", + "parser", + "strategy", + "key", + "expiration_time", + "url", + "published", ) def validate_strategy(self, value): - if value == 'receive' and not self.initial_data.get('key'): + if value == "receive" and not self.initial_data.get("key"): raise serializers.ValidationError( - _('A key must be specified when using RECEIVE strategy') + _("A key must be specified when using RECEIVE strategy") ) return value def validate_url(self, value): - if not value and self.initial_data.get('strategy') == 'fetch': + if not value and self.initial_data.get("strategy") == "fetch": raise serializers.ValidationError( - _('An url must be specified when using FETCH strategy') + _("An url must be specified when using FETCH strategy") ) return value @@ -160,20 +160,20 @@ class BaseNodeLinkSerializer(BaseSerializer): def validate(self, data): instance = self.instance or self.Meta.model(**data) instance.full_clean() - data['organization'] = instance.organization + data["organization"] = instance.organization return data def validate_properties(self, value): if type(value) is not dict: raise serializers.ValidationError( - _('Value must be valid JSON or key, valued pair.') + _("Value must be valid JSON or key, valued pair.") ) return value def validate_user_properties(self, value): if type(value) is not dict: raise serializers.ValidationError( - _('Value must be valid JSON or key, valued pair.') + _("Value must be valid JSON or key, valued pair.") ) return value @@ -185,39 +185,39 @@ class NodeSerializer(BaseNodeLinkSerializer): class Meta: model = Node fields = ( - 'id', - 'topology', - 'organization', - 'label', - 'addresses', - 'properties', - 'user_properties', - 'created', - 'modified', + "id", + "topology", + "organization", + "label", + "addresses", + "properties", + "user_properties", + "created", + "modified", ) - read_only_fields = ('created', 'modified') + read_only_fields = ("created", "modified") class LinkSerializer(BaseNodeLinkSerializer): user_properties = serializers.JSONField( initial={}, - help_text=_('If you need to add additional data to this link use this field'), + help_text=_("If you need to add additional data to this link use this field"), ) class Meta: model = Link fields = ( - 'id', - 'topology', - 'organization', - 'status', - 'source', - 'target', - 'cost', - 'cost_text', - 'properties', - 'user_properties', - 'created', - 'modified', + "id", + "topology", + "organization", + "status", + "source", + "target", + "cost", + "cost_text", + "properties", + "user_properties", + "created", + "modified", ) - read_only_fields = ('organization', 'created', 'modified') + read_only_fields = ("organization", "created", "modified") diff --git a/openwisp_network_topology/api/views.py b/openwisp_network_topology/api/views.py index d61acbbd..58f786b7 100644 --- a/openwisp_network_topology/api/views.py +++ b/openwisp_network_topology/api/views.py @@ -28,10 +28,10 @@ ) logger = logging.getLogger(__name__) -Snapshot = swapper.load_model('topology', 'Snapshot') -Topology = swapper.load_model('topology', 'Topology') -Node = swapper.load_model('topology', 'Node') -Link = swapper.load_model('topology', 'Link') +Snapshot = swapper.load_model("topology", "Snapshot") +Topology = swapper.load_model("topology", "Topology") +Node = swapper.load_model("topology", "Node") +Link = swapper.load_model("topology", "Link") class RequireAuthentication(APIView): @@ -50,14 +50,14 @@ class RequireAuthentication(APIView): class ListViewPagination(pagination.PageNumberPagination): page_size = 10 - page_size_query_param = 'page_size' + page_size_query_param = "page_size" max_page_size = 100 class UnpublishedTopologyFilterMixin: def get_queryset(self): qs = super().get_queryset() - if self.request.query_params.get('include_unpublished'): + if self.request.query_params.get("include_unpublished"): return qs return qs.filter(published=True) @@ -74,7 +74,7 @@ class NetworkCollectionView( """ serializer_class = NetworkGraphSerializer - queryset = Topology.objects.select_related('organization') + queryset = Topology.objects.select_related("organization") filter_backends = (DjangoFilterBackend,) filterset_class = NetworkCollectionFilter @@ -102,7 +102,7 @@ class NetworkGraphView( """ serializer_class = NetworkGraphUpdateSerializer - queryset = Topology.objects.select_related('organization') + queryset = Topology.objects.select_related("organization") class ReceiveTopologyView(APIView): @@ -118,24 +118,24 @@ class ReceiveTopologyView(APIView): parser_classes = (TextParser,) def _validate_request(self, request, topology): - key = request.query_params.get('key') + key = request.query_params.get("key") # wrong content type: 415 - if request.content_type != 'text/plain': + if request.content_type != "text/plain": return Response( - {'detail': _('expected content type "text/plain"')}, status=415 + {"detail": _('expected content type "text/plain"')}, status=415 ) # missing key: 400 if not key: return Response( - {'detail': _('missing required "key" parameter')}, status=400 + {"detail": _('missing required "key" parameter')}, status=400 ) # wrong key 403 if topology.key != key: - return Response({'detail': _('wrong key')}, status=403) + return Response({"detail": _("wrong key")}, status=403) return def post(self, request, pk, format=None): - topology = get_object_or_404(self.model, pk, strategy='receive') + topology = get_object_or_404(self.model, pk, strategy="receive") validation_response = self._validate_request(request, topology) if validation_response: return validation_response @@ -143,13 +143,13 @@ def post(self, request, pk, format=None): topology.receive(request.data) except NetdiffException as e: error = _( - 'Supplied data not recognized as %s, ' + "Supplied data not recognized as %s, " 'got exception of type "%s" ' 'with message "%s"' ) % (topology.get_parser_display(), e.__class__.__name__, e) - return Response({'detail': error}, status=400) - success_message = _('data received successfully') - return Response({'detail': success_message}) + return Response({"detail": error}, status=400) + success_message = _("data received successfully") + return Response({"detail": success_message}) class NetworkGraphHistoryView(RequireAuthentication): @@ -165,28 +165,28 @@ class NetworkGraphHistoryView(RequireAuthentication): def get(self, request, pk, format=None): topology = get_object_or_404(self.topology_model, pk) self.check_object_permissions(request, topology) - date = request.query_params.get('date') + date = request.query_params.get("date") options = dict(topology=topology, date=date) # missing date: 400 if not date: return Response( - {'detail': _('missing required "date" parameter')}, status=400 + {"detail": _('missing required "date" parameter')}, status=400 ) try: s = self.snapshot_model.objects.get(**options) return Response(json.loads(s.data)) except self.snapshot_model.DoesNotExist: return Response( - {'detail': _('no snapshot found for this date')}, status=404 + {"detail": _("no snapshot found for this date")}, status=404 ) except ValidationError: - return Response({'detail': _('invalid date supplied')}, status=403) + return Response({"detail": _("invalid date supplied")}, status=403) class NodeListCreateView( ProtectedAPIMixin, FilterByOrganizationManaged, generics.ListCreateAPIView ): - queryset = Node.objects.order_by('-created') + queryset = Node.objects.order_by("-created") serializer_class = NodeSerializer pagination_class = ListViewPagination filter_backends = (DjangoFilterBackend,) @@ -198,7 +198,7 @@ class NodeDetailView( FilterByOrganizationManaged, generics.RetrieveUpdateDestroyAPIView, ): - queryset = Node.objects.all().select_related('topology') + queryset = Node.objects.all().select_related("topology") serializer_class = NodeSerializer @@ -206,10 +206,10 @@ class LinkListCreateView( ProtectedAPIMixin, FilterByOrganizationManaged, generics.ListCreateAPIView ): queryset = Link.objects.select_related( - 'topology', - 'source', - 'target', - ).order_by('-created') + "topology", + "source", + "target", + ).order_by("-created") serializer_class = LinkSerializer pagination_class = ListViewPagination filter_backends = (DjangoFilterBackend,) @@ -222,9 +222,9 @@ class LinkDetailView( generics.RetrieveUpdateDestroyAPIView, ): queryset = Link.objects.select_related( - 'topology', - 'source', - 'target', + "topology", + "source", + "target", ) serializer_class = LinkSerializer diff --git a/openwisp_network_topology/apps.py b/openwisp_network_topology/apps.py index fbe5c1af..bf919234 100644 --- a/openwisp_network_topology/apps.py +++ b/openwisp_network_topology/apps.py @@ -8,9 +8,9 @@ class OpenwispNetworkTopologyConfig(AppConfig): - name = 'openwisp_network_topology' - label = 'topology' - verbose_name = _('Network Topology') + name = "openwisp_network_topology" + label = "topology" + verbose_name = _("Network Topology") def ready(self, *args, **kwargs): if SIGNALS: # pragma: nocover @@ -24,27 +24,27 @@ def register_menu_groups(self): register_menu_group( position=110, config={ - 'label': 'Network Topology', - 'items': { + "label": "Network Topology", + "items": { 1: { - 'label': _('Topologies'), - 'model': get_model_name('topology', 'Topology'), - 'name': 'changelist', - 'icon': 'ow-topology', + "label": _("Topologies"), + "model": get_model_name("topology", "Topology"), + "name": "changelist", + "icon": "ow-topology", }, 2: { - 'label': _('Nodes'), - 'model': get_model_name('topology', 'Node'), - 'name': 'changelist', - 'icon': 'ow-node', + "label": _("Nodes"), + "model": get_model_name("topology", "Node"), + "name": "changelist", + "icon": "ow-node", }, 3: { - 'label': _('Links'), - 'model': get_model_name('topology', 'Link'), - 'name': 'changelist', - 'icon': 'ow-link', + "label": _("Links"), + "model": get_model_name("topology", "Link"), + "name": "changelist", + "icon": "ow-link", }, }, - 'icon': 'ow-network-topology', + "icon": "ow-network-topology", }, ) diff --git a/openwisp_network_topology/base/link.py b/openwisp_network_topology/base/link.py index fb6ef4f1..addc7bd0 100644 --- a/openwisp_network_topology/base/link.py +++ b/openwisp_network_topology/base/link.py @@ -29,35 +29,35 @@ class AbstractLink(ShareableOrgMixin, TimeStampedEditableModel): """ topology = models.ForeignKey( - swapper.get_model_name('topology', 'Topology'), on_delete=models.CASCADE + swapper.get_model_name("topology", "Topology"), on_delete=models.CASCADE ) source = models.ForeignKey( - swapper.get_model_name('topology', 'Node'), - related_name='source_link_set', + swapper.get_model_name("topology", "Node"), + related_name="source_link_set", on_delete=models.CASCADE, ) target = models.ForeignKey( - swapper.get_model_name('topology', 'Node'), - related_name='target_link_set', + swapper.get_model_name("topology", "Node"), + related_name="target_link_set", on_delete=models.CASCADE, ) cost = models.FloatField() cost_text = models.CharField(max_length=24, blank=True) - STATUS = Choices('up', 'down') + STATUS = Choices("up", "down") status = StatusField() properties = JSONField( default=dict, blank=True, - load_kwargs={'object_pairs_hook': OrderedDict}, - dump_kwargs={'indent': 4, 'cls': JSONEncoder}, + load_kwargs={"object_pairs_hook": OrderedDict}, + dump_kwargs={"indent": 4, "cls": JSONEncoder}, ) user_properties = JSONField( - verbose_name=_('user defined properties'), - help_text=_('If you need to add additional data to this link use this field'), + verbose_name=_("user defined properties"), + help_text=_("If you need to add additional data to this link use this field"), default=dict, blank=True, - load_kwargs={'object_pairs_hook': OrderedDict}, - dump_kwargs={'indent': 4, 'cls': JSONEncoder}, + load_kwargs={"object_pairs_hook": OrderedDict}, + dump_kwargs={"indent": 4, "cls": JSONEncoder}, ) status_changed = models.DateTimeField(auto_now=True) @@ -76,7 +76,7 @@ def save(self, *args, **kwargs): self._initial_status = self.status def __str__(self): - return '{0} - {1}'.format(self.source.get_name(), self.target.get_name()) + return "{0} - {1}".format(self.source.get_name(), self.target.get_name()) def full_clean(self, *args, **kwargs): self.validate_organization() @@ -85,20 +85,20 @@ def full_clean(self, *args, **kwargs): def clean(self): if self.source == self.target or self.source_id == self.target_id: - raise ValidationError(_('source and target must not be the same')) + raise ValidationError(_("source and target must not be the same")) if self.properties is None: self.properties = {} # these properties are auto-generated and should not be saved - for attr in ['status', 'status_changed', 'modified', 'created']: + for attr in ["status", "status_changed", "modified", "created"]: if attr in self.properties: del self.properties[attr] def validate_topology(self): errors = {} if self.source.topology_id != self.topology_id: - errors['source'] = _('Source node and link should have same topology.') + errors["source"] = _("Source node and link should have same topology.") if self.target.topology_id != self.topology_id: - errors['target'] = _('Target node and link should have same topology.') + errors["target"] = _("Target node and link should have same topology.") if errors: raise ValidationError(errors) @@ -115,16 +115,16 @@ def validate_organization(self): if self.source.organization_id != self.topology.organization_id: errors.update( { - 'source': _( - 'Source node and topology should have same organization.' + "source": _( + "Source node and topology should have same organization." ) } ) if self.target.organization_id != self.topology.organization_id: errors.update( { - 'target': _( - 'Target node and topology should have same organization.' + "target": _( + "Target node and topology should have same organization." ) } ) @@ -142,10 +142,10 @@ def json(self, dict=False, original=False, **kwargs): """ netjson = OrderedDict( ( - ('source', self.source.netjson_id), - ('target', self.target.netjson_id), - ('cost', self.cost), - ('cost_text', self.cost_text or ''), + ("source", self.source.netjson_id), + ("target", self.target.netjson_id), + ("cost", self.cost), + ("cost_text", self.cost_text or ""), ) ) # properties contain status by default @@ -154,11 +154,11 @@ def json(self, dict=False, original=False, **kwargs): properties.update(self.properties) if not original: properties.update(self.user_properties) - properties['status'] = self.status - properties['status_changed'] = self.status_changed - properties['created'] = self.created - properties['modified'] = self.modified - netjson['properties'] = properties + properties["status"] = self.status + properties["status_changed"] = self.status_changed + properties["created"] = self.created + properties["modified"] = self.modified + netjson["properties"] = properties if dict: return netjson return json.dumps(netjson, cls=JSONEncoder, **kwargs) @@ -195,21 +195,21 @@ def delete_expired_links(cls): if LINK_EXPIRATION not in [False, None]: expiration_date = now() - timedelta(days=int(LINK_EXPIRATION)) expired_links = cls.objects.filter( - status='down', modified__lt=expiration_date + status="down", modified__lt=expiration_date ) expired_links_length = len(expired_links) if expired_links_length: - print_info('Deleting {0} expired links'.format(expired_links_length)) + print_info("Deleting {0} expired links".format(expired_links_length)) for link in expired_links: link.delete() @classmethod def get_queryset(cls, qs): """admin list queryset""" - return qs.select_related('organization', 'topology', 'source', 'target') + return qs.select_related("organization", "topology", "source", "target") -@receiver(post_save, sender=swapper.get_model_name('topology', 'Link')) -@receiver(post_delete, sender=swapper.get_model_name('topology', 'Link')) +@receiver(post_save, sender=swapper.get_model_name("topology", "Link")) +@receiver(post_delete, sender=swapper.get_model_name("topology", "Link")) def send_topology_signal(sender, instance, **kwargs): update_topology.send(sender=sender, topology=instance.topology) diff --git a/openwisp_network_topology/base/node.py b/openwisp_network_topology/base/node.py index 15a52551..483e9edd 100644 --- a/openwisp_network_topology/base/node.py +++ b/openwisp_network_topology/base/node.py @@ -28,7 +28,7 @@ class AbstractNode(ShareableOrgMixin, TimeStampedEditableModel): """ topology = models.ForeignKey( - swapper.get_model_name('topology', 'Topology'), on_delete=models.CASCADE + swapper.get_model_name("topology", "Topology"), on_delete=models.CASCADE ) label = models.CharField(max_length=64, blank=True) # netjson ID and local_addresses @@ -36,16 +36,16 @@ class AbstractNode(ShareableOrgMixin, TimeStampedEditableModel): properties = JSONField( default=dict, blank=True, - load_kwargs={'object_pairs_hook': OrderedDict}, - dump_kwargs={'indent': 4, 'cls': JSONEncoder}, + load_kwargs={"object_pairs_hook": OrderedDict}, + dump_kwargs={"indent": 4, "cls": JSONEncoder}, ) user_properties = JSONField( - verbose_name=_('user defined properties'), - help_text=_('If you need to add additional data to this node use this field'), + verbose_name=_("user defined properties"), + help_text=_("If you need to add additional data to this node use this field"), default=dict, blank=True, - load_kwargs={'object_pairs_hook': OrderedDict}, - dump_kwargs={'indent': 4, 'cls': JSONEncoder}, + load_kwargs={"object_pairs_hook": OrderedDict}, + dump_kwargs={"indent": 4, "cls": JSONEncoder}, ) class Meta: @@ -77,7 +77,7 @@ def local_addresses(self): @property def name(self): - return self.label or self.netjson_id or '' + return self.label or self.netjson_id or "" def get_name(self): """ @@ -102,7 +102,7 @@ def get_organization_id(self): and self.topology.organization_id != self.organization_id ): raise ValidationError( - _('node should have same organization as topology.') + _("node should have same organization as topology.") ) return self.organization_id @@ -114,18 +114,18 @@ def json(self, dict=False, original=False, **kwargs): as it has been collected from the network (used when doing the comparison). """ - netjson = OrderedDict({'id': self.netjson_id}) + netjson = OrderedDict({"id": self.netjson_id}) label = self.get_name() if label: - netjson['label'] = label - for attr in ['local_addresses', 'properties']: + netjson["label"] = label + for attr in ["local_addresses", "properties"]: value = getattr(self, attr) - if value or attr == 'properties': + if value or attr == "properties": netjson[attr] = deepcopy(value) if not original: - netjson['properties'].update(deepcopy(self.user_properties)) - netjson['properties']['created'] = JSONEncoder().default(self.created) - netjson['properties']['modified'] = JSONEncoder().default(self.modified) + netjson["properties"].update(deepcopy(self.user_properties)) + netjson["properties"]["created"] = JSONEncoder().default(self.created) + netjson["properties"]["modified"] = JSONEncoder().default(self.modified) if dict: return netjson return json.dumps(netjson, cls=JSONEncoder, **kwargs) @@ -177,17 +177,17 @@ def delete_expired_nodes(cls): ) expired_nodes_length = len(expired_nodes) if expired_nodes_length: - print_info('Deleting {0} expired nodes'.format(expired_nodes_length)) + print_info("Deleting {0} expired nodes".format(expired_nodes_length)) for node in expired_nodes: node.delete() @classmethod def get_queryset(cls, qs): """admin list queryset""" - return qs.select_related('organization', 'topology') + return qs.select_related("organization", "topology") -@receiver(post_save, sender=swapper.get_model_name('topology', 'Node')) -@receiver(post_delete, sender=swapper.get_model_name('topology', 'Node')) +@receiver(post_save, sender=swapper.get_model_name("topology", "Node")) +@receiver(post_delete, sender=swapper.get_model_name("topology", "Node")) def send_topology_signal(sender, instance, **kwargs): update_topology.send(sender=sender, topology=instance.topology) diff --git a/openwisp_network_topology/base/snapshot.py b/openwisp_network_topology/base/snapshot.py index ed5ae879..1db9a9b2 100644 --- a/openwisp_network_topology/base/snapshot.py +++ b/openwisp_network_topology/base/snapshot.py @@ -11,13 +11,13 @@ class AbstractSnapshot(TimeStampedEditableModel): """ topology = models.ForeignKey( - swapper.get_model_name('topology', 'Topology'), on_delete=models.CASCADE + swapper.get_model_name("topology", "Topology"), on_delete=models.CASCADE ) data = models.TextField(blank=False) date = models.DateField(auto_now=True) class Meta: - verbose_name_plural = _('snapshots') + verbose_name_plural = _("snapshots") abstract = True def __str__(self): diff --git a/openwisp_network_topology/base/topology.py b/openwisp_network_topology/base/topology.py index 65038dd2..6a0cd6cf 100644 --- a/openwisp_network_topology/base/topology.py +++ b/openwisp_network_topology/base/topology.py @@ -24,79 +24,79 @@ from ..tasks import handle_update_topology from ..utils import print_info -STRATEGIES = (('fetch', _('FETCH')), ('receive', _('RECEIVE'))) +STRATEGIES = (("fetch", _("FETCH")), ("receive", _("RECEIVE"))) class AbstractTopology(ShareableOrgMixin, TimeStampedEditableModel): - label = models.CharField(_('label'), max_length=64) + label = models.CharField(_("label"), max_length=64) parser = models.CharField( - _('format'), + _("format"), choices=PARSERS, max_length=128, - help_text=_('Select topology format'), + help_text=_("Select topology format"), ) strategy = models.CharField( - _('strategy'), max_length=16, choices=STRATEGIES, default='fetch', db_index=True + _("strategy"), max_length=16, choices=STRATEGIES, default="fetch", db_index=True ) # fetch strategy url = models.URLField( - _('url'), + _("url"), blank=True, - help_text=_('Topology data will be fetched from this URL' ' (FETCH strategy)'), + help_text=_("Topology data will be fetched from this URL" " (FETCH strategy)"), ) # receive strategy key = KeyField( unique=False, db_index=False, - help_text=_('key needed to update topology from nodes '), - verbose_name=_('key'), + help_text=_("key needed to update topology from nodes "), + verbose_name=_("key"), blank=True, ) # receive strategy expiration_time = models.PositiveIntegerField( - _('expiration time'), + _("expiration time"), default=0, help_text=_( '"Expiration Time" in seconds: setting this to 0 will immediately mark missing links ' - 'as down; a value higher than 0 will delay marking missing links as down until the ' + "as down; a value higher than 0 will delay marking missing links as down until the " '"modified" field of a link is older than "Expiration Time"' ), ) published = models.BooleanField( - _('published'), + _("published"), default=True, help_text=_( - 'Unpublished topologies won\'t be updated or ' 'shown in the visualizer' + "Unpublished topologies won't be updated or " "shown in the visualizer" ), ) # the following fields will be filled automatically - protocol = models.CharField(_('protocol'), max_length=64, blank=True) - version = models.CharField(_('version'), max_length=24, blank=True) - revision = models.CharField(_('revision'), max_length=64, blank=True) - metric = models.CharField(_('metric'), max_length=24, blank=True) + protocol = models.CharField(_("protocol"), max_length=64, blank=True) + version = models.CharField(_("version"), max_length=24, blank=True) + revision = models.CharField(_("revision"), max_length=64, blank=True) + metric = models.CharField(_("metric"), max_length=24, blank=True) - status = {'added': 'up', 'removed': 'down', 'changed': 'up'} - action = {'added': 'add', 'changed': 'change', 'removed': 'remove'} + status = {"added": "up", "removed": "down", "changed": "up"} + action = {"added": "add", "changed": "change", "removed": "remove"} class Meta: - verbose_name_plural = _('topologies') + verbose_name_plural = _("topologies") abstract = True def __str__(self): - return '{0} - {1}'.format(self.label, self.get_parser_display()) + return "{0} - {1}".format(self.label, self.get_parser_display()) def get_absolute_url(self): - return reverse('topology_detail', args=[self.pk]) + return reverse("topology_detail", args=[self.pk]) def clean(self): - if self.strategy == 'fetch' and not self.url: + if self.strategy == "fetch" and not self.url: raise ValidationError( - {'url': [_('an url must be specified when using FETCH strategy')]} + {"url": [_("an url must be specified when using FETCH strategy")]} ) - elif self.strategy == 'receive' and not self.key: + elif self.strategy == "receive" and not self.key: raise ValidationError( - {'key': [_('a key must be specified when using RECEIVE strategy')]} + {"key": [_("a key must be specified when using RECEIVE strategy")]} ) @cached_property @@ -123,7 +123,7 @@ def get_topology_data(self, data=None): latest = self.parser_class(data=data, url=self.url, timeout=TIMEOUT) # update topology attributes if needed changed = False - for attr in ['protocol', 'version', 'metric']: + for attr in ["protocol", "version", "metric"]: latest_attr = getattr(latest, attr) if getattr(self, attr) != latest_attr: setattr(self, attr, latest_attr) @@ -147,7 +147,7 @@ def get_nodes_queryset(self): return self.node_set.all() def get_links_queryset(self): - return self.link_set.select_related('source', 'target') + return self.link_set.select_related("source", "target") def json(self, dict=False, omit_down=False, original=False, **kwargs): """returns a dict that represents a NetJSON NetworkGraph object""" @@ -156,7 +156,7 @@ def json(self, dict=False, omit_down=False, original=False, **kwargs): links_queryset = self.get_links_queryset() # needed to detect links coming back online if omit_down: - links_queryset = links_queryset.filter(status='up') + links_queryset = links_queryset.filter(status="up") # populate graph for link in links_queryset: links.append(link.json(dict=True, original=original)) @@ -164,17 +164,17 @@ def json(self, dict=False, omit_down=False, original=False, **kwargs): nodes.append(node.json(dict=True, original=original)) netjson = OrderedDict( ( - ('type', 'NetworkGraph'), - ('protocol', self.protocol), - ('version', self.version), - ('metric', self.metric), - ('label', self.label), - ('id', str(self.id)), - ('parser', self.parser), - ('created', self.created), - ('modified', self.modified), - ('nodes', nodes), - ('links', links), + ("type", "NetworkGraph"), + ("protocol", self.protocol), + ("version", self.version), + ("metric", self.metric), + ("label", self.label), + ("id", str(self.id)), + ("parser", self.parser), + ("created", self.created), + ("modified", self.modified), + ("nodes", nodes), + ("links", links), ) ) if dict: @@ -196,40 +196,40 @@ def _create_link(self, **kwargs): def _update_added_items(self, items): Link, Node = self.link_model, self.node_model - for node_dict in items.get('nodes', []): + for node_dict in items.get("nodes", []): # if node exists, update its properties - node = Node.get_from_address(node_dict['id'], topology=self) + node = Node.get_from_address(node_dict["id"], topology=self) if node: - self._update_node_properties(node, node_dict, section='added') + self._update_node_properties(node, node_dict, section="added") continue # if node doesn't exist create new - addresses = [node_dict['id']] - addresses += node_dict.get('local_addresses', []) - label = node_dict.get('label', '') - properties = node_dict.get('properties', {}) + addresses = [node_dict["id"]] + addresses += node_dict.get("local_addresses", []) + label = node_dict.get("label", "") + properties = node_dict.get("properties", {}) node = self._create_node( label=label, addresses=addresses, properties=properties ) node.full_clean() node.save() - for link_dict in items.get('links', []): + for link_dict in items.get("links", []): link = Link.get_from_nodes( - link_dict['source'], link_dict['target'], topology=self + link_dict["source"], link_dict["target"], topology=self ) # if link exists, update its properties if link: - self._update_link_properties(link, link_dict, section='added') + self._update_link_properties(link, link_dict, section="added") continue # if link does not exist create new - source = Node.get_from_address(link_dict['source'], self) - target = Node.get_from_address(link_dict['target'], self) + source = Node.get_from_address(link_dict["source"], self) + target = Node.get_from_address(link_dict["target"], self) link = self._create_link( source=source, target=target, - cost=link_dict['cost'], - cost_text=link_dict['cost_text'], - properties=link_dict['properties'], + cost=link_dict["cost"], + cost_text=link_dict["cost_text"], + properties=link_dict["properties"], topology=self, ) link.full_clean() @@ -237,18 +237,18 @@ def _update_added_items(self, items): def _update_node_properties(self, node, node_dict, section): changed = False - if node.label != node_dict.get('label'): + if node.label != node_dict.get("label"): changed = True - node.label = node_dict.get('label') - local_addresses = node_dict.get('local_addresses') + node.label = node_dict.get("label") + local_addresses = node_dict.get("local_addresses") if node.addresses != local_addresses: changed = True - node.addresses = [node_dict['id']] + node.addresses = [node_dict["id"]] if local_addresses: node.addresses += local_addresses - if node.properties != node_dict.get('properties'): + if node.properties != node_dict.get("properties"): changed = True - node.properties = node_dict.get('properties') + node.properties = node_dict.get("properties") # perform writes only if needed if changed: with log_failure(self.action[section], node): @@ -261,14 +261,14 @@ def _update_link_properties(self, link, link_dict, section): if self.link_status_changed(link, self.status[section]): link.status = self.status[section] changed = True - if link.cost != link_dict.get('cost'): - link.cost = link_dict.get('cost') + if link.cost != link_dict.get("cost"): + link.cost = link_dict.get("cost") changed = True - if link.cost_text != link_dict.get('cost_text'): - link.cost_text = link_dict.get('cost_text') + if link.cost_text != link_dict.get("cost_text"): + link.cost_text = link_dict.get("cost_text") changed = True - if link.properties != link_dict.get('properties'): - link.properties = link_dict.get('properties') + if link.properties != link_dict.get("properties"): + link.properties = link_dict.get("properties") changed = True # perform writes only if needed if changed: @@ -276,33 +276,33 @@ def _update_link_properties(self, link, link_dict, section): link.full_clean() link.save() - def _update_changed_items(self, items, section='changed'): + def _update_changed_items(self, items, section="changed"): Link, Node = self.link_model, self.node_model - for node_dict in items.get('nodes', []): - node = Node.get_from_address(node_dict['id'], topology=self) + for node_dict in items.get("nodes", []): + node = Node.get_from_address(node_dict["id"], topology=self) if node: self._update_node_properties(node, node_dict, section=section) - for link_dict in items.get('links', []): + for link_dict in items.get("links", []): link = Link.get_from_nodes( - link_dict['source'], link_dict['target'], topology=self + link_dict["source"], link_dict["target"], topology=self ) if link: self._update_link_properties(link, link_dict, section=section) def update_topology(self, diff): - if diff['added']: - self._update_added_items(diff['added']) - if diff['changed']: - self._update_changed_items(diff['changed']) - if diff['removed']: + if diff["added"]: + self._update_added_items(diff["added"]) + if diff["changed"]: + self._update_changed_items(diff["changed"]) + if diff["removed"]: Link = self.link_model - for link_dict in diff['removed'].get('links', []): + for link_dict in diff["removed"].get("links", []): link = Link.get_from_nodes( - link_dict['source'], link_dict['target'], topology=self + link_dict["source"], link_dict["target"], topology=self ) if link: - self._update_link_properties(link, link_dict, section='removed') + self._update_link_properties(link, link_dict, section="removed") def update(self, data=None): """ @@ -340,7 +340,7 @@ def link_status_changed(self, link, status): # if using fetch strategy or # using receive strategy and link is coming back up or # receive strategy and ``expiration_time == 0`` - elif self.strategy == 'fetch' or status == 'up' or self.expiration_time == 0: + elif self.strategy == "fetch" or status == "up" or self.expiration_time == 0: return True # if using receive strategy and expiration_time of link has expired elif link.modified < (now() - timedelta(seconds=self.expiration_time)): @@ -361,9 +361,9 @@ def receive(self, data): Link = self.link_model netjson = data.json(dict=True) # update last modified date of all received links - for link_dict in netjson['links']: + for link_dict in netjson["links"]: link = Link.get_from_nodes( - link_dict['source'], link_dict['target'], topology=self + link_dict["source"], link_dict["target"], topology=self ) if link: link.save() @@ -376,12 +376,12 @@ def update_all(cls, label=None): - logs failures - calls delete_expired_links() """ - queryset = cls.objects.filter(published=True, strategy='fetch') + queryset = cls.objects.filter(published=True, strategy="fetch") if label: queryset = queryset.filter(label__icontains=label) for topology in queryset: - print_info('Updating topology {0}'.format(topology)) - with log_failure('update', topology): + print_info("Updating topology {0}".format(topology)) + with log_failure("update", topology): topology.update() cls().link_model.delete_expired_links() cls().node_model.delete_expired_nodes() @@ -396,11 +396,11 @@ def save_snapshot_all(cls, label=None): if label: queryset = queryset.filter(label__icontains=label) for topology in queryset: - print_info('Saving topology {0}'.format(topology)) - with log_failure('save_snapshot', topology): + print_info("Saving topology {0}".format(topology)) + with log_failure("save_snapshot", topology): topology.save_snapshot() -@receiver(post_save, sender=swapper.get_model_name('topology', 'Topology')) +@receiver(post_save, sender=swapper.get_model_name("topology", "Topology")) def send_topology_signal(sender, instance, **kwargs): update_topology.send(sender=sender, topology=instance) diff --git a/openwisp_network_topology/consumers.py b/openwisp_network_topology/consumers.py index 4f4fefb6..4e8563b4 100644 --- a/openwisp_network_topology/consumers.py +++ b/openwisp_network_topology/consumers.py @@ -7,11 +7,11 @@ from . import settings as app_settings -Topology = load_model('topology', 'Topology') +Topology = load_model("topology", "Topology") class TopologyConsumer(WebsocketConsumer): - channel_layer_group = 'topology' + channel_layer_group = "topology" def _is_user_authorized_to_view_topology(self, user, topology_pk): try: @@ -23,13 +23,13 @@ def _is_user_authorized_to_view_topology(self, user, topology_pk): return user.is_superuser or ( user.is_authenticated and user.is_manager(topology.organization_id) - and user.has_perm(f'{Topology._meta.app_label}.view_topology') + and user.has_perm(f"{Topology._meta.app_label}.view_topology") ) def connect(self): - user, topology_pk = self.scope.get('user'), self.scope.get('url_route').get( - 'kwargs' - ).get('pk') + user, topology_pk = self.scope.get("user"), self.scope.get("url_route").get( + "kwargs" + ).get("pk") if self._is_user_authorized_to_view_topology(user, topology_pk): async_to_sync(self.channel_layer.group_add)( f"{self.channel_layer_group}-{topology_pk}", self.channel_name @@ -45,8 +45,8 @@ def send_topology_update(self, event): self.send( text_data=json.dumps( { - 'type': "broadcast_topology", - 'topology': event['data'], + "type": "broadcast_topology", + "topology": event["data"], } ) ) diff --git a/openwisp_network_topology/contextmanagers.py b/openwisp_network_topology/contextmanagers.py index e47c4622..96be865b 100644 --- a/openwisp_network_topology/contextmanagers.py +++ b/openwisp_network_topology/contextmanagers.py @@ -9,9 +9,9 @@ def log_failure(action, obj): try: yield except Exception as e: - msg = 'Failed to perform {0} on {1}'.format(action, obj.__repr__()) + msg = "Failed to perform {0} on {1}".format(action, obj.__repr__()) logger.exception(msg) print( - '{0}: {1}\nSee error log for more ' - 'information\n'.format(msg, e.__class__.__name__) + "{0}: {1}\nSee error log for more " + "information\n".format(msg, e.__class__.__name__) ) diff --git a/openwisp_network_topology/integrations/device/admin.py b/openwisp_network_topology/integrations/device/admin.py index 9e76c8fc..c4c58761 100644 --- a/openwisp_network_topology/integrations/device/admin.py +++ b/openwisp_network_topology/integrations/device/admin.py @@ -5,8 +5,8 @@ from . import settings as app_settings -WifiMesh = load_model('topology_device', 'WifiMesh') -Topology = load_model('topology', 'Topology') +WifiMesh = load_model("topology_device", "WifiMesh") +Topology = load_model("topology", "Topology") class WifiMeshInlineAdmin(admin.StackedInline): diff --git a/openwisp_network_topology/integrations/device/apps.py b/openwisp_network_topology/integrations/device/apps.py index 7f075289..8ad941c9 100644 --- a/openwisp_network_topology/integrations/device/apps.py +++ b/openwisp_network_topology/integrations/device/apps.py @@ -11,25 +11,25 @@ class OpenwispTopologyDeviceConfig(AppConfig): - name = 'openwisp_network_topology.integrations.device' - label = 'topology_device' - verbose_name = _('Topology Device Integration') + name = "openwisp_network_topology.integrations.device" + label = "topology_device" + verbose_name = _("Topology Device Integration") def ready(self): self.connect_signals() self.override_node_label() def connect_signals(self): - Node = swapper.load_model('topology', 'Node') - Link = swapper.load_model('topology', 'Link') + Node = swapper.load_model("topology", "Node") + Link = swapper.load_model("topology", "Link") post_save.connect( - self.create_device_rel, sender=Node, dispatch_uid='node_to_device_rel' + self.create_device_rel, sender=Node, dispatch_uid="node_to_device_rel" ) link_status_changed.connect( self.link_status_changed_receiver, sender=Link, - dispatch_uid='controller_integration_link_status_chaged', + dispatch_uid="controller_integration_link_status_chaged", ) @classmethod @@ -43,4 +43,4 @@ def link_status_changed_receiver(cls, link, **kwargs): transaction.on_commit(lambda: trigger_device_updates.delay(link.pk)) def override_node_label(self): - import_module('openwisp_network_topology.integrations.device.overrides') + import_module("openwisp_network_topology.integrations.device.overrides") diff --git a/openwisp_network_topology/integrations/device/base/models.py b/openwisp_network_topology/integrations/device/base/models.py index 8307171b..cbf38c1d 100644 --- a/openwisp_network_topology/integrations/device/base/models.py +++ b/openwisp_network_topology/integrations/device/base/models.py @@ -15,38 +15,38 @@ logger = logging.getLogger(__name__) -trigger_device_checks_path = 'openwisp_monitoring.device.tasks.trigger_device_checks' +trigger_device_checks_path = "openwisp_monitoring.device.tasks.trigger_device_checks" class AbstractDeviceNode(UUIDModel): node = models.OneToOneField( - get_model_name('topology', 'Node'), on_delete=models.CASCADE + get_model_name("topology", "Node"), on_delete=models.CASCADE ) device = models.ForeignKey( - get_model_name('config', 'Device'), on_delete=models.CASCADE + get_model_name("config", "Device"), on_delete=models.CASCADE ) # relations will be auto-created only for these parsers ENABLED_PARSERS = { - 'netdiff.OpenvpnParser': { - 'auto_create': 'auto_create_openvpn', - 'link_down': 'link_down_openvpn', - 'link_up': 'link_up_openvpn', + "netdiff.OpenvpnParser": { + "auto_create": "auto_create_openvpn", + "link_down": "link_down_openvpn", + "link_up": "link_up_openvpn", }, - 'netdiff.WireguardParser': { - 'auto_create': 'auto_create_wireguard', + "netdiff.WireguardParser": { + "auto_create": "auto_create_wireguard", }, - 'netdiff.ZeroTierParser': { - 'auto_create': 'auto_create_zerotier', + "netdiff.ZeroTierParser": { + "auto_create": "auto_create_zerotier", }, - 'netdiff.NetJsonParser': { - 'auto_create': 'auto_create_netjsongraph', + "netdiff.NetJsonParser": { + "auto_create": "auto_create_netjsongraph", }, } class Meta: abstract = True - unique_together = ('node', 'device') + unique_together = ("node", "device") @classmethod def save_device_node(cls, device, node): @@ -58,12 +58,12 @@ def save_device_node(cls, device, node): # when topology is shared. if node.organization_id is None: node.organization_id = device.organization_id - node.save(update_fields=['organization_id']) + node.save(update_fields=["organization_id"]) except Exception: - logger.exception('Exception raised during save_device_node') + logger.exception("Exception raised during save_device_node") return else: - logger.info(f'DeviceNode relation created for {node.label} - {device.name}') + logger.info(f"DeviceNode relation created for {node.label} - {device.name}") return device_node @classmethod @@ -74,7 +74,7 @@ def auto_create(cls, node): """ opts = cls.ENABLED_PARSERS.get(node.topology.parser) if opts: - return getattr(cls, opts['auto_create'])(node) + return getattr(cls, opts["auto_create"])(node) @classmethod def auto_create_openvpn(cls, node): @@ -83,17 +83,17 @@ def auto_create_openvpn(cls, node): controller and network-topology modules when using OpenVPN (using the common name) """ - common_name = node.properties.get('common_name') + common_name = node.properties.get("common_name") if not common_name: return - Device = load_model('config', 'Device') + Device = load_model("config", "Device") device_filter = models.Q(config__vpnclient__cert__common_name=common_name) if node.organization_id: device_filter &= models.Q(organization_id=node.organization_id) device = ( Device.objects.only( - 'id', 'name', 'last_ip', 'management_ip', 'organization_id' + "id", "name", "last_ip", "management_ip", "organization_id" ) .filter(device_filter) .first() @@ -105,10 +105,10 @@ def auto_create_openvpn(cls, node): @classmethod def auto_create_wireguard(cls, node): - allowed_ips = node.properties.get('allowed_ips') + allowed_ips = node.properties.get("allowed_ips") if not allowed_ips: return - Device = load_model('config', 'Device') + Device = load_model("config", "Device") ip_addresses = [] for ip in allowed_ips: try: @@ -121,7 +121,7 @@ def auto_create_wireguard(cls, node): device_filter &= models.Q(organization_id=node.organization_id) device = ( Device.objects.only( - 'id', 'name', 'last_ip', 'management_ip', 'organization_id' + "id", "name", "last_ip", "management_ip", "organization_id" ) .filter(device_filter) .first() @@ -137,11 +137,11 @@ def auto_create_zerotier(cls, node): controller and network-topology modules when using ZeroTier (using the `zerotier_member_id`) """ - zerotier_member_id = node.properties.get('address') + zerotier_member_id = node.properties.get("address") if not zerotier_member_id: return - Device = load_model('config', 'Device') + Device = load_model("config", "Device") device_filter = models.Q( config__vpnclient__secret__startswith=zerotier_member_id ) @@ -149,7 +149,7 @@ def auto_create_zerotier(cls, node): device_filter &= models.Q(organization_id=node.organization_id) device = ( Device.objects.only( - 'id', 'name', 'last_ip', 'management_ip', 'organization_id' + "id", "name", "last_ip", "management_ip", "organization_id" ) .filter(device_filter) .first() @@ -166,15 +166,15 @@ def auto_create_netjsongraph(cls, node): # If a node only has one MAC address, it means that # it only contains interface MAC address. return - Device = load_model('config', 'Device') + Device = load_model("config", "Device") device_filter = models.Q( - mac_address__iexact=node.addresses[0].rpartition('@')[0] + mac_address__iexact=node.addresses[0].rpartition("@")[0] ) if node.organization_id: device_filter &= models.Q(organization_id=node.organization_id) device = ( Device.objects.only( - 'id', 'name', 'last_ip', 'management_ip', 'organization_id' + "id", "name", "last_ip", "management_ip", "organization_id" ) .filter(device_filter) .first() @@ -190,7 +190,7 @@ def link_action(self, link, status): """ opts = self.ENABLED_PARSERS.get(link.topology.parser) if opts: - key = f'link_{status}' + key = f"link_{status}" if key in opts and hasattr(self, opts[key]): return getattr(self, opts[key])() @@ -209,9 +209,9 @@ def link_up_openvpn(self): try: address = ip_address(addresses[1]) except (IndexError, ValueError) as e: - addresses = ', '.join(addresses) + addresses = ", ".join(addresses) logger.warning( - f'{e.__class__.__name__} raised while processing addresses: {addresses}' + f"{e.__class__.__name__} raised while processing addresses: {addresses}" ) else: self.device.management_ip = str(address) @@ -226,7 +226,7 @@ def filter_by_link(cls, link): return cls.objects.filter( models.Q(node__source_link_set__pk=link.pk) | models.Q(node__target_link_set__pk=link.pk) - ).select_related('device', 'node') + ).select_related("device", "node") @classmethod def trigger_device_updates(cls, link): @@ -239,37 +239,37 @@ def trigger_device_updates(cls, link): for device_node in cls.filter_by_link(link): device_node.link_action(link, link.status) # triggers monitoring checks if OpenWISP Monitoring is enabled - if 'openwisp_monitoring.device' in settings.INSTALLED_APPS: + if "openwisp_monitoring.device" in settings.INSTALLED_APPS: run_checks = import_string(trigger_device_checks_path) - run_checks.delay(device_node.device.pk, recovery=link.status == 'up') + run_checks.delay(device_node.device.pk, recovery=link.status == "up") class AbstractWifiMesh(UUIDModel): _NODE_PROPERTIES = [ - 'ht', - 'vht', - 'he', - 'mfp', - 'wmm', - 'vendor', + "ht", + "vht", + "he", + "mfp", + "wmm", + "vendor", ] _LINK_PROPERTIES = [ - 'auth', - 'authorized', - 'noise', - 'signal', - 'signal_avg', - 'mesh_llid', - 'mesh_plid', - 'mesh_plink', - 'mesh_non_peer_ps', + "auth", + "authorized", + "noise", + "signal", + "signal_avg", + "mesh_llid", + "mesh_plid", + "mesh_plink", + "mesh_non_peer_ps", ] topology = models.ForeignKey( - get_model_name('topology', 'Topology'), on_delete=models.CASCADE + get_model_name("topology", "Topology"), on_delete=models.CASCADE ) mesh_id = models.CharField( - max_length=32, null=False, blank=False, verbose_name=_('Mesh ID') + max_length=32, null=False, blank=False, verbose_name=_("Mesh ID") ) class Meta: @@ -281,7 +281,7 @@ def create_topology(cls, organization_ids, discard_older_data_time): raise ImproperlyConfigured( '"OPENIWSP_NETWORK_TOPOLOGY_WIFI_MESH_INTEGRATION" is set to "False".' ) - Link = load_model('topology', 'Link') + Link = load_model("topology", "Link") for org_id in organization_ids: intermediate_topologies = cls._create_intermediate_topologies( org_id, discard_older_data_time @@ -290,7 +290,7 @@ def create_topology(cls, organization_ids, discard_older_data_time): Link.objects.filter( topology__wifimesh__isnull=False, organization_id=org_id ).exclude(topology__wifimesh__in=intermediate_topologies.keys()).update( - status='down' + status="down" ) continue cls._create_topology(intermediate_topologies, org_id) @@ -309,10 +309,10 @@ def _create_intermediate_topologies(cls, organization_id, discard_older_data_tim These individual topologies are then complied to create the complete mesh topology. """ - DeviceData = load_model('device_monitoring', 'DeviceData') + DeviceData = load_model("device_monitoring", "DeviceData") intermediate_topologies = {} query = DeviceData.objects.filter(organization_id=organization_id).only( - 'mac_address' + "mac_address" ) discard_older_data_time = now() - timedelta(seconds=discard_older_data_time) for device_data in query.iterator(): @@ -322,30 +322,30 @@ def _create_intermediate_topologies(cls, organization_id, discard_older_data_tim assert data_timestamp > discard_older_data_time except (AttributeError, AssertionError): continue - for interface in device_data.data.get('interfaces', []): + for interface in device_data.data.get("interfaces", []): if not AbstractWifiMesh._is_mesh_interfaces(interface): continue - mesh_id = '{}@{}'.format( - interface['wireless']['ssid'], interface['wireless']['channel'] + mesh_id = "{}@{}".format( + interface["wireless"]["ssid"], interface["wireless"]["channel"] ) if mesh_id not in intermediate_topologies: intermediate_topologies[mesh_id] = { - 'nodes': {}, - 'links': {}, - 'mac_mapping': {}, + "nodes": {}, + "links": {}, + "mac_mapping": {}, } topology = intermediate_topologies[mesh_id] ( collected_nodes, collected_links, ) = cls._get_intermediate_nodes_and_links( - interface, device_data, topology['mac_mapping'] + interface, device_data, topology["mac_mapping"] ) AbstractWifiMesh._merge_nodes( - interface, topology['nodes'], collected_nodes + interface, topology["nodes"], collected_nodes ) AbstractWifiMesh._merge_links( - interface, topology['links'], collected_links + interface, topology["links"], collected_links ) return intermediate_topologies @@ -360,20 +360,20 @@ def _get_intermediate_nodes_and_links(cls, interface, device_data, mac_mapping): device MAC address. """ device_mac = device_data.mac_address.upper() - interface_mac = interface['mac'].upper() - channel = interface['wireless']['channel'] - device_node_id = f'{device_mac}@{channel}' + interface_mac = interface["mac"].upper() + channel = interface["wireless"]["channel"] + device_node_id = f"{device_mac}@{channel}" mac_mapping[interface_mac] = device_node_id collected_nodes = {} collected_links = {} - for client in interface['wireless'].get('clients', []): - client_mac = client['mac'].upper() + for client in interface["wireless"].get("clients", []): + client_mac = client["mac"].upper() node_properties = {} for property in cls._NODE_PROPERTIES: if property in client: node_properties[property] = client[property] - collected_nodes[client_mac] = {'properties': node_properties} - if client.get('mesh_plink') and client.get('mesh_plink') != 'ESTAB': + collected_nodes[client_mac] = {"properties": node_properties} + if client.get("mesh_plink") and client.get("mesh_plink") != "ESTAB": # The link is not established. # Do not add this link to the topology. continue @@ -384,23 +384,23 @@ def _get_intermediate_nodes_and_links(cls, interface, device_data, mac_mapping): collected_links[client_mac] = { interface_mac: { - 'source': f'{device_mac}@{channel}', - 'target': f'{client_mac}', - 'cost': 1.0, - 'properties': link_properties, + "source": f"{device_mac}@{channel}", + "target": f"{client_mac}", + "cost": 1.0, + "properties": link_properties, } } return collected_nodes, collected_links @staticmethod def _is_mesh_interfaces(interface): - return interface.get('wireless', False) and interface['wireless'].get( - 'mode' - ) in ['802.11s'] + return interface.get("wireless", False) and interface["wireless"].get( + "mode" + ) in ["802.11s"] @staticmethod def _merge_nodes(interface, topology_nodes, collected_nodes): - interface_mac = interface['mac'].upper() + interface_mac = interface["mac"].upper() topology_nodes.update(collected_nodes) if not topology_nodes.get(interface_mac): # Handle case when there is only one node present @@ -417,39 +417,39 @@ def _merge_links(interface, topology_links, collected_links): It takes into consideration the nature of the property (e.g. taking average for signal and noise, etc.). """ - interface_mac = interface['mac'].upper() + interface_mac = interface["mac"].upper() if topology_links.get(interface_mac): for source, topology_link in topology_links[interface_mac].items(): if not collected_links.get(source): continue for property, value in collected_links[source][interface_mac][ - 'properties' + "properties" ].items(): if isinstance(value, int): # If the value is integer, then take average of the # values provided by the two nodes. # This is for fields like signal and noise. - if topology_link['properties'].get(property): - topology_link['properties'][property] = ( - value + topology_link['properties'][property] + if topology_link["properties"].get(property): + topology_link["properties"][property] = ( + value + topology_link["properties"][property] ) // 2 else: # The link in topology does not contain this property, # hence instead of averaging, assign the current value. - topology_link['properties'][property] = value + topology_link["properties"][property] = value elif ( - property in ['mesh_plink', 'mesh_non_peer_ps'] - and topology_link['properties'].get(property) - and value != topology_link['properties'][property] + property in ["mesh_plink", "mesh_non_peer_ps"] + and topology_link["properties"].get(property) + and value != topology_link["properties"][property] ): # The value for "mesh_plink" and "mesh_non_peer_ps" properties # should be reported same by both the nodes. # Flag the value as inconsistent if they are different. - topology_link['properties'][ - property - ] = 'INCONSISTENT: ({} / {})'.format( - value, - topology_link['properties'][property], + topology_link["properties"][property] = ( + "INCONSISTENT: ({} / {})".format( + value, + topology_link["properties"][property], + ) ) collected_links.pop(source) for key, value in collected_links.items(): @@ -465,12 +465,12 @@ def _create_topology(intermediate_topologies, organization_id): nodes = AbstractWifiMesh._get_nodes_from_intermediate_topology(intermediate) links = AbstractWifiMesh._get_links_from_intermediate_topology(intermediate) topology = { - 'type': 'NetworkGraph', - 'protocol': 'Mesh', - 'version': '1', - 'metric': 'Airtime', - 'nodes': nodes, - 'links': links, + "type": "NetworkGraph", + "protocol": "Mesh", + "version": "1", + "metric": "Airtime", + "nodes": nodes, + "links": links, } topology_obj.receive(topology) @@ -484,18 +484,18 @@ def _get_nodes_from_intermediate_topology(intermediate_topology): which is used to create DeviceNode relation. """ nodes = [] - mac_mapping = intermediate_topology['mac_mapping'] - for interface_mac, intermediate_node in intermediate_topology['nodes'].items(): + mac_mapping = intermediate_topology["mac_mapping"] + for interface_mac, intermediate_node in intermediate_topology["nodes"].items(): device_mac = mac_mapping.get(interface_mac) if not device_mac: continue node = { - 'id': device_mac, - 'label': device_mac, - 'local_addresses': [interface_mac], + "id": device_mac, + "label": device_mac, + "local_addresses": [interface_mac], } - if intermediate_node.get('properties'): - node['properties'] = intermediate_node['properties'] + if intermediate_node.get("properties"): + node["properties"] = intermediate_node["properties"] nodes.append(node) return nodes @@ -508,13 +508,13 @@ def _get_links_from_intermediate_topology(intermediate_topology): It maps the interface's MAC address to the device's main MAC address. """ links = [] - mac_mapping = intermediate_topology['mac_mapping'] - for interface_mac, intermediate_link in intermediate_topology['links'].items(): + mac_mapping = intermediate_topology["mac_mapping"] + for interface_mac, intermediate_link in intermediate_topology["links"].items(): device_mac = mac_mapping.get(interface_mac) if not device_mac: continue for link in intermediate_link.values(): - link['target'] = device_mac + link["target"] = device_mac links.append(link) return links @@ -525,11 +525,11 @@ def _get_mesh_topology(mesh_id, organization_id): It also creates WifiMesh object to keep track of mesh's ID if a new topology object is created. """ - Topology = load_model('topology', 'Topology') - WifiMesh = load_model('topology_device', 'WifiMesh') + Topology = load_model("topology", "Topology") + WifiMesh = load_model("topology_device", "WifiMesh") try: mesh_topology = ( - WifiMesh.objects.select_related('topology') + WifiMesh.objects.select_related("topology") .get( mesh_id=mesh_id, topology__organization_id=organization_id, @@ -540,8 +540,8 @@ def _get_mesh_topology(mesh_id, organization_id): mesh_topology = Topology( organization_id=organization_id, label=mesh_id, - parser='netdiff.NetJsonParser', - strategy='receive', + parser="netdiff.NetJsonParser", + strategy="receive", expiration_time=330, ) mesh_topology.full_clean() diff --git a/openwisp_network_topology/integrations/device/management/commands/__init__.py b/openwisp_network_topology/integrations/device/management/commands/__init__.py index 36590ef4..4e2bd747 100644 --- a/openwisp_network_topology/integrations/device/management/commands/__init__.py +++ b/openwisp_network_topology/integrations/device/management/commands/__init__.py @@ -4,15 +4,15 @@ class BaseCreateDeviceNodeCommand(BaseCommand): - help = 'Create initial DeviceNode objects' + help = "Create initial DeviceNode objects" def handle(self, *args, **kwargs): - Node = swapper.load_model('topology', 'Node') - DeviceNode = swapper.load_model('topology_device', 'DeviceNode') - queryset = Node.objects.select_related('topology').filter( - Q(topology__parser='netdiff.OpenvpnParser') - | Q(topology__parser='netdiff.WireguardParser') - | Q(topology__parser='netdiff.ZeroTierParser') + Node = swapper.load_model("topology", "Node") + DeviceNode = swapper.load_model("topology_device", "DeviceNode") + queryset = Node.objects.select_related("topology").filter( + Q(topology__parser="netdiff.OpenvpnParser") + | Q(topology__parser="netdiff.WireguardParser") + | Q(topology__parser="netdiff.ZeroTierParser") ) for node in queryset.iterator(): DeviceNode.auto_create(node) diff --git a/openwisp_network_topology/integrations/device/migrations/0001_initial.py b/openwisp_network_topology/integrations/device/migrations/0001_initial.py index e4954792..bd70de8d 100644 --- a/openwisp_network_topology/integrations/device/migrations/0001_initial.py +++ b/openwisp_network_topology/integrations/device/migrations/0001_initial.py @@ -11,16 +11,16 @@ class Migration(migrations.Migration): initial = True dependencies = [ - swapper.dependency('topology', 'Node'), - swapper.dependency('config', 'Device'), + swapper.dependency("topology", "Node"), + swapper.dependency("config", "Device"), ] operations = [ migrations.CreateModel( - name='DeviceNode', + name="DeviceNode", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -29,24 +29,24 @@ class Migration(migrations.Migration): ), ), ( - 'device', + "device", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to=swapper.get_model_name('config', 'Device'), + to=swapper.get_model_name("config", "Device"), ), ), ( - 'node', + "node", models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, - to=swapper.get_model_name('topology', 'Node'), + to=swapper.get_model_name("topology", "Node"), ), ), ], options={ - 'abstract': False, - 'swappable': swapper.swappable_setting('topology_device', 'DeviceNode'), - 'unique_together': {('node', 'device')}, + "abstract": False, + "swappable": swapper.swappable_setting("topology_device", "DeviceNode"), + "unique_together": {("node", "device")}, }, ) ] diff --git a/openwisp_network_topology/integrations/device/migrations/0002_wifimesh.py b/openwisp_network_topology/integrations/device/migrations/0002_wifimesh.py index beae89d5..419e0c0c 100644 --- a/openwisp_network_topology/integrations/device/migrations/0002_wifimesh.py +++ b/openwisp_network_topology/integrations/device/migrations/0002_wifimesh.py @@ -10,15 +10,15 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.TOPOLOGY_TOPOLOGY_MODEL), - ('topology_device', '0001_initial'), + ("topology_device", "0001_initial"), ] operations = [ migrations.CreateModel( - name='WifiMesh', + name="WifiMesh", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -26,9 +26,9 @@ class Migration(migrations.Migration): serialize=False, ), ), - ('mesh_id', models.CharField(max_length=32, verbose_name='Mesh ID')), + ("mesh_id", models.CharField(max_length=32, verbose_name="Mesh ID")), ( - 'topology', + "topology", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, to=settings.TOPOLOGY_TOPOLOGY_MODEL, @@ -36,8 +36,8 @@ class Migration(migrations.Migration): ), ], options={ - 'abstract': False, - 'swappable': 'TOPOLOGY_DEVICE_WIFIMESH_MODEL', + "abstract": False, + "swappable": "TOPOLOGY_DEVICE_WIFIMESH_MODEL", }, ), ] diff --git a/openwisp_network_topology/integrations/device/migrations/0003_wifimesh_permissions.py b/openwisp_network_topology/integrations/device/migrations/0003_wifimesh_permissions.py index bd8a0e80..f2420aa0 100644 --- a/openwisp_network_topology/integrations/device/migrations/0003_wifimesh_permissions.py +++ b/openwisp_network_topology/integrations/device/migrations/0003_wifimesh_permissions.py @@ -13,31 +13,31 @@ def _add_permission_to_group(group, models, operations): for operation in operations: try: permission = Permission.objects.get( - codename='{}_{}'.format(operation, model) + codename="{}_{}".format(operation, model) ) except Permission.DoesNotExist: continue else: group.permissions.add(permission.pk) - Group = apps.get_model('openwisp_users', 'Group') - manage_operations = ['add', 'change', 'delete', 'view'] + Group = apps.get_model("openwisp_users", "Group") + manage_operations = ["add", "change", "delete", "view"] try: - admin = Group.objects.get(name='Administrator') - operator = Group.objects.get(name='Operator') + admin = Group.objects.get(name="Administrator") + operator = Group.objects.get(name="Operator") # consider failures custom cases # that do not have to be dealt with except Group.DoesNotExist: return - _add_permission_to_group(operator, ['wifimesh'], manage_operations) - _add_permission_to_group(admin, ['wifimesh'], manage_operations) + _add_permission_to_group(operator, ["wifimesh"], manage_operations) + _add_permission_to_group(admin, ["wifimesh"], manage_operations) class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.TOPOLOGY_TOPOLOGY_MODEL), - ('topology_device', '0002_wifimesh'), + ("topology_device", "0002_wifimesh"), ] operations = [ diff --git a/openwisp_network_topology/integrations/device/models.py b/openwisp_network_topology/integrations/device/models.py index f3835b02..3c5c470c 100644 --- a/openwisp_network_topology/integrations/device/models.py +++ b/openwisp_network_topology/integrations/device/models.py @@ -6,10 +6,10 @@ class DeviceNode(AbstractDeviceNode): class Meta(AbstractDeviceNode.Meta): abstract = False - swappable = swappable_setting('topology_device', 'DeviceNode') + swappable = swappable_setting("topology_device", "DeviceNode") class WifiMesh(AbstractWifiMesh): class Meta(AbstractWifiMesh.Meta): abstract = False - swappable = swappable_setting('topology_device', 'WifiMesh') + swappable = swappable_setting("topology_device", "WifiMesh") diff --git a/openwisp_network_topology/integrations/device/overrides.py b/openwisp_network_topology/integrations/device/overrides.py index 60ab167b..c334eea5 100644 --- a/openwisp_network_topology/integrations/device/overrides.py +++ b/openwisp_network_topology/integrations/device/overrides.py @@ -1,19 +1,19 @@ import swapper from django.utils.translation import gettext_lazy as _ -Node = swapper.load_model('topology', 'Node') -Link = swapper.load_model('topology', 'Link') -Topology = swapper.load_model('topology', 'Topology') +Node = swapper.load_model("topology", "Node") +Link = swapper.load_model("topology", "Link") +Topology = swapper.load_model("topology", "Topology") def get_name(self): - if hasattr(self, 'devicenode'): + if hasattr(self, "devicenode"): return self.devicenode.device.name return super(Node, self).get_name() def node_get_organization_id(self): - if hasattr(self, 'devicenode'): + if hasattr(self, "devicenode"): return self.devicenode.device.organization_id return super(Node, self).get_organization_id() @@ -27,20 +27,20 @@ def topology_get_nodes_queryset(self): return ( super(Topology, self) .get_nodes_queryset() - .select_related('devicenode__device') + .select_related("devicenode__device") .only( - 'id', - 'topology_id', - 'label', - 'addresses', - 'properties', - 'user_properties', - 'created', - 'modified', - 'devicenode__device__id', - 'devicenode__device__name', - 'devicenode__device__last_ip', - 'devicenode__device__management_ip', + "id", + "topology_id", + "label", + "addresses", + "properties", + "user_properties", + "created", + "modified", + "devicenode__device__id", + "devicenode__device__name", + "devicenode__device__last_ip", + "devicenode__device__management_ip", ) ) @@ -50,24 +50,24 @@ def node_get_queryset(cls, qs): return ( super(Node, cls) .get_queryset(qs) - .select_related('devicenode__device') + .select_related("devicenode__device") .only( - 'id', - 'topology_id', - 'topology__label', - 'topology__parser', - 'organization__id', - 'organization__name', - 'organization__is_active', - 'label', - 'addresses', - 'properties', - 'user_properties', - 'devicenode__device__id', - 'devicenode__device__name', - 'devicenode__device__name', - 'devicenode__device__last_ip', - 'devicenode__device__management_ip', + "id", + "topology_id", + "topology__label", + "topology__parser", + "organization__id", + "organization__name", + "organization__is_active", + "label", + "addresses", + "properties", + "user_properties", + "devicenode__device__id", + "devicenode__device__name", + "devicenode__device__name", + "devicenode__device__last_ip", + "devicenode__device__management_ip", ) ) @@ -77,38 +77,38 @@ def link_get_queryset(cls, qs): return ( super(Link, cls) .get_queryset(qs) - .select_related('source__devicenode__device', 'target__devicenode__device') + .select_related("source__devicenode__device", "target__devicenode__device") .only( - 'id', - 'topology_id', - 'topology__label', - 'topology__parser', - 'organization__id', - 'organization__name', - 'organization__is_active', - 'status', - 'properties', - 'user_properties', - 'cost', - 'cost_text', - 'source__addresses', - 'source__label', - 'source__devicenode__device__id', - 'source__devicenode__device__name', - 'source__devicenode__device__last_ip', - 'source__devicenode__device__management_ip', - 'target__addresses', - 'target__label', - 'target__devicenode__device__id', - 'target__devicenode__device__name', - 'target__devicenode__device__last_ip', - 'target__devicenode__device__management_ip', + "id", + "topology_id", + "topology__label", + "topology__parser", + "organization__id", + "organization__name", + "organization__is_active", + "status", + "properties", + "user_properties", + "cost", + "cost_text", + "source__addresses", + "source__label", + "source__devicenode__device__id", + "source__devicenode__device__name", + "source__devicenode__device__last_ip", + "source__devicenode__device__management_ip", + "target__addresses", + "target__label", + "target__devicenode__device__id", + "target__devicenode__device__name", + "target__devicenode__device__last_ip", + "target__devicenode__device__management_ip", ) ) Node.get_name = get_name -Node.get_name.short_description = _('label') +Node.get_name.short_description = _("label") Node.get_organization_id = node_get_organization_id Node.get_queryset = node_get_queryset Link.get_queryset = link_get_queryset diff --git a/openwisp_network_topology/integrations/device/settings.py b/openwisp_network_topology/integrations/device/settings.py index abb7bf32..d8ba6bfa 100644 --- a/openwisp_network_topology/integrations/device/settings.py +++ b/openwisp_network_topology/integrations/device/settings.py @@ -1,3 +1,3 @@ from ...settings import get_settings_value -WIFI_MESH_INTEGRATION = get_settings_value('WIFI_MESH_INTEGRATION', False) +WIFI_MESH_INTEGRATION = get_settings_value("WIFI_MESH_INTEGRATION", False) diff --git a/openwisp_network_topology/integrations/device/tasks.py b/openwisp_network_topology/integrations/device/tasks.py index 629d2f6e..8d2b36ce 100644 --- a/openwisp_network_topology/integrations/device/tasks.py +++ b/openwisp_network_topology/integrations/device/tasks.py @@ -6,17 +6,17 @@ @shared_task def create_device_node_relation(node_pk): - Node = swapper.load_model('topology', 'Node') - DeviceNode = swapper.load_model('topology_device', 'DeviceNode') - node = Node.objects.select_related('topology').get(pk=node_pk) + Node = swapper.load_model("topology", "Node") + DeviceNode = swapper.load_model("topology_device", "DeviceNode") + node = Node.objects.select_related("topology").get(pk=node_pk) DeviceNode.auto_create(node) @shared_task def trigger_device_updates(link_pk): - Link = swapper.load_model('topology', 'Link') - DeviceNode = swapper.load_model('topology_device', 'DeviceNode') - link = Link.objects.select_related('topology').get(pk=link_pk) + Link = swapper.load_model("topology", "Link") + DeviceNode = swapper.load_model("topology_device", "DeviceNode") + link = Link.objects.select_related("topology").get(pk=link_pk) DeviceNode.trigger_device_updates(link) @@ -24,5 +24,5 @@ def trigger_device_updates(link_pk): def create_mesh_topology(organization_ids, discard_older_data_time=360): if not app_settings.WIFI_MESH_INTEGRATION: return - WifiMesh = swapper.load_model('topology_device', 'WifiMesh') + WifiMesh = swapper.load_model("topology_device", "WifiMesh") WifiMesh.create_topology(organization_ids, discard_older_data_time) diff --git a/openwisp_network_topology/integrations/device/tests/__init__.py b/openwisp_network_topology/integrations/device/tests/__init__.py index 672645c1..153d5469 100644 --- a/openwisp_network_topology/integrations/device/tests/__init__.py +++ b/openwisp_network_topology/integrations/device/tests/__init__.py @@ -1,220 +1,220 @@ SIMPLE_MESH_DATA = { - '64:70:02:C3:03:B2': [ + "64:70:02:C3:03:B2": [ { - 'mac': '64:70:02:c3:03:b3', - 'mtu': 1500, - 'multicast': True, - 'name': 'mesh0', - 'txqueuelen': 1000, - 'type': 'wireless', - 'up': True, - 'wireless': { - 'channel': 11, - 'clients': [ + "mac": "64:70:02:c3:03:b3", + "mtu": 1500, + "multicast": True, + "name": "mesh0", + "txqueuelen": 1000, + "type": "wireless", + "up": True, + "wireless": { + "channel": 11, + "clients": [ { - 'auth': True, - 'authorized': True, - 'ht': True, - 'mac': 'a4:bc:3f:ae:c7:0c', - 'mfp': False, - 'noise': -90, - 'vendor': 'TP-LINK TECHNOLOGIES CO.,LTD.', - 'vht': False, - 'wmm': True, - 'mesh_llid': 19000, - 'mesh_plid': 24000, - 'mesh_non_peer_ps': 'ACTIVE', + "auth": True, + "authorized": True, + "ht": True, + "mac": "a4:bc:3f:ae:c7:0c", + "mfp": False, + "noise": -90, + "vendor": "TP-LINK TECHNOLOGIES CO.,LTD.", + "vht": False, + "wmm": True, + "mesh_llid": 19000, + "mesh_plid": 24000, + "mesh_non_peer_ps": "ACTIVE", }, { - 'auth': True, - 'authorized': True, - 'ht': True, - 'mac': '2a:9a:fb:12:11:77', - 'mfp': False, - 'noise': -94, - 'signal': -58, - 'vendor': 'TP-LINK TECHNOLOGIES CO.,LTD.', - 'vht': False, - 'wmm': True, - 'mesh_llid': 19500, - 'mesh_plid': 24500, - 'mesh_non_peer_ps': 'ACTIVE', + "auth": True, + "authorized": True, + "ht": True, + "mac": "2a:9a:fb:12:11:77", + "mfp": False, + "noise": -94, + "signal": -58, + "vendor": "TP-LINK TECHNOLOGIES CO.,LTD.", + "vht": False, + "wmm": True, + "mesh_llid": 19500, + "mesh_plid": 24500, + "mesh_non_peer_ps": "ACTIVE", }, ], - 'country': 'ES', - 'frequency': 2462, - 'htmode': 'HT20', - 'mode': '802.11s', - 'noise': -95, - 'signal': -61, - 'ssid': 'Test Mesh', - 'tx_power': 17, + "country": "ES", + "frequency": 2462, + "htmode": "HT20", + "mode": "802.11s", + "noise": -95, + "signal": -61, + "ssid": "Test Mesh", + "tx_power": 17, }, } ], - 'A4:BC:3F:AE:C7:0B': [ + "A4:BC:3F:AE:C7:0B": [ { - 'mac': 'a4:bc:3f:ae:c7:0c', - 'mtu': 1500, - 'multicast': True, - 'name': 'mesh0', - 'txqueuelen': 1000, - 'type': 'wireless', - 'up': True, - 'wireless': { - 'channel': 11, - 'clients': [ + "mac": "a4:bc:3f:ae:c7:0c", + "mtu": 1500, + "multicast": True, + "name": "mesh0", + "txqueuelen": 1000, + "type": "wireless", + "up": True, + "wireless": { + "channel": 11, + "clients": [ { - 'auth': True, - 'authorized': True, - 'ht': True, - 'mac': '64:70:02:C3:03:B3', - 'mfp': False, - 'noise': -98, - 'signal': -58, - 'vendor': 'TP-LINK TECHNOLOGIES CO.,LTD.', - 'vht': False, - 'wmm': True, - 'mesh_llid': 20000, - 'mesh_plid': 25000, - 'mesh_non_peer_ps': 'ACTIVE', + "auth": True, + "authorized": True, + "ht": True, + "mac": "64:70:02:C3:03:B3", + "mfp": False, + "noise": -98, + "signal": -58, + "vendor": "TP-LINK TECHNOLOGIES CO.,LTD.", + "vht": False, + "wmm": True, + "mesh_llid": 20000, + "mesh_plid": 25000, + "mesh_non_peer_ps": "ACTIVE", }, { - 'auth': True, - 'authorized': True, - 'ht': True, - 'mac': '2a:9a:fb:12:11:77', - 'mfp': False, - 'noise': -96, - 'signal': -60, - 'vendor': 'TP-LINK TECHNOLOGIES CO.,LTD.', - 'vht': False, - 'wmm': True, - 'mesh_llid': 19500, - 'mesh_plid': 24500, - 'mesh_non_peer_ps': 'ACTIVE', + "auth": True, + "authorized": True, + "ht": True, + "mac": "2a:9a:fb:12:11:77", + "mfp": False, + "noise": -96, + "signal": -60, + "vendor": "TP-LINK TECHNOLOGIES CO.,LTD.", + "vht": False, + "wmm": True, + "mesh_llid": 19500, + "mesh_plid": 24500, + "mesh_non_peer_ps": "ACTIVE", }, ], - 'country': 'ES', - 'frequency': 2462, - 'htmode': 'HT20', - 'mode': '802.11s', - 'noise': -95, - 'signal': -56, - 'ssid': 'Test Mesh', - 'tx_power': 17, + "country": "ES", + "frequency": 2462, + "htmode": "HT20", + "mode": "802.11s", + "noise": -95, + "signal": -56, + "ssid": "Test Mesh", + "tx_power": 17, }, } ], - '2A:9A:FB:12:11:76': [ + "2A:9A:FB:12:11:76": [ { - 'mac': '2a:9a:fb:12:11:77', - 'mtu': 1500, - 'multicast': True, - 'name': 'mesh0', - 'txqueuelen': 1000, - 'type': 'wireless', - 'up': True, - 'wireless': { - 'channel': 11, - 'clients': [ + "mac": "2a:9a:fb:12:11:77", + "mtu": 1500, + "multicast": True, + "name": "mesh0", + "txqueuelen": 1000, + "type": "wireless", + "up": True, + "wireless": { + "channel": 11, + "clients": [ { # Link properties - 'auth': True, - 'authorized': True, - 'noise': -92, - 'signal': -56, + "auth": True, + "authorized": True, + "noise": -92, + "signal": -56, # Node properties - 'ht': True, - 'mfp': False, - 'vht': False, - 'wmm': True, - 'mac': 'a4:bc:3f:ae:c7:0c', - 'vendor': 'TP-LINK TECHNOLOGIES CO.,LTD.', - 'mesh_non_peer_ps': 'LISTEN', + "ht": True, + "mfp": False, + "vht": False, + "wmm": True, + "mac": "a4:bc:3f:ae:c7:0c", + "vendor": "TP-LINK TECHNOLOGIES CO.,LTD.", + "mesh_non_peer_ps": "LISTEN", }, { - 'mac': '0a:cc:ae:34:ff:3d', - 'wps': False, - 'wds': False, - 'ht': True, - 'preauth': False, - 'assoc': True, - 'authorized': True, - 'vht': False, - 'wmm': False, - 'aid': 1, - 'mfp': False, - 'auth': True, - 'vendor': 'TP-LINK TECHNOLOGIES CO.,LTD.', - 'signature': 'test_signature', - 'mesh_plink': 'LISTEN', + "mac": "0a:cc:ae:34:ff:3d", + "wps": False, + "wds": False, + "ht": True, + "preauth": False, + "assoc": True, + "authorized": True, + "vht": False, + "wmm": False, + "aid": 1, + "mfp": False, + "auth": True, + "vendor": "TP-LINK TECHNOLOGIES CO.,LTD.", + "signature": "test_signature", + "mesh_plink": "LISTEN", }, ], - 'country': 'ES', - 'frequency': 2462, - 'htmode': 'HT20', - 'mode': '802.11s', - 'noise': -95, - 'signal': -64, - 'ssid': 'Test Mesh', - 'tx_power': 17, + "country": "ES", + "frequency": 2462, + "htmode": "HT20", + "mode": "802.11s", + "noise": -95, + "signal": -64, + "ssid": "Test Mesh", + "tx_power": 17, }, }, { - 'addresses': [ + "addresses": [ { - 'address': '192.168.1.1', - 'family': 'ipv4', - 'mask': 24, - 'proto': 'static', + "address": "192.168.1.1", + "family": "ipv4", + "mask": 24, + "proto": "static", }, ], - 'bridge_members': ['eth0.1', 'mesh0'], - 'mac': '2a:9a:fb:12:11:76', - 'mtu': 1500, - 'multicast': True, - 'name': 'br-lan', - 'speed': '-1F', - 'stp': False, - 'txqueuelen': 1000, - 'type': 'bridge', - 'up': True, + "bridge_members": ["eth0.1", "mesh0"], + "mac": "2a:9a:fb:12:11:76", + "mtu": 1500, + "multicast": True, + "name": "br-lan", + "speed": "-1F", + "stp": False, + "txqueuelen": 1000, + "type": "bridge", + "up": True, }, { - 'name': 'wlan0', - 'type': 'wireless', - 'up': True, - 'mac': '2a:9a:fb:12:11:78', - 'txqueuelen': 1000, - 'multicast': True, - 'mtu': 1500, - 'wireless': { - 'frequency': 2437, - 'mode': 'access_point', - 'signal': -29, - 'tx_power': 6, - 'channel': 6, - 'ssid': 'testnet', - 'noise': -95, - 'country': 'US', - 'htmode': 'HT20', - 'clients': [ + "name": "wlan0", + "type": "wireless", + "up": True, + "mac": "2a:9a:fb:12:11:78", + "txqueuelen": 1000, + "multicast": True, + "mtu": 1500, + "wireless": { + "frequency": 2437, + "mode": "access_point", + "signal": -29, + "tx_power": 6, + "channel": 6, + "ssid": "testnet", + "noise": -95, + "country": "US", + "htmode": "HT20", + "clients": [ { - 'mac': '00:ee:ad:34:f5:3b', - 'wps': False, - 'wds': False, - 'ht': True, - 'preauth': False, - 'assoc': True, - 'authorized': True, - 'vht': False, - 'wmm': True, - 'aid': 1, - 'mfp': False, - 'auth': True, - 'vendor': 'TP-LINK TECHNOLOGIES CO.,LTD.', - 'signature': 'test_signature', + "mac": "00:ee:ad:34:f5:3b", + "wps": False, + "wds": False, + "ht": True, + "preauth": False, + "assoc": True, + "authorized": True, + "vht": False, + "wmm": True, + "aid": 1, + "mfp": False, + "auth": True, + "vendor": "TP-LINK TECHNOLOGIES CO.,LTD.", + "signature": "test_signature", }, ], }, @@ -223,32 +223,32 @@ } SINGLE_NODE_MESH_DATA = { - '64:70:02:C3:03:B2': [ + "64:70:02:C3:03:B2": [ { - 'mac': '64:70:02:c3:03:b3', - 'mtu': 1500, - 'multicast': True, - 'name': 'mesh0', - 'txqueuelen': 1000, - 'type': 'wireless', - 'up': True, - 'wireless': { - 'channel': 11, - 'clients': [], - 'country': 'ES', - 'frequency': 2462, - 'htmode': 'HT20', - 'mode': '802.11s', - 'noise': -95, - 'signal': -61, - 'ssid': 'Test Mesh', - 'tx_power': 17, - 'mesh_llid': 19751, - 'mesh_local_ps': 'ACTIVE', - 'mesh_non_peer_ps': 'ACTIVE', - 'mesh_peer_ps': 'ACTIVE', - 'mesh_plid': 24413, - 'mesh_plink': 'ESTAB', + "mac": "64:70:02:c3:03:b3", + "mtu": 1500, + "multicast": True, + "name": "mesh0", + "txqueuelen": 1000, + "type": "wireless", + "up": True, + "wireless": { + "channel": 11, + "clients": [], + "country": "ES", + "frequency": 2462, + "htmode": "HT20", + "mode": "802.11s", + "noise": -95, + "signal": -61, + "ssid": "Test Mesh", + "tx_power": 17, + "mesh_llid": 19751, + "mesh_local_ps": "ACTIVE", + "mesh_non_peer_ps": "ACTIVE", + "mesh_peer_ps": "ACTIVE", + "mesh_plid": 24413, + "mesh_plink": "ESTAB", }, } ], diff --git a/openwisp_network_topology/integrations/device/tests/test_integration.py b/openwisp_network_topology/integrations/device/tests/test_integration.py index 833c5767..19b5366e 100644 --- a/openwisp_network_topology/integrations/device/tests/test_integration.py +++ b/openwisp_network_topology/integrations/device/tests/test_integration.py @@ -24,16 +24,16 @@ from ..base.models import logger as models_logger from ..base.models import trigger_device_checks_path -Node = swapper.load_model('topology', 'Node') -Link = swapper.load_model('topology', 'Link') -Topology = swapper.load_model('topology', 'Topology') -DeviceNode = swapper.load_model('topology_device', 'DeviceNode') -WifiMesh = swapper.load_model('topology_device', 'WifiMesh') -Device = swapper.load_model('config', 'Device') -Template = swapper.load_model('config', 'Template') -Vpn = swapper.load_model('config', 'Template') -Organization = swapper.load_model('openwisp_users', 'Organization') -Cert = swapper.load_model('pki', 'Cert') +Node = swapper.load_model("topology", "Node") +Link = swapper.load_model("topology", "Link") +Topology = swapper.load_model("topology", "Topology") +DeviceNode = swapper.load_model("topology_device", "DeviceNode") +WifiMesh = swapper.load_model("topology_device", "WifiMesh") +Device = swapper.load_model("config", "Device") +Template = swapper.load_model("config", "Template") +Vpn = swapper.load_model("config", "Template") +Organization = swapper.load_model("openwisp_users", "Organization") +Cert = swapper.load_model("pki", "Cert") class Base( @@ -47,25 +47,25 @@ class Base( ): topology_model = Topology node_model = Node - _ZT_SERVICE_REQUESTS = 'openwisp_controller.config.api.zerotier_service.requests' - _ZT_GENERATE_IDENTITY_SUBPROCESS = 'openwisp_controller.config.base.vpn.subprocess' + _ZT_SERVICE_REQUESTS = "openwisp_controller.config.api.zerotier_service.requests" + _ZT_GENERATE_IDENTITY_SUBPROCESS = "openwisp_controller.config.base.vpn.subprocess" def _init_test_node( self, topology, addresses=None, - label='test', + label="test", common_name=None, create=True, ): if not addresses: - addresses = ['netjson_id'] + addresses = ["netjson_id"] node = Node( organization=topology.organization, topology=topology, label=label, addresses=addresses, - properties={'common_name': common_name}, + properties={"common_name": common_name}, ) if create: node.full_clean() @@ -74,22 +74,22 @@ def _init_test_node( def _init_wireguard_test_node(self, topology, addresses=[], create=True, **kwargs): if not addresses: - addresses = ['public_key'] + addresses = ["public_key"] properties = { - 'preshared_key': None, - 'endpoint': None, - 'latest_handsake': '0', - 'transfer_rx': '0', - 'transfer_tx': '0', - 'persistent_keepalive': 'off', - 'allowed_ips': ['10.0.0.2/32'], + "preshared_key": None, + "endpoint": None, + "latest_handsake": "0", + "transfer_rx": "0", + "transfer_tx": "0", + "persistent_keepalive": "off", + "allowed_ips": ["10.0.0.2/32"], } properties.update(kwargs) - allowed_ips = properties.get('allowed_ips') + allowed_ips = properties.get("allowed_ips") node = Node( organization=topology.organization, topology=topology, - label=','.join(allowed_ips), + label=",".join(allowed_ips), addresses=addresses, properties=properties, ) @@ -99,17 +99,17 @@ def _init_wireguard_test_node(self, topology, addresses=[], create=True, **kwarg return node def _init_zerotier_test_node( - self, topology, addresses=None, label='test', zt_member_id=None, create=True + self, topology, addresses=None, label="test", zt_member_id=None, create=True ): if not addresses: - addresses = [self._TEST_ZT_MEMBER_CONFIG['address']] + addresses = [self._TEST_ZT_MEMBER_CONFIG["address"]] node = Node( organization=topology.organization, topology=topology, label=label, addresses=addresses, # zt peer address is `zt_memeber_id` - properties={'address': zt_member_id}, + properties={"address": zt_member_id}, ) if create: node.full_clean() @@ -141,7 +141,7 @@ def _create_zerotier_test_env(self, mock_requests, mock_subprocess, parser): self._get_mock_response(200), ] mock_stdout = mock.MagicMock() - mock_stdout.stdout.decode.return_value = self._TEST_ZT_MEMBER_CONFIG['identity'] + mock_stdout.stdout.decode.return_value = self._TEST_ZT_MEMBER_CONFIG["identity"] mock_subprocess.run.return_value = mock_stdout org = self._get_org() device, _, _ = self._create_zerotier_vpn_template() @@ -152,19 +152,19 @@ def _create_zerotier_test_env(self, mock_requests, mock_subprocess, parser): def _create_test_env(self, parser): organization = self._get_org() - vpn = self._create_vpn(name='test VPN', organization=organization) + vpn = self._create_vpn(name="test VPN", organization=organization) self._create_template( - name='VPN', - type='vpn', + name="VPN", + type="vpn", vpn=vpn, config=vpn.auto_client(), default=True, organization=organization, ) - vpn2 = self._create_vpn(name='test VPN2', ca=vpn.ca, organization=organization) + vpn2 = self._create_vpn(name="test VPN2", ca=vpn.ca, organization=organization) self._create_template( - name='VPN2', - type='vpn', + name="VPN2", + type="vpn", vpn=vpn2, config=vpn.auto_client(), default=True, @@ -179,9 +179,9 @@ def _create_test_env(self, parser): class TestControllerIntegration(Base, TransactionTestCase): def test_auto_create_openvpn(self): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") self.assertEqual(DeviceNode.objects.count(), 0) - with self.subTest('assert number of queries'): + with self.subTest("assert number of queries"): with self.assertNumQueries(15): node = self._init_test_node(topology, common_name=cert.common_name) self.assertEqual(DeviceNode.objects.count(), 1) @@ -189,27 +189,27 @@ def test_auto_create_openvpn(self): self.assertEqual(device_node.device, device) self.assertEqual(device_node.node, node) - with self.subTest('not run on save'): - with mock.patch.object(transaction, 'on_commit') as on_commit: + with self.subTest("not run on save"): + with mock.patch.object(transaction, "on_commit") as on_commit: node.save() on_commit.assert_not_called() def test_auto_create_openvpn_failures(self): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") - with self.subTest('common_name not present'): + with self.subTest("common_name not present"): self._init_test_node(topology) self.assertFalse(DeviceNode.objects.exists()) - with self.subTest('cert does not exist'): - self._init_test_node(topology, common_name='missing') + with self.subTest("cert does not exist"): + self._init_test_node(topology, common_name="missing") self.assertFalse(DeviceNode.objects.exists()) - with self.subTest('exception during save'): + with self.subTest("exception during save"): with mock.patch.object( - DeviceNode, 'save', side_effect=Exception('test') + DeviceNode, "save", side_effect=Exception("test") ) as save: - with mock.patch.object(models_logger, 'exception') as logger_exception: + with mock.patch.object(models_logger, "exception") as logger_exception: self._init_test_node(topology, common_name=cert.common_name) save.assert_called_once() logger_exception.assert_called_once() @@ -217,42 +217,42 @@ def test_auto_create_openvpn_failures(self): def test_auto_create_wireguard(self): topology, device = self._create_wireguard_test_env( - parser='netdiff.WireguardParser' + parser="netdiff.WireguardParser" ) self.assertEqual(DeviceNode.objects.count(), 0) - with self.subTest('return if node has no allowed ips'): + with self.subTest("return if node has no allowed ips"): node = self._init_wireguard_test_node(topology, allowed_ips=[]) self.assertEqual(DeviceNode.objects.count(), 0) - with self.subTest('handle error if node has bogus allowed ips'): + with self.subTest("handle error if node has bogus allowed ips"): try: - node = self._init_wireguard_test_node(topology, allowed_ips=['invalid']) + node = self._init_wireguard_test_node(topology, allowed_ips=["invalid"]) except ValueError: - self.fail('ValueError raised') - with self.subTest('assert number of queries'): + self.fail("ValueError raised") + with self.subTest("assert number of queries"): with self.assertNumQueries(15): node = self._init_wireguard_test_node(topology) self.assertEqual(DeviceNode.objects.count(), 1) device_node = DeviceNode.objects.first() self.assertEqual(device_node.device, device) self.assertEqual(device_node.node, node) - with self.subTest('do not raise Exception on link status changed'): + with self.subTest("do not raise Exception on link status changed"): try: target_node = self._init_wireguard_test_node(topology) link = Link( topology=node.topology, source=node, target=target_node, cost=0 ) link.save() - link.status = 'down' - link.save(update_fields=['status']) + link.status = "down" + link.save(update_fields=["status"]) except KeyError: - self.fail('KeyError raised') + self.fail("KeyError raised") def test_auto_create_zerotier(self): topology, device, zerotier_member_id = self._create_zerotier_test_env( - parser='netdiff.ZeroTierParser' + parser="netdiff.ZeroTierParser" ) self.assertEqual(DeviceNode.objects.count(), 0) - with self.subTest('assert number of queries'): + with self.subTest("assert number of queries"): with self.assertNumQueries(15): node = self._init_zerotier_test_node( topology, zt_member_id=zerotier_member_id @@ -262,29 +262,29 @@ def test_auto_create_zerotier(self): self.assertEqual(device_node.device, device) self.assertEqual(device_node.node, node) - with self.subTest('not run on save'): - with mock.patch.object(transaction, 'on_commit') as on_commit: + with self.subTest("not run on save"): + with mock.patch.object(transaction, "on_commit") as on_commit: node.save() on_commit.assert_not_called() def test_auto_create_zerotier_failures(self): topology, device, zerotier_member_id = self._create_zerotier_test_env( - parser='netdiff.ZeroTierParser' + parser="netdiff.ZeroTierParser" ) - with self.subTest('zerotier_member_id not present'): + with self.subTest("zerotier_member_id not present"): self._init_zerotier_test_node(topology) self.assertFalse(DeviceNode.objects.exists()) - with self.subTest('zerotier_member_id does not exist'): - self._init_zerotier_test_node(topology, zt_member_id='non_existent_id') + with self.subTest("zerotier_member_id does not exist"): + self._init_zerotier_test_node(topology, zt_member_id="non_existent_id") self.assertFalse(DeviceNode.objects.exists()) - with self.subTest('exception during save'): + with self.subTest("exception during save"): with mock.patch.object( - DeviceNode, 'save', side_effect=Exception('test') + DeviceNode, "save", side_effect=Exception("test") ) as save: - with mock.patch.object(models_logger, 'exception') as logger_exception: + with mock.patch.object(models_logger, "exception") as logger_exception: self._init_zerotier_test_node( topology, zt_member_id=zerotier_member_id ) @@ -293,15 +293,15 @@ def test_auto_create_zerotier_failures(self): self.assertEqual(DeviceNode.objects.count(), 0) def test_filter_by_link(self): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") - with self.subTest('empty result'): + with self.subTest("empty result"): node = self._init_test_node(topology, common_name=cert.common_name) self.assertFalse(DeviceNode.filter_by_link(Link()).exists()) - with self.subTest('non empty result'): + with self.subTest("non empty result"): node2 = self._init_test_node( - topology, addresses=['netjson_id2'], label='test2' + topology, addresses=["netjson_id2"], label="test2" ) link = Link( source=node, @@ -315,15 +315,15 @@ def test_filter_by_link(self): self.assertTrue(DeviceNode.filter_by_link(link).exists()) def test_link_down_openvpn(self): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") node = self._init_test_node(topology, common_name=cert.common_name) - node2 = self._init_test_node(topology, addresses=['netjson_id2'], label='test2') - device.management_ip = '10.0.0.8' + node2 = self._init_test_node(topology, addresses=["netjson_id2"], label="test2") + device.management_ip = "10.0.0.8" device.save() link = Link( source=node, target=node2, - status='up', + status="up", topology=topology, organization=topology.organization, cost=1, @@ -331,23 +331,23 @@ def test_link_down_openvpn(self): link.full_clean() link.save() with self.assertNumQueries(6): - link.status = 'down' + link.status = "down" link.save() device.refresh_from_db() self.assertIsNone(device.management_ip) def test_link_up_openvpn(self): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") node = self._init_test_node( - topology, addresses=['netjson_id', '10.0.0.2'], common_name=cert.common_name + topology, addresses=["netjson_id", "10.0.0.2"], common_name=cert.common_name ) - node2 = self._init_test_node(topology, addresses=['netjson_id2'], label='test2') + node2 = self._init_test_node(topology, addresses=["netjson_id2"], label="test2") device.management_ip = None device.save() link = Link( source=node, target=node2, - status='down', + status="down", topology=topology, organization=topology.organization, cost=1, @@ -355,26 +355,26 @@ def test_link_up_openvpn(self): link.full_clean() link.save() with self.assertNumQueries(6): - link.status = 'up' + link.status = "up" link.save() device.refresh_from_db() - self.assertEqual(device.management_ip, '10.0.0.2') + self.assertEqual(device.management_ip, "10.0.0.2") - @mock.patch.object(models_logger, 'warning') + @mock.patch.object(models_logger, "warning") def test_link_up_openvpn_failure(self, logger_warning): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") node = self._init_test_node( topology, - addresses=['netjson_id', 'not_an_ip'], + addresses=["netjson_id", "not_an_ip"], common_name=cert.common_name, ) - node2 = self._init_test_node(topology, addresses=['netjson_id2'], label='test2') + node2 = self._init_test_node(topology, addresses=["netjson_id2"], label="test2") device.management_ip = None device.save() link = Link( source=node, target=node2, - status='down', + status="down", topology=topology, organization=topology.organization, cost=1, @@ -382,64 +382,64 @@ def test_link_up_openvpn_failure(self, logger_warning): link.full_clean() link.save() - with self.subTest('Test invalid addresses'): - link.status = 'up' + with self.subTest("Test invalid addresses"): + link.status = "up" link.save() logger_warning.assert_called_once() logger_warning.assert_called_with( - 'ValueError raised while processing addresses: netjson_id, not_an_ip' + "ValueError raised while processing addresses: netjson_id, not_an_ip" ) - link.status = 'down' + link.status = "down" link.save() logger_warning.reset_mock() - with self.subTest('Test disconnected nodes'): - node.addresses = ['10.0.0.1'] + with self.subTest("Test disconnected nodes"): + node.addresses = ["10.0.0.1"] node.save() - link.status = 'up' + link.status = "up" link.save() logger_warning.assert_called_once() logger_warning.assert_called_with( - 'IndexError raised while processing addresses: 10.0.0.1' + "IndexError raised while processing addresses: 10.0.0.1" ) device.refresh_from_db() self.assertIsNone(device.management_ip) def test_node_label_override(self): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") node = self._init_test_node(topology, common_name=cert.common_name) node.refresh_from_db() self.assertEqual(node.get_name(), device.name) def test_topology_json(self): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") self._init_test_node(topology, common_name=cert.common_name) - with self.subTest('assert number of queries'): + with self.subTest("assert number of queries"): with self.assertNumQueries(2): json = topology.json(dict=True) - self.assertEqual(json['nodes'][0]['label'], device.name) + self.assertEqual(json["nodes"][0]["label"], device.name) def test_create_device_nodes_command(self): - topology, _, cert = self._create_test_env(parser='netdiff.OpenvpnParser') - properties = {'common_name': cert.common_name} + topology, _, cert = self._create_test_env(parser="netdiff.OpenvpnParser") + properties = {"common_name": cert.common_name} n = self._create_node(topology=topology, properties=properties) DeviceNode.objects.all().delete() - call_command('create_device_nodes') + call_command("create_device_nodes") qs = DeviceNode.objects.filter(node=n) self.assertEqual(qs.count(), 1) def test_shared_topology_org_devices(self): - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") shared_topology = self._create_topology( - organization=None, parser='netdiff.OpenvpnParser' + organization=None, parser="netdiff.OpenvpnParser" ) - shared_vpn = self._create_vpn(name='test VPN', organization=None) + shared_vpn = self._create_vpn(name="test VPN", organization=None) self._create_template( - name='VPN', - type='vpn', + name="VPN", + type="vpn", vpn=shared_vpn, config=shared_vpn.auto_client(), default=True, @@ -457,18 +457,18 @@ def test_shared_topology_org_devices(self): ) openvpn_status = ( - 'OpenVPN CLIENT LIST\n' - 'Updated,Sun Oct 8 19:46:06 2017\n' - 'Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since\n' - f'{org1_cert_common_name},87.7.9.213:56857,417209,6046898,Sun Oct 8 08:39:11 2017\n' - f'{org2_cert_common_name},2.226.154.66:52091,6020304,444463,Sun Oct 8 08:39:10 2017\n' - 'ROUTING TABLE\n' - 'Virtual Address,Common Name,Real Address,Last Ref\n' - f'9e:c2:f2:55:f4:33,{org1_cert_common_name},87.7.9.213:56857,Sun Oct 8 19:45:37 2017\n' - f'46:8a:69:e7:46:24,{org2_cert_common_name},2.226.154.66:52091,Sun Oct 8 19:45:37 2017\n' - 'GLOBAL STATS\n' - 'Max bcast/mcast queue length,6\n' - 'END' + "OpenVPN CLIENT LIST\n" + "Updated,Sun Oct 8 19:46:06 2017\n" + "Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since\n" + f"{org1_cert_common_name},87.7.9.213:56857,417209,6046898,Sun Oct 8 08:39:11 2017\n" + f"{org2_cert_common_name},2.226.154.66:52091,6020304,444463,Sun Oct 8 08:39:10 2017\n" + "ROUTING TABLE\n" + "Virtual Address,Common Name,Real Address,Last Ref\n" + f"9e:c2:f2:55:f4:33,{org1_cert_common_name},87.7.9.213:56857,Sun Oct 8 19:45:37 2017\n" + f"46:8a:69:e7:46:24,{org2_cert_common_name},2.226.154.66:52091,Sun Oct 8 19:45:37 2017\n" + "GLOBAL STATS\n" + "Max bcast/mcast queue length,6\n" + "END" ) shared_topology.receive(openvpn_status) self.assertEqual(DeviceNode.objects.count(), 2) @@ -480,25 +480,25 @@ def test_shared_topology_org_devices(self): class TestMonitoringIntegration(Base, TransactionTestCase): @mock.patch( - 'openwisp_monitoring.device.apps.DeviceMonitoringConfig.connect_device_signals' + "openwisp_monitoring.device.apps.DeviceMonitoringConfig.connect_device_signals" ) - @mock.patch('openwisp_monitoring.db.timeseries_db.create_database') - @mock.patch('openwisp_monitoring.device.utils.manage_short_retention_policy') - @mock.patch('openwisp_monitoring.device.utils.manage_default_retention_policy') - @mock.patch('openwisp_monitoring.device.exportable._exportable_fields', []) - @mock.patch('openwisp_notifications.types.NOTIFICATION_TYPES', dict()) + @mock.patch("openwisp_monitoring.db.timeseries_db.create_database") + @mock.patch("openwisp_monitoring.device.utils.manage_short_retention_policy") + @mock.patch("openwisp_monitoring.device.utils.manage_default_retention_policy") + @mock.patch("openwisp_monitoring.device.exportable._exportable_fields", []) + @mock.patch("openwisp_notifications.types.NOTIFICATION_TYPES", dict()) def test_monitoring_integration(self, *args): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") node = self._init_test_node( - topology, addresses=['netjson_id', '10.0.0.2'], common_name=cert.common_name + topology, addresses=["netjson_id", "10.0.0.2"], common_name=cert.common_name ) - node2 = self._init_test_node(topology, addresses=['netjson_id2'], label='test2') + node2 = self._init_test_node(topology, addresses=["netjson_id2"], label="test2") device.management_ip = None device.save() link = Link( source=node, target=node2, - status='up', + status="up", topology=topology, organization=topology.organization, cost=1, @@ -510,43 +510,43 @@ def test_monitoring_integration(self, *args): MENU.clear() # needed for monitoring trigger_device_checks = import_string(trigger_device_checks_path) - with mock.patch.object(trigger_device_checks, 'delay') as mocked_task: + with mock.patch.object(trigger_device_checks, "delay") as mocked_task: with self.modify_settings( INSTALLED_APPS={ - 'append': [ - 'openwisp_controller.connection', - 'openwisp_controller.geo', - 'openwisp_monitoring.monitoring', - 'openwisp_monitoring.device', - 'openwisp_monitoring.check', - 'openwisp_notifications', + "append": [ + "openwisp_controller.connection", + "openwisp_controller.geo", + "openwisp_monitoring.monitoring", + "openwisp_monitoring.device", + "openwisp_monitoring.check", + "openwisp_notifications", ] } ): - link.status = 'down' + link.status = "down" link.save() mocked_task.assert_called_once() mocked_task.assert_called_with(device.pk, recovery=False) class TestAdmin(Base, TransactionTestCase): - module = 'openwisp_network_topology' - app_label = 'topology' + module = "openwisp_network_topology" + app_label = "topology" topology_model = Topology link_model = Link node_model = Node user_model = get_user_model() - fixtures = ['test_users.json'] - api_urls_path = 'api.urls' + fixtures = ["test_users.json"] + api_urls_path = "api.urls" @property def prefix(self): - return 'admin:{0}'.format(self.app_label) + return "admin:{0}".format(self.app_label) def setUp(self): - topology, device, cert = self._create_test_env(parser='netdiff.OpenvpnParser') + topology, device, cert = self._create_test_env(parser="netdiff.OpenvpnParser") node1 = self._init_test_node(topology, common_name=cert.common_name) - node2 = self._init_test_node(topology, addresses=['netjson_id2'], label='test2') + node2 = self._init_test_node(topology, addresses=["netjson_id2"], label="test2") link = Link( source=node1, target=node2, @@ -557,15 +557,15 @@ def setUp(self): link.full_clean() link.save() self._create_link(topology=topology, source=node1, target=node2) - self.client.force_login(self.user_model.objects.get(username='admin')) + self.client.force_login(self.user_model.objects.get(username="admin")) def test_node_change_list_queries(self): - path = reverse('{0}_node_changelist'.format(self.prefix)) + path = reverse("{0}_node_changelist".format(self.prefix)) with self.assertNumQueries(5): self.client.get(path) def test_link_change_list_queries(self): - path = reverse('{0}_link_changelist'.format(self.prefix)) + path = reverse("{0}_link_changelist".format(self.prefix)) with self.assertNumQueries(5): self.client.get(path) @@ -586,30 +586,30 @@ def test_link_node_different_topology(self): with self.assertRaises(ValidationError) as context: # Raises ValidationError if link belongs to diff topologies link.full_clean() - self.assertIn('source', context.exception.error_dict) - self.assertIn('target', context.exception.error_dict) + self.assertIn("source", context.exception.error_dict) + self.assertIn("target", context.exception.error_dict) def test_link_update_status(self): t = Topology.objects.first() - n1 = self._create_node(label='node1org1', topology=t) - n2 = self._create_node(label='node2org1', topology=t) + n1 = self._create_node(label="node1org1", topology=t) + n2 = self._create_node(label="node2org1", topology=t) link = self._create_link(topology=t, source=n1, target=n2) - path = reverse('{0}_link_change'.format(self.prefix), args=[link.pk]) + path = reverse("{0}_link_change".format(self.prefix), args=[link.pk]) response = self.client.post( path, data={ - 'topology': str(t.pk), - 'organization': str(t.organization_id), - 'source': str(n1.id), - 'target': str(n2.id), - 'cost': '1.0', - 'status': 'down', + "topology": str(t.pk), + "organization": str(t.organization_id), + "source": str(n1.id), + "target": str(n2.id), + "cost": "1.0", + "status": "down", }, follow=True, ) self.assertEqual(response.status_code, 200) link.refresh_from_db() - self.assertEqual(link.status, 'down') + self.assertEqual(link.status, "down") def test_topology_admin(self): """ @@ -623,11 +623,11 @@ def test_topology_admin(self): """ topology = Topology.objects.first() response = self.client.get( - reverse(f'{self.prefix}_topology_change', args=[topology.id]) + reverse(f"{self.prefix}_topology_change", args=[topology.id]) ) - self.assertNotContains(response, 'Wifi mesh') + self.assertNotContains(response, "Wifi mesh") def test_topology_get_name_desc(self): - response = self.client.get(reverse(f'{self.prefix}_node_changelist')) - self.assertNotContains(response, 'Get name', html=True) - self.assertContains(response, 'Label', html=True) + response = self.client.get(reverse(f"{self.prefix}_node_changelist")) + self.assertNotContains(response, "Get name", html=True) + self.assertContains(response, "Label", html=True) diff --git a/openwisp_network_topology/integrations/device/tests/test_wifi_mesh.py b/openwisp_network_topology/integrations/device/tests/test_wifi_mesh.py index c0acf1d8..5236e92d 100644 --- a/openwisp_network_topology/integrations/device/tests/test_wifi_mesh.py +++ b/openwisp_network_topology/integrations/device/tests/test_wifi_mesh.py @@ -15,17 +15,17 @@ from . import SIMPLE_MESH_DATA, SINGLE_NODE_MESH_DATA from .utils import TopologyTestMixin -Node = swapper.load_model('topology', 'Node') -Link = swapper.load_model('topology', 'Link') -Topology = swapper.load_model('topology', 'Topology') -DeviceNode = swapper.load_model('topology_device', 'DeviceNode') -WifiMesh = swapper.load_model('topology_device', 'WifiMesh') -Device = swapper.load_model('config', 'Device') +Node = swapper.load_model("topology", "Node") +Link = swapper.load_model("topology", "Link") +Topology = swapper.load_model("topology", "Topology") +DeviceNode = swapper.load_model("topology_device", "DeviceNode") +WifiMesh = swapper.load_model("topology_device", "WifiMesh") +Device = swapper.load_model("config", "Device") -@tag('wifi_mesh') +@tag("wifi_mesh") class TestWifiMeshIntegration(TopologyTestMixin, TransactionTestCase): - app_label = 'topology' + app_label = "topology" def setUp(self): super().setUp() @@ -33,7 +33,7 @@ def setUp(self): @property def prefix(self): - return 'admin:{0}'.format(self.app_label) + return "admin:{0}".format(self.app_label) def _populate_mesh(self, data): org = self._get_org() @@ -42,27 +42,27 @@ def _populate_mesh(self, data): device = self._create_device(name=mac, mac_address=mac, organization=org) devices.append(device) response = self.client.post( - '{0}?key={1}&time={2}'.format( - reverse('monitoring:api_device_metric', args=[device.id]), + "{0}?key={1}&time={2}".format( + reverse("monitoring:api_device_metric", args=[device.id]), device.key, - now().utcnow().strftime('%d-%m-%Y_%H:%M:%S.%f'), + now().utcnow().strftime("%d-%m-%Y_%H:%M:%S.%f"), ), data=json.dumps( { - 'type': 'DeviceMonitoring', - 'interfaces': interfaces, + "type": "DeviceMonitoring", + "interfaces": interfaces, } ), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 200) create_mesh_topology.delay(organization_ids=(org.id,)) return devices, org - @patch.object(app_settings, 'WIFI_MESH_INTEGRATION', False) + @patch.object(app_settings, "WIFI_MESH_INTEGRATION", False) def test_wifi_mesh_integration_disabled(self): with self.subTest('Test calling "create_mesh_topology" task'): - with patch.object(WifiMesh, 'create_topology') as mocked: + with patch.object(WifiMesh, "create_topology") as mocked: _, org = self._populate_mesh(SIMPLE_MESH_DATA) self.assertEqual(Topology.objects.count(), 0) mocked.assert_not_called() @@ -71,7 +71,7 @@ def test_wifi_mesh_integration_disabled(self): # previous one fails. Topology.objects.all().delete() - with self.subTest('Test calling WifiMesh.create_topology'): + with self.subTest("Test calling WifiMesh.create_topology"): with self.assertRaises(ImproperlyConfigured) as error: WifiMesh.create_topology( organization_ids=[org.id], discard_older_data_time=360 @@ -86,7 +86,7 @@ def test_simple_mesh(self): self.assertEqual(Topology.objects.filter(organization=org).count(), 1) topology = Topology.objects.filter(organization=org).first() self.assertEqual( - WifiMesh.objects.filter(topology=topology, mesh_id='Test Mesh@11').count(), + WifiMesh.objects.filter(topology=topology, mesh_id="Test Mesh@11").count(), 1, ) self.assertEqual( @@ -124,17 +124,17 @@ def test_simple_mesh(self): # Test DeviceNode creation logic is not executed when the create_topology # is executed again - with patch.object(DeviceNode, 'auto_create') as mocked_auto_create: + with patch.object(DeviceNode, "auto_create") as mocked_auto_create: create_mesh_topology.delay(organization_ids=(org.id,)) mocked_auto_create.assert_not_called() - @patch('logging.Logger.exception') + @patch("logging.Logger.exception") def test_single_node_mesh(self, mocked_logger): devices, org = self._populate_mesh(SINGLE_NODE_MESH_DATA) self.assertEqual(Topology.objects.filter(organization=org).count(), 1) topology = Topology.objects.filter(organization=org).first() self.assertEqual( - WifiMesh.objects.filter(topology=topology, mesh_id='Test Mesh@11').count(), + WifiMesh.objects.filter(topology=topology, mesh_id="Test Mesh@11").count(), 1, ) self.assertEqual( @@ -157,7 +157,7 @@ def test_single_node_mesh(self, mocked_logger): def test_mesh_id_changed(self): devices, org = self._populate_mesh(SIMPLE_MESH_DATA) self.assertEqual(Topology.objects.filter(organization=org).count(), 1) - self.assertEqual(WifiMesh.objects.filter(mesh_id='Test Mesh@11').count(), 1) + self.assertEqual(WifiMesh.objects.filter(mesh_id="Test Mesh@11").count(), 1) topology = Topology.objects.filter(organization=org).first() self.assertEqual( Node.objects.filter( @@ -176,20 +176,20 @@ def test_mesh_id_changed(self): # Change mesh_id reported in the monitoring data mesh_data = deepcopy(SIMPLE_MESH_DATA) for device, interfaces in zip(devices, mesh_data.values()): - interfaces[0]['wireless']['ssid'] = 'New Mesh' + interfaces[0]["wireless"]["ssid"] = "New Mesh" response = self.client.post( - '{0}?key={1}&time={2}'.format( - reverse('monitoring:api_device_metric', args=[device.id]), + "{0}?key={1}&time={2}".format( + reverse("monitoring:api_device_metric", args=[device.id]), device.key, - now().utcnow().strftime('%d-%m-%Y_%H:%M:%S.%f'), + now().utcnow().strftime("%d-%m-%Y_%H:%M:%S.%f"), ), data=json.dumps( { - 'type': 'DeviceMonitoring', - 'interfaces': interfaces, + "type": "DeviceMonitoring", + "interfaces": interfaces, } ), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 200) create_mesh_topology.delay(organization_ids=(org.id,)) @@ -197,9 +197,9 @@ def test_mesh_id_changed(self): self.assertEqual(WifiMesh.objects.count(), 2) self.assertEqual(Node.objects.count(), 6) self.assertEqual(Link.objects.count(), 6) - self.assertEqual(WifiMesh.objects.filter(mesh_id='New Mesh@11').count(), 1) + self.assertEqual(WifiMesh.objects.filter(mesh_id="New Mesh@11").count(), 1) topology = Topology.objects.filter( - organization=org, wifimesh__mesh_id='New Mesh@11' + organization=org, wifimesh__mesh_id="New Mesh@11" ).first() self.assertEqual( Node.objects.filter( @@ -223,36 +223,36 @@ def test_discard_old_monitoring_data(self): self.assertEqual(Topology.objects.count(), 1) topology = Topology.objects.first() self.assertEqual(topology.node_set.count(), 3) - self.assertEqual(topology.link_set.filter(status='up').count(), 3) + self.assertEqual(topology.link_set.filter(status="up").count(), 3) with freeze_time(now_time - timedelta(minutes=10)): # Only two devices sent monitoring data for device in devices[:2]: response = self.client.post( - '{0}?key={1}&time={2}'.format( - reverse('monitoring:api_device_metric', args=[device.id]), + "{0}?key={1}&time={2}".format( + reverse("monitoring:api_device_metric", args=[device.id]), device.key, - now().utcnow().strftime('%d-%m-%Y_%H:%M:%S.%f'), + now().utcnow().strftime("%d-%m-%Y_%H:%M:%S.%f"), ), data=json.dumps( { - 'type': 'DeviceMonitoring', - 'interfaces': SIMPLE_MESH_DATA[device.mac_address], + "type": "DeviceMonitoring", + "interfaces": SIMPLE_MESH_DATA[device.mac_address], } ), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 200) create_mesh_topology.delay(organization_ids=(org.id,)) self.assertEqual(Topology.objects.count(), 1) self.assertEqual(topology.node_set.count(), 3) - self.assertEqual(topology.link_set.filter(status='up').count(), 1) + self.assertEqual(topology.link_set.filter(status="up").count(), 1) # No device is sending monitoring data create_mesh_topology.delay(organization_ids=(org.id,)) self.assertEqual(Topology.objects.count(), 1) self.assertEqual(topology.node_set.count(), 3) - self.assertEqual(topology.link_set.filter(status='up').count(), 0) + self.assertEqual(topology.link_set.filter(status="up").count(), 0) def test_topology_admin(self): """ @@ -267,13 +267,13 @@ def test_topology_admin(self): admin = self._create_admin() self.client.force_login(admin) - with self.subTest('Test add form'): - response = self.client.get(reverse(f'{self.prefix}_topology_add')) - self.assertContains(response, 'Wifi mesh') + with self.subTest("Test add form"): + response = self.client.get(reverse(f"{self.prefix}_topology_add")) + self.assertContains(response, "Wifi mesh") - with self.subTest('Test change form'): + with self.subTest("Test change form"): topology = self._create_topology() response = self.client.get( - reverse(f'{self.prefix}_topology_change', args=[topology.id]) + reverse(f"{self.prefix}_topology_change", args=[topology.id]) ) - self.assertContains(response, 'Wifi mesh') + self.assertContains(response, "Wifi mesh") diff --git a/openwisp_network_topology/integrations/device/tests/utils.py b/openwisp_network_topology/integrations/device/tests/utils.py index f3b19fc5..a5625a69 100644 --- a/openwisp_network_topology/integrations/device/tests/utils.py +++ b/openwisp_network_topology/integrations/device/tests/utils.py @@ -9,9 +9,9 @@ from openwisp_network_topology.tests.utils import CreateGraphObjectsMixin from openwisp_users.tests.utils import TestOrganizationMixin -Node = swapper.load_model('topology', 'Node') +Node = swapper.load_model("topology", "Node") -Topology = swapper.load_model('topology', 'Topology') +Topology = swapper.load_model("topology", "Topology") class TopologyTestMixin( @@ -29,18 +29,18 @@ def _init_test_node( self, topology, addresses=None, - label='test', + label="test", common_name=None, create=True, ): if not addresses: - addresses = ['netjson_id'] + addresses = ["netjson_id"] node = Node( organization=topology.organization, topology=topology, label=label, addresses=addresses, - properties={'common_name': common_name}, + properties={"common_name": common_name}, ) if create: node.full_clean() @@ -49,22 +49,22 @@ def _init_test_node( def _init_wireguard_test_node(self, topology, addresses=[], create=True, **kwargs): if not addresses: - addresses = ['public_key'] + addresses = ["public_key"] properties = { - 'preshared_key': None, - 'endpoint': None, - 'latest_handsake': '0', - 'transfer_rx': '0', - 'transfer_tx': '0', - 'persistent_keepalive': 'off', - 'allowed_ips': ['10.0.0.2/32'], + "preshared_key": None, + "endpoint": None, + "latest_handsake": "0", + "transfer_rx": "0", + "transfer_tx": "0", + "persistent_keepalive": "off", + "allowed_ips": ["10.0.0.2/32"], } properties.update(kwargs) - allowed_ips = properties.get('allowed_ips') + allowed_ips = properties.get("allowed_ips") node = Node( organization=topology.organization, topology=topology, - label=','.join(allowed_ips), + label=",".join(allowed_ips), addresses=addresses, properties=properties, ) @@ -82,19 +82,19 @@ def _create_wireguard_test_env(self, parser): def _create_test_env(self, parser): organization = self._get_org() - vpn = self._create_vpn(name='test VPN', organization=organization) + vpn = self._create_vpn(name="test VPN", organization=organization) self._create_template( - name='VPN', - type='vpn', + name="VPN", + type="vpn", vpn=vpn, config=vpn.auto_client(), default=True, organization=organization, ) - vpn2 = self._create_vpn(name='test VPN2', ca=vpn.ca, organization=organization) + vpn2 = self._create_vpn(name="test VPN2", ca=vpn.ca, organization=organization) self._create_template( - name='VPN2', - type='vpn', + name="VPN2", + type="vpn", vpn=vpn2, config=vpn.auto_client(), default=True, diff --git a/openwisp_network_topology/management/commands/__init__.py b/openwisp_network_topology/management/commands/__init__.py index 19654db5..2005bbd5 100644 --- a/openwisp_network_topology/management/commands/__init__.py +++ b/openwisp_network_topology/management/commands/__init__.py @@ -1,36 +1,36 @@ import swapper from django.core.management.base import BaseCommand -Topology = swapper.load_model('topology', 'Topology') +Topology = swapper.load_model("topology", "Topology") class BaseUpdateCommand(BaseCommand): - help = 'Update network topology' + help = "Update network topology" topology_model = Topology def add_arguments(self, parser): parser.add_argument( - '--label', - action='store', + "--label", + action="store", default=None, - help='Will update topologies containing label', + help="Will update topologies containing label", ) def handle(self, *args, **options): - self.topology_model.update_all(options['label']) + self.topology_model.update_all(options["label"]) class BaseSaveSnapshotCommand(BaseCommand): - help = 'Save network topology graph snapshot' + help = "Save network topology graph snapshot" topology_model = Topology def add_arguments(self, parser): parser.add_argument( - '--label', - action='store', + "--label", + action="store", default=None, - help='Will save snapshots of topologies containing label', + help="Will save snapshots of topologies containing label", ) def handle(self, *args, **options): - self.topology_model.save_snapshot_all(options['label']) + self.topology_model.save_snapshot_all(options["label"]) diff --git a/openwisp_network_topology/management/commands/upgrade_from_django_netjsongraph.py b/openwisp_network_topology/management/commands/upgrade_from_django_netjsongraph.py index ed6b1654..5fad055d 100644 --- a/openwisp_network_topology/management/commands/upgrade_from_django_netjsongraph.py +++ b/openwisp_network_topology/management/commands/upgrade_from_django_netjsongraph.py @@ -9,29 +9,29 @@ from swapper import get_model_name, load_model, split User = get_user_model() -Topology = load_model('topology', 'Topology') -Organization = load_model('openwisp_users', 'Organization') +Topology = load_model("topology", "Topology") +Organization = load_model("openwisp_users", "Organization") class BaseUpdateFromDjangoNetjsonGraph(BaseCommand): - help = 'Upgrade from django-netjsongraph' - app_label_users = split(get_model_name('openwisp_users', 'Organization'))[0] - app_label = split(get_model_name('topology', 'Topology'))[0] + help = "Upgrade from django-netjsongraph" + app_label_users = split(get_model_name("openwisp_users", "Organization"))[0] + app_label = split(get_model_name("topology", "Topology"))[0] def add_arguments(self, parser): parser.add_argument( - '--backup', - action='store', + "--backup", + action="store", default=settings.BASE_DIR, - help='(Optional) Path to the backup files', + help="(Optional) Path to the backup files", ) parser.add_argument( - '--organization', - action='store', + "--organization", + action="store", default=None, help=( - '(Optional) organization UUID of the organization in ' - 'which you want to import the data.' + "(Optional) organization UUID of the organization in " + "which you want to import the data." ), ) @@ -44,22 +44,22 @@ def _get_updated_permission_list( permit_list = [] for permit_pk in permissions_list: for item in permission_data: - if item['pk'] == permit_pk: + if item["pk"] == permit_pk: for content in contenttype_data: - if item['fields']['content_type'] == content['pk']: - permit_app_label = content['fields']['app_label'] - if permit_app_label == 'django_netjsongraph': + if item["fields"]["content_type"] == content["pk"]: + permit_app_label = content["fields"]["app_label"] + if permit_app_label == "django_netjsongraph": permit_app_label = self.app_label elif ( - content['fields']['model'] in ['user', 'group'] - and permit_app_label == 'auth' + content["fields"]["model"] in ["user", "group"] + and permit_app_label == "auth" ): permit_app_label = self.app_label_users try: permit_list.append( Permission.objects.get( content_type__app_label=permit_app_label, - codename=item['fields']['codename'], + codename=item["fields"]["codename"], ).pk ) except Permission.DoesNotExist: # pragma: nocover @@ -67,8 +67,8 @@ def _get_updated_permission_list( return permit_list def handle(self, *args, **options): - if options['organization']: - org = Organization.objects.get(pk=options['organization']) + if options["organization"]: + org = Organization.objects.get(pk=options["organization"]) else: org = Organization.objects.first() @@ -77,14 +77,14 @@ def handle(self, *args, **options): netjsongraph_data = json.load(netjsongraph) # Make changes for data in netjsongraph_data: - data['fields']['organization'] = str(org.pk) - data['model'] = f'{self.app_label}.{data["model"].split(".")[1]}' + data["fields"]["organization"] = str(org.pk) + data["model"] = f'{self.app_label}.{data["model"].split(".")[1]}' # Save in anotherfile - with open(f'{options["backup"]}/netjsongraph_loaded.json', 'w') as outfile: + with open(f'{options["backup"]}/netjsongraph_loaded.json', "w") as outfile: json.dump(netjsongraph_data, outfile) # Load to database call_command( - 'loaddata', f'{options["backup"]}/netjsongraph_loaded.json', verbosity=0 + "loaddata", f'{options["backup"]}/netjsongraph_loaded.json', verbosity=0 ) # Group Model @@ -96,16 +96,16 @@ def handle(self, *args, **options): group_data = json.load(group) load_group_data = [] for data in group_data: - if not Group.objects.filter(name=data['fields']['name']).exists(): + if not Group.objects.filter(name=data["fields"]["name"]).exists(): load_group_data.append( { - 'model': f'{self.app_label_users}.group', - 'pk': data['pk'] + Group.objects.count(), - 'fields': { - 'name': data['fields']['name'], - 'permissions': self._get_updated_permission_list( + "model": f"{self.app_label_users}.group", + "pk": data["pk"] + Group.objects.count(), + "fields": { + "name": data["fields"]["name"], + "permissions": self._get_updated_permission_list( permission_data, - data['fields']['permissions'], + data["fields"]["permissions"], contenttype_data, ), }, @@ -113,11 +113,11 @@ def handle(self, *args, **options): ) if load_group_data: # Save in anotherfile - with open(f'{options["backup"]}/group_loaded.json', 'w') as outfile: + with open(f'{options["backup"]}/group_loaded.json', "w") as outfile: json.dump(load_group_data, outfile) # Load to database call_command( - 'loaddata', f'{options["backup"]}/group_loaded.json', verbosity=0 + "loaddata", f'{options["backup"]}/group_loaded.json', verbosity=0 ) # User Model @@ -127,51 +127,51 @@ def handle(self, *args, **options): org_users_data = [] load_users_data = [] for data in users_data: - data['model'] = f'{self.app_label_users}.user' - data['pk'] = self.int_to_uuid(data['pk']) + data["model"] = f"{self.app_label_users}.user" + data["pk"] = self.int_to_uuid(data["pk"]) # If the user doesn't have an email, give them a # @example.com email but in openwisp-network-topology # email is UNIQUE. - if not data['fields']['email']: - data['fields']['email'] = f'{data["fields"]["username"]}@example.com' + if not data["fields"]["email"]: + data["fields"]["email"] = f'{data["fields"]["username"]}@example.com' group_list = [] - for group_pk in data['fields']['groups']: + for group_pk in data["fields"]["groups"]: for item in group_data: - if item['pk'] == group_pk: + if item["pk"] == group_pk: group_list.append( - Group.objects.filter(name=item['fields']['name']).first().pk + Group.objects.filter(name=item["fields"]["name"]).first().pk ) - data['fields']['groups'] = group_list - data['fields']['user_permissions'] = self._get_updated_permission_list( - permission_data, data['fields']['user_permissions'], contenttype_data + data["fields"]["groups"] = group_list + data["fields"]["user_permissions"] = self._get_updated_permission_list( + permission_data, data["fields"]["user_permissions"], contenttype_data ) - if not User.objects.filter(email=data['fields']['email']): + if not User.objects.filter(email=data["fields"]["email"]): load_users_data.append(data) - if not data['fields']['is_superuser']: + if not data["fields"]["is_superuser"]: org_users_data.append( { - 'model': f'{self.app_label_users}.organizationuser', - 'pk': str(uuid.uuid4()), - 'fields': { - 'created': data['fields']['date_joined'], - 'modified': data['fields']['date_joined'], - 'is_admin': False, - 'user': data['pk'], - 'organization': str(org.pk), + "model": f"{self.app_label_users}.organizationuser", + "pk": str(uuid.uuid4()), + "fields": { + "created": data["fields"]["date_joined"], + "modified": data["fields"]["date_joined"], + "is_admin": False, + "user": data["pk"], + "organization": str(org.pk), }, } ) load_users_data.extend(org_users_data) if load_users_data: # Save in anotherfile - with open(f'{options["backup"]}/user_loaded.json', 'w') as outfile: + with open(f'{options["backup"]}/user_loaded.json', "w") as outfile: json.dump(load_users_data, outfile) # Load to database call_command( - 'loaddata', f'{options["backup"]}/user_loaded.json', verbosity=0 + "loaddata", f'{options["backup"]}/user_loaded.json', verbosity=0 ) - self.stdout.write(self.style.SUCCESS('Migration Process Complete!')) + self.stdout.write(self.style.SUCCESS("Migration Process Complete!")) class Command(BaseUpdateFromDjangoNetjsonGraph): diff --git a/openwisp_network_topology/migrations/0001_initial.py b/openwisp_network_topology/migrations/0001_initial.py index 690c64a5..77e23ea5 100644 --- a/openwisp_network_topology/migrations/0001_initial.py +++ b/openwisp_network_topology/migrations/0001_initial.py @@ -21,15 +21,15 @@ class Migration(migrations.Migration): initial = True dependencies = [ - dependency(*split(settings.AUTH_USER_MODEL), version='0004_default_groups'), + dependency(*split(settings.AUTH_USER_MODEL), version="0004_default_groups"), ] operations = [ migrations.CreateModel( - name='Link', + name="Link", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -38,57 +38,57 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('cost', models.FloatField()), - ('cost_text', models.CharField(blank=True, max_length=24)), + ("cost", models.FloatField()), + ("cost_text", models.CharField(blank=True, max_length=24)), ( - 'status', + "status", model_utils.fields.StatusField( - choices=[('up', 'up'), ('down', 'down')], - default='up', + choices=[("up", "up"), ("down", "down")], + default="up", max_length=100, no_check_for_status=True, ), ), ( - 'properties', + "properties", jsonfield.fields.JSONField( blank=True, default=dict, - dump_kwargs={'indent': 4}, - load_kwargs={'object_pairs_hook': collections.OrderedDict}, + dump_kwargs={"indent": 4}, + load_kwargs={"object_pairs_hook": collections.OrderedDict}, ), ), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ], - options={'abstract': False}, + options={"abstract": False}, ), migrations.CreateModel( - name='Node', + name="Node", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -97,48 +97,48 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('label', models.CharField(blank=True, max_length=64)), - ('addresses', models.CharField(db_index=True, max_length=510)), + ("label", models.CharField(blank=True, max_length=64)), + ("addresses", models.CharField(db_index=True, max_length=510)), ( - 'properties', + "properties", jsonfield.fields.JSONField( blank=True, default=dict, - dump_kwargs={'indent': 4}, - load_kwargs={'object_pairs_hook': collections.OrderedDict}, + dump_kwargs={"indent": 4}, + load_kwargs={"object_pairs_hook": collections.OrderedDict}, ), ), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ], - options={'abstract': False}, + options={"abstract": False}, ), migrations.CreateModel( - name='Topology', + name="Topology", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -147,157 +147,157 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('label', models.CharField(max_length=64, verbose_name='label')), + ("label", models.CharField(max_length=64, verbose_name="label")), ( - 'parser', + "parser", models.CharField( choices=[ - ('netdiff.OlsrParser', 'OLSRd (txtinfo/jsoninfo)'), + ("netdiff.OlsrParser", "OLSRd (txtinfo/jsoninfo)"), ( - 'netdiff.BatmanParser', - 'batman-advanced (jsondoc/txtinfo)', + "netdiff.BatmanParser", + "batman-advanced (jsondoc/txtinfo)", ), - ('netdiff.BmxParser', 'BMX6 (q6m)'), - ('netdiff.NetJsonParser', 'NetJSON NetworkGraph'), - ('netdiff.CnmlParser', 'CNML 1.0'), + ("netdiff.BmxParser", "BMX6 (q6m)"), + ("netdiff.NetJsonParser", "NetJSON NetworkGraph"), + ("netdiff.CnmlParser", "CNML 1.0"), ], - help_text='Select topology format', + help_text="Select topology format", max_length=128, - verbose_name='format', + verbose_name="format", ), ), ( - 'strategy', + "strategy", models.CharField( - choices=[('fetch', 'FETCH'), ('receive', 'RECEIVE')], + choices=[("fetch", "FETCH"), ("receive", "RECEIVE")], db_index=True, - default='fetch', + default="fetch", max_length=16, - verbose_name='strategy', + verbose_name="strategy", ), ), ( - 'url', + "url", models.URLField( blank=True, - help_text='Topology data will be fetched from this URL (FETCH strategy)', - verbose_name='url', + help_text="Topology data will be fetched from this URL (FETCH strategy)", + verbose_name="url", ), ), ( - 'key', + "key", openwisp_utils.base.KeyField( blank=True, default=openwisp_utils.utils.get_random_key, - help_text='key needed to update topology from nodes ', + help_text="key needed to update topology from nodes ", max_length=64, validators=[ django.core.validators.RegexValidator( - re.compile('^[^\\s/\\.]+$'), - code='invalid', - message='This value must not contain spaces, dots or slashes.', + re.compile("^[^\\s/\\.]+$"), + code="invalid", + message="This value must not contain spaces, dots or slashes.", ) ], - verbose_name='key', + verbose_name="key", ), ), ( - 'expiration_time', + "expiration_time", models.PositiveIntegerField( default=0, help_text=( '"Expiration Time" in seconds: setting this to 0 will ' - 'immediately mark missing links as down; a value higher ' - 'than 0 will delay marking missing links as down until ' + "immediately mark missing links as down; a value higher " + "than 0 will delay marking missing links as down until " 'the "modified" field of a link is older than "Expiration Time"' ), - verbose_name='expiration time', + verbose_name="expiration time", ), ), ( - 'published', + "published", models.BooleanField( default=True, help_text="Unpublished topologies won't be updated or shown in the visualizer", - verbose_name='published', + verbose_name="published", ), ), ( - 'protocol', + "protocol", models.CharField( - blank=True, max_length=64, verbose_name='protocol' + blank=True, max_length=64, verbose_name="protocol" ), ), ( - 'version', - models.CharField(blank=True, max_length=24, verbose_name='version'), + "version", + models.CharField(blank=True, max_length=24, verbose_name="version"), ), ( - 'revision', + "revision", models.CharField( - blank=True, max_length=64, verbose_name='revision' + blank=True, max_length=64, verbose_name="revision" ), ), ( - 'metric', - models.CharField(blank=True, max_length=24, verbose_name='metric'), + "metric", + models.CharField(blank=True, max_length=24, verbose_name="metric"), ), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ], - options={'verbose_name_plural': 'topologies', 'abstract': False}, + options={"verbose_name_plural": "topologies", "abstract": False}, ), migrations.AddField( - model_name='node', - name='topology', + model_name="node", + name="topology", field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to='topology.Topology' + on_delete=django.db.models.deletion.CASCADE, to="topology.Topology" ), ), migrations.AddField( - model_name='link', - name='source', + model_name="link", + name="source", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='source_link_set', - to='topology.Node', + related_name="source_link_set", + to="topology.Node", ), ), migrations.AddField( - model_name='link', - name='target', + model_name="link", + name="target", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='source_target_set', - to='topology.Node', + related_name="source_target_set", + to="topology.Node", ), ), migrations.AddField( - model_name='link', - name='topology', + model_name="link", + name="topology", field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to='topology.Topology' + on_delete=django.db.models.deletion.CASCADE, to="topology.Topology" ), ), ] diff --git a/openwisp_network_topology/migrations/0002_snapshot.py b/openwisp_network_topology/migrations/0002_snapshot.py index 0a0aa189..74a592a9 100644 --- a/openwisp_network_topology/migrations/0002_snapshot.py +++ b/openwisp_network_topology/migrations/0002_snapshot.py @@ -11,14 +11,14 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0001_initial')] + dependencies = [("topology", "0001_initial")] operations = [ migrations.CreateModel( - name='Snapshot', + name="Snapshot", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -27,39 +27,39 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('data', models.TextField()), - ('date', models.DateField(auto_now=True)), + ("data", models.TextField()), + ("date", models.DateField(auto_now=True)), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ( - 'topology', + "topology", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to='topology.Topology', + to="topology.Topology", ), ), ], - options={'abstract': False, 'verbose_name_plural': 'snapshots'}, + options={"abstract": False, "verbose_name_plural": "snapshots"}, ) ] diff --git a/openwisp_network_topology/migrations/0003_link_status_and_openvpn_parser.py b/openwisp_network_topology/migrations/0003_link_status_and_openvpn_parser.py index 02178a0f..e2202268 100644 --- a/openwisp_network_topology/migrations/0003_link_status_and_openvpn_parser.py +++ b/openwisp_network_topology/migrations/0003_link_status_and_openvpn_parser.py @@ -4,29 +4,29 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0002_snapshot')] + dependencies = [("topology", "0002_snapshot")] operations = [ migrations.AddField( - model_name='link', - name='status_changed', + model_name="link", + name="status_changed", field=models.DateTimeField(auto_now=True), ), migrations.AlterField( - model_name='topology', - name='parser', + model_name="topology", + name="parser", field=models.CharField( choices=[ - ('netdiff.OlsrParser', 'OLSRd (txtinfo/jsoninfo)'), - ('netdiff.BatmanParser', 'batman-advanced (jsondoc/txtinfo)'), - ('netdiff.BmxParser', 'BMX6 (q6m)'), - ('netdiff.NetJsonParser', 'NetJSON NetworkGraph'), - ('netdiff.CnmlParser', 'CNML 1.0'), - ('netdiff.OpenvpnParser', 'OpenVPN'), + ("netdiff.OlsrParser", "OLSRd (txtinfo/jsoninfo)"), + ("netdiff.BatmanParser", "batman-advanced (jsondoc/txtinfo)"), + ("netdiff.BmxParser", "BMX6 (q6m)"), + ("netdiff.NetJsonParser", "NetJSON NetworkGraph"), + ("netdiff.CnmlParser", "CNML 1.0"), + ("netdiff.OpenvpnParser", "OpenVPN"), ], - help_text='Select topology format', + help_text="Select topology format", max_length=128, - verbose_name='format', + verbose_name="format", ), ), ] diff --git a/openwisp_network_topology/migrations/0004_fixed_target_link_set.py b/openwisp_network_topology/migrations/0004_fixed_target_link_set.py index 6c922af9..48a179c8 100644 --- a/openwisp_network_topology/migrations/0004_fixed_target_link_set.py +++ b/openwisp_network_topology/migrations/0004_fixed_target_link_set.py @@ -5,16 +5,16 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0003_link_status_and_openvpn_parser')] + dependencies = [("topology", "0003_link_status_and_openvpn_parser")] operations = [ migrations.AlterField( - model_name='link', - name='target', + model_name="link", + name="target", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='target_link_set', - to='topology.Node', + related_name="target_link_set", + to="topology.Node", ), ) ] diff --git a/openwisp_network_topology/migrations/0005_default_operator_permission.py b/openwisp_network_topology/migrations/0005_default_operator_permission.py index a77aedaf..ed609134 100644 --- a/openwisp_network_topology/migrations/0005_default_operator_permission.py +++ b/openwisp_network_topology/migrations/0005_default_operator_permission.py @@ -14,23 +14,23 @@ def _add_permission_to_group(group, models, operations): for operation in operations: try: permission = Permission.objects.get( - codename='{}_{}'.format(operation, model) + codename="{}_{}".format(operation, model) ) except Permission.DoesNotExist: continue else: group.permissions.add(permission.pk) - operators_can_manage = ['link', 'node'] - operators_can_only_view = ['topology'] - admin_can_manage = ['link', 'node', 'topology'] - manage_operations = ['add', 'change', 'delete', 'view'] - view_only_operations = ['view'] - Group = apps.get_model('openwisp_users', 'Group') + operators_can_manage = ["link", "node"] + operators_can_only_view = ["topology"] + admin_can_manage = ["link", "node", "topology"] + manage_operations = ["add", "change", "delete", "view"] + view_only_operations = ["view"] + Group = apps.get_model("openwisp_users", "Group") try: - admin = Group.objects.get(name='Administrator') - operator = Group.objects.get(name='Operator') + admin = Group.objects.get(name="Administrator") + operator = Group.objects.get(name="Operator") # consider failures custom cases # that do not have to be dealt with except Group.DoesNotExist: @@ -45,8 +45,8 @@ def _add_permission_to_group(group, models, operations): class Migration(migrations.Migration): dependencies = [ - dependency(*split(settings.AUTH_USER_MODEL), version='0004_default_groups'), - ('topology', '0004_fixed_target_link_set'), + dependency(*split(settings.AUTH_USER_MODEL), version="0004_default_groups"), + ("topology", "0004_fixed_target_link_set"), ] operations = [ migrations.RunPython( diff --git a/openwisp_network_topology/migrations/0006_reformat_addresses.py b/openwisp_network_topology/migrations/0006_reformat_addresses.py index 42524ff1..820f16d5 100644 --- a/openwisp_network_topology/migrations/0006_reformat_addresses.py +++ b/openwisp_network_topology/migrations/0006_reformat_addresses.py @@ -2,23 +2,23 @@ def reformat_address_forward(apps, schema_editor): - Node = apps.get_model('topology', 'Node') + Node = apps.get_model("topology", "Node") for node in Node.objects.all(): - if not node.addresses.startswith(';'): - node.addresses = ';{0}'.format(node.addresses) + if not node.addresses.startswith(";"): + node.addresses = ";{0}".format(node.addresses) node.save() def reformat_address_backward(apps, schema_editor): - Fake_node_model = apps.get_model('topology', 'Node') + Fake_node_model = apps.get_model("topology", "Node") for node in Fake_node_model.objects.all(): - if node.addresses.startswith(';'): + if node.addresses.startswith(";"): node.addresses = node.addresses[1:] node.save() class Migration(migrations.Migration): - dependencies = [('topology', '0005_default_operator_permission')] + dependencies = [("topology", "0005_default_operator_permission")] operations = [ migrations.RunPython(reformat_address_forward, reformat_address_backward) diff --git a/openwisp_network_topology/migrations/0007_create_new_address_field.py b/openwisp_network_topology/migrations/0007_create_new_address_field.py index d4a84664..25199af5 100644 --- a/openwisp_network_topology/migrations/0007_create_new_address_field.py +++ b/openwisp_network_topology/migrations/0007_create_new_address_field.py @@ -5,15 +5,15 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0006_reformat_addresses')] + dependencies = [("topology", "0006_reformat_addresses")] operations = [ migrations.RenameField( - model_name='node', old_name='addresses', new_name='addresses_old' + model_name="node", old_name="addresses", new_name="addresses_old" ), migrations.AddField( - model_name='node', - name='addresses', + model_name="node", + name="addresses", field=jsonfield.fields.JSONField(default=[]), ), ] diff --git a/openwisp_network_topology/migrations/0008_migrate_addresses_data.py b/openwisp_network_topology/migrations/0008_migrate_addresses_data.py index 2ecdc962..55fa06c7 100644 --- a/openwisp_network_topology/migrations/0008_migrate_addresses_data.py +++ b/openwisp_network_topology/migrations/0008_migrate_addresses_data.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0007_create_new_address_field')] + dependencies = [("topology", "0007_create_new_address_field")] operations = [ migrations.RunPython(migrate_addresses, reverse_code=migrations.RunPython.noop) diff --git a/openwisp_network_topology/migrations/0009_remove_old_addresses.py b/openwisp_network_topology/migrations/0009_remove_old_addresses.py index 24c95019..a39bd302 100644 --- a/openwisp_network_topology/migrations/0009_remove_old_addresses.py +++ b/openwisp_network_topology/migrations/0009_remove_old_addresses.py @@ -4,6 +4,6 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0008_migrate_addresses_data')] + dependencies = [("topology", "0008_migrate_addresses_data")] - operations = [migrations.RemoveField(model_name='node', name='addresses_old')] + operations = [migrations.RemoveField(model_name="node", name="addresses_old")] diff --git a/openwisp_network_topology/migrations/0010_properties_json.py b/openwisp_network_topology/migrations/0010_properties_json.py index 7be7f396..02495a00 100644 --- a/openwisp_network_topology/migrations/0010_properties_json.py +++ b/openwisp_network_topology/migrations/0010_properties_json.py @@ -8,38 +8,38 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0009_remove_old_addresses')] + dependencies = [("topology", "0009_remove_old_addresses")] operations = [ migrations.AlterField( - model_name='link', - name='properties', + model_name="link", + name="properties", field=jsonfield.fields.JSONField( blank=True, default=dict, dump_kwargs={ - 'cls': rest_framework.utils.encoders.JSONEncoder, - 'indent': 4, + "cls": rest_framework.utils.encoders.JSONEncoder, + "indent": 4, }, - load_kwargs={'object_pairs_hook': collections.OrderedDict}, + load_kwargs={"object_pairs_hook": collections.OrderedDict}, ), ), migrations.AlterField( - model_name='node', - name='addresses', + model_name="node", + name="addresses", field=jsonfield.fields.JSONField(blank=True, default=[]), ), migrations.AlterField( - model_name='node', - name='properties', + model_name="node", + name="properties", field=jsonfield.fields.JSONField( blank=True, default=dict, dump_kwargs={ - 'cls': rest_framework.utils.encoders.JSONEncoder, - 'indent': 4, + "cls": rest_framework.utils.encoders.JSONEncoder, + "indent": 4, }, - load_kwargs={'object_pairs_hook': collections.OrderedDict}, + load_kwargs={"object_pairs_hook": collections.OrderedDict}, ), ), ] diff --git a/openwisp_network_topology/migrations/0011_fix_link_properties.py b/openwisp_network_topology/migrations/0011_fix_link_properties.py index 30062565..9f7be14b 100644 --- a/openwisp_network_topology/migrations/0011_fix_link_properties.py +++ b/openwisp_network_topology/migrations/0011_fix_link_properties.py @@ -4,7 +4,7 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0010_properties_json')] + dependencies = [("topology", "0010_properties_json")] operations = [ migrations.RunPython( diff --git a/openwisp_network_topology/migrations/0012_update_openvpn_netjson_ids.py b/openwisp_network_topology/migrations/0012_update_openvpn_netjson_ids.py index 55df3f8e..dc8b55f0 100644 --- a/openwisp_network_topology/migrations/0012_update_openvpn_netjson_ids.py +++ b/openwisp_network_topology/migrations/0012_update_openvpn_netjson_ids.py @@ -4,7 +4,7 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0011_fix_link_properties')] + dependencies = [("topology", "0011_fix_link_properties")] operations = [ migrations.RunPython( diff --git a/openwisp_network_topology/migrations/0013_add_user_defined_properties_field.py b/openwisp_network_topology/migrations/0013_add_user_defined_properties_field.py index 8d95520a..6a94627e 100644 --- a/openwisp_network_topology/migrations/0013_add_user_defined_properties_field.py +++ b/openwisp_network_topology/migrations/0013_add_user_defined_properties_field.py @@ -8,37 +8,37 @@ class Migration(migrations.Migration): - dependencies = [('topology', '0012_update_openvpn_netjson_ids')] + dependencies = [("topology", "0012_update_openvpn_netjson_ids")] operations = [ migrations.AddField( - model_name='link', - name='user_properties', + model_name="link", + name="user_properties", field=jsonfield.fields.JSONField( blank=True, default=dict, dump_kwargs={ - 'cls': rest_framework.utils.encoders.JSONEncoder, - 'indent': 4, + "cls": rest_framework.utils.encoders.JSONEncoder, + "indent": 4, }, - help_text='If you need to add additional data to this link use this field', - load_kwargs={'object_pairs_hook': collections.OrderedDict}, - verbose_name='user defined properties', + help_text="If you need to add additional data to this link use this field", + load_kwargs={"object_pairs_hook": collections.OrderedDict}, + verbose_name="user defined properties", ), ), migrations.AddField( - model_name='node', - name='user_properties', + model_name="node", + name="user_properties", field=jsonfield.fields.JSONField( blank=True, default=dict, dump_kwargs={ - 'cls': rest_framework.utils.encoders.JSONEncoder, - 'indent': 4, + "cls": rest_framework.utils.encoders.JSONEncoder, + "indent": 4, }, - help_text='If you need to add additional data to this node use this field', - load_kwargs={'object_pairs_hook': collections.OrderedDict}, - verbose_name='user defined properties', + help_text="If you need to add additional data to this node use this field", + load_kwargs={"object_pairs_hook": collections.OrderedDict}, + verbose_name="user defined properties", ), ), ] diff --git a/openwisp_network_topology/migrations/0014_remove_snapshot_organization.py b/openwisp_network_topology/migrations/0014_remove_snapshot_organization.py index 98757935..c4616dfb 100644 --- a/openwisp_network_topology/migrations/0014_remove_snapshot_organization.py +++ b/openwisp_network_topology/migrations/0014_remove_snapshot_organization.py @@ -5,12 +5,12 @@ class Migration(migrations.Migration): dependencies = [ - ('topology', '0013_add_user_defined_properties_field'), + ("topology", "0013_add_user_defined_properties_field"), ] operations = [ migrations.RemoveField( - model_name='snapshot', - name='organization', + model_name="snapshot", + name="organization", ), ] diff --git a/openwisp_network_topology/migrations/0015_shareable_topology_node_link.py b/openwisp_network_topology/migrations/0015_shareable_topology_node_link.py index 1d4cee90..5b6a8dcf 100644 --- a/openwisp_network_topology/migrations/0015_shareable_topology_node_link.py +++ b/openwisp_network_topology/migrations/0015_shareable_topology_node_link.py @@ -7,41 +7,41 @@ class Migration(migrations.Migration): dependencies = [ - ('topology', '0014_remove_snapshot_organization'), + ("topology", "0014_remove_snapshot_organization"), ] operations = [ migrations.AlterField( - model_name='link', - name='organization', + model_name="link", + name="organization", field=models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), migrations.AlterField( - model_name='node', - name='organization', + model_name="node", + name="organization", field=models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), migrations.AlterField( - model_name='topology', - name='organization', + model_name="topology", + name="organization", field=models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ] diff --git a/openwisp_network_topology/migrations/0016_alter_topology_parser.py b/openwisp_network_topology/migrations/0016_alter_topology_parser.py index b8b26eb2..65cc6840 100644 --- a/openwisp_network_topology/migrations/0016_alter_topology_parser.py +++ b/openwisp_network_topology/migrations/0016_alter_topology_parser.py @@ -5,27 +5,27 @@ class Migration(migrations.Migration): dependencies = [ - ('topology', '0015_shareable_topology_node_link'), + ("topology", "0015_shareable_topology_node_link"), ] operations = [ migrations.AlterField( - model_name='topology', - name='parser', + model_name="topology", + name="parser", field=models.CharField( choices=[ - ('netdiff.OlsrParser', 'OLSRd (txtinfo/jsoninfo)'), - ('netdiff.BatmanParser', 'batman-advanced (jsondoc/txtinfo)'), - ('netdiff.BmxParser', 'BMX6 (q6m)'), - ('netdiff.NetJsonParser', 'NetJSON NetworkGraph'), - ('netdiff.CnmlParser', 'CNML 1.0'), - ('netdiff.OpenvpnParser', 'OpenVPN'), - ('netdiff.WireguardParser', 'Wireguard'), - ('netdiff.ZeroTierParser', 'ZeroTier'), + ("netdiff.OlsrParser", "OLSRd (txtinfo/jsoninfo)"), + ("netdiff.BatmanParser", "batman-advanced (jsondoc/txtinfo)"), + ("netdiff.BmxParser", "BMX6 (q6m)"), + ("netdiff.NetJsonParser", "NetJSON NetworkGraph"), + ("netdiff.CnmlParser", "CNML 1.0"), + ("netdiff.OpenvpnParser", "OpenVPN"), + ("netdiff.WireguardParser", "Wireguard"), + ("netdiff.ZeroTierParser", "ZeroTier"), ], - help_text='Select topology format', + help_text="Select topology format", max_length=128, - verbose_name='format', + verbose_name="format", ), ), ] diff --git a/openwisp_network_topology/migrations/__init__.py b/openwisp_network_topology/migrations/__init__.py index 8d646a7f..b5ffba00 100644 --- a/openwisp_network_topology/migrations/__init__.py +++ b/openwisp_network_topology/migrations/__init__.py @@ -10,19 +10,19 @@ def get_model(apps, app_name, model): def migrate_addresses(apps, schema_editor): - Node = get_model(apps, 'topology', 'Node') + Node = get_model(apps, "topology", "Node") for node in Node.objects.iterator(): - addresses = node.addresses_old.replace(' ', '') - if addresses.startswith(';'): + addresses = node.addresses_old.replace(" ", "") + if addresses.startswith(";"): addresses = addresses[1:] - addresses = addresses[0:-1].split(';') + addresses = addresses[0:-1].split(";") node.addresses = addresses node.save() def migrate_openvpn_ids_0012(apps, schema_editor): - Node = get_model(apps, 'topology', 'Node') - queryset = Node.objects.filter(topology__parser='netdiff.OpenvpnParser') + Node = get_model(apps, "topology", "Node") + queryset = Node.objects.filter(topology__parser="netdiff.OpenvpnParser") for node in queryset.iterator(): node.addresses[0] = node.label node.full_clean() @@ -30,7 +30,7 @@ def migrate_openvpn_ids_0012(apps, schema_editor): def fix_link_properties(apps, schema_editor): - Link = get_model(apps, 'topology', 'Link') + Link = get_model(apps, "topology", "Link") for link in Link.objects.all(): link.full_clean() link.save() diff --git a/openwisp_network_topology/models.py b/openwisp_network_topology/models.py index 35c40fab..41b6dc44 100644 --- a/openwisp_network_topology/models.py +++ b/openwisp_network_topology/models.py @@ -9,22 +9,22 @@ class Link(AbstractLink): class Meta(AbstractLink.Meta): abstract = False - swappable = swapper.swappable_setting('topology', 'Link') + swappable = swapper.swappable_setting("topology", "Link") class Node(AbstractNode): class Meta(AbstractNode.Meta): abstract = False - swappable = swapper.swappable_setting('topology', 'Node') + swappable = swapper.swappable_setting("topology", "Node") class Snapshot(AbstractSnapshot): class Meta(AbstractSnapshot.Meta): abstract = False - swappable = swapper.swappable_setting('topology', 'Snapshot') + swappable = swapper.swappable_setting("topology", "Snapshot") class Topology(AbstractTopology): class Meta(AbstractTopology.Meta): abstract = False - swappable = swapper.swappable_setting('topology', 'Topology') + swappable = swapper.swappable_setting("topology", "Topology") diff --git a/openwisp_network_topology/routing.py b/openwisp_network_topology/routing.py index 2ea0c44d..da5dae16 100644 --- a/openwisp_network_topology/routing.py +++ b/openwisp_network_topology/routing.py @@ -6,7 +6,7 @@ # This route is used by both # the admin and non-admin topology view path( - 'ws/network-topology/topology//', + "ws/network-topology/topology//", consumers.TopologyConsumer.as_asgi(), ), ] diff --git a/openwisp_network_topology/settings.py b/openwisp_network_topology/settings.py index 4b423d4d..5697d618 100644 --- a/openwisp_network_topology/settings.py +++ b/openwisp_network_topology/settings.py @@ -5,41 +5,41 @@ # 'pre_django_setup' is supposed to be a logger # that can work before registered Apps are # ready in django.setup() process. -logger = logging.getLogger('pre_django_setup') +logger = logging.getLogger("pre_django_setup") def get_settings_value(option, default): outdated_option_used = False - if hasattr(settings, f'NETJSONGRAPH_{option}'): - outdated_option_used = 'NETJSONGRAPH' - elif hasattr(settings, f'TOPOLOGY_{option}'): - outdated_option_used = 'TOPOLOGY' + if hasattr(settings, f"NETJSONGRAPH_{option}"): + outdated_option_used = "NETJSONGRAPH" + elif hasattr(settings, f"TOPOLOGY_{option}"): + outdated_option_used = "TOPOLOGY" if outdated_option_used: logger.warn( - f'{outdated_option_used}_{option} setting is deprecated. It will be removed ' - f'in the future, please use OPENWISP_NETWORK_TOPOLOGY_{option} instead.' + f"{outdated_option_used}_{option} setting is deprecated. It will be removed " + f"in the future, please use OPENWISP_NETWORK_TOPOLOGY_{option} instead." ) - return getattr(settings, f'{outdated_option_used}_{option}') - return getattr(settings, f'OPENWISP_NETWORK_TOPOLOGY_{option}', default) + return getattr(settings, f"{outdated_option_used}_{option}") + return getattr(settings, f"OPENWISP_NETWORK_TOPOLOGY_{option}", default) DEFAULT_PARSERS = [ - ('netdiff.OlsrParser', 'OLSRd (txtinfo/jsoninfo)'), - ('netdiff.BatmanParser', 'batman-advanced (jsondoc/txtinfo)'), - ('netdiff.BmxParser', 'BMX6 (q6m)'), - ('netdiff.NetJsonParser', 'NetJSON NetworkGraph'), - ('netdiff.CnmlParser', 'CNML 1.0'), - ('netdiff.OpenvpnParser', 'OpenVPN'), - ('netdiff.WireguardParser', 'Wireguard'), - ('netdiff.ZeroTierParser', 'ZeroTier'), + ("netdiff.OlsrParser", "OLSRd (txtinfo/jsoninfo)"), + ("netdiff.BatmanParser", "batman-advanced (jsondoc/txtinfo)"), + ("netdiff.BmxParser", "BMX6 (q6m)"), + ("netdiff.NetJsonParser", "NetJSON NetworkGraph"), + ("netdiff.CnmlParser", "CNML 1.0"), + ("netdiff.OpenvpnParser", "OpenVPN"), + ("netdiff.WireguardParser", "Wireguard"), + ("netdiff.ZeroTierParser", "ZeroTier"), ] -PARSERS = DEFAULT_PARSERS + get_settings_value('PARSERS', []) -SIGNALS = get_settings_value('SIGNALS', None) -TIMEOUT = get_settings_value('TIMEOUT', 8) -LINK_EXPIRATION = get_settings_value('LINK_EXPIRATION', 60) -NODE_EXPIRATION = get_settings_value('NODE_EXPIRATION', False) -VISUALIZER_CSS = get_settings_value('VISUALIZER_CSS', 'netjsongraph/css/style.css') -TOPOLOGY_API_URLCONF = get_settings_value('API_URLCONF', None) -TOPOLOGY_API_BASEURL = get_settings_value('API_BASEURL', None) -TOPOLOGY_API_AUTH_REQUIRED = get_settings_value('API_AUTH_REQUIRED', True) +PARSERS = DEFAULT_PARSERS + get_settings_value("PARSERS", []) +SIGNALS = get_settings_value("SIGNALS", None) +TIMEOUT = get_settings_value("TIMEOUT", 8) +LINK_EXPIRATION = get_settings_value("LINK_EXPIRATION", 60) +NODE_EXPIRATION = get_settings_value("NODE_EXPIRATION", False) +VISUALIZER_CSS = get_settings_value("VISUALIZER_CSS", "netjsongraph/css/style.css") +TOPOLOGY_API_URLCONF = get_settings_value("API_URLCONF", None) +TOPOLOGY_API_BASEURL = get_settings_value("API_BASEURL", None) +TOPOLOGY_API_AUTH_REQUIRED = get_settings_value("API_AUTH_REQUIRED", True) diff --git a/openwisp_network_topology/signals.py b/openwisp_network_topology/signals.py index 0ed6ae90..e8efca8b 100644 --- a/openwisp_network_topology/signals.py +++ b/openwisp_network_topology/signals.py @@ -11,6 +11,6 @@ def broadcast_topology(topology, *args, **kwargs): channel_layer = layers.get_channel_layer() async_to_sync(channel_layer.group_send)( - f'topology-{topology.pk}', - {'type': 'send_topology_update', 'data': topology.json()}, + f"topology-{topology.pk}", + {"type": "send_topology_update", "data": topology.json()}, ) diff --git a/openwisp_network_topology/tasks.py b/openwisp_network_topology/tasks.py index 01d713b0..4ffc80fc 100644 --- a/openwisp_network_topology/tasks.py +++ b/openwisp_network_topology/tasks.py @@ -20,7 +20,7 @@ def handle_update_topology(topology_pk, diff): diff (str): A dict containing the network topology diff. """ - Topology = load_model('topology', 'Topology') + Topology = load_model("topology", "Topology") try: topology = Topology.objects.get(pk=topology_pk) except ObjectDoesNotExist as e: diff --git a/openwisp_network_topology/tests/test_admin.py b/openwisp_network_topology/tests/test_admin.py index 7a03ad02..256c35e4 100644 --- a/openwisp_network_topology/tests/test_admin.py +++ b/openwisp_network_topology/tests/test_admin.py @@ -14,43 +14,43 @@ from ..admin import TopologyAdmin from .utils import CreateGraphObjectsMixin, CreateOrgMixin, LoadMixin -Link = swapper.load_model('topology', 'Link') -Node = swapper.load_model('topology', 'Node') -Topology = swapper.load_model('topology', 'Topology') +Link = swapper.load_model("topology", "Link") +Node = swapper.load_model("topology", "Node") +Topology = swapper.load_model("topology", "Topology") class TestAdmin(CreateGraphObjectsMixin, CreateOrgMixin, LoadMixin, TestCase): - module = 'openwisp_network_topology' - app_label = 'topology' + module = "openwisp_network_topology" + app_label = "topology" topology_model = Topology link_model = Link node_model = Node user_model = get_user_model() - fixtures = ['test_users.json'] - api_urls_path = 'api.urls' + fixtures = ["test_users.json"] + api_urls_path = "api.urls" @property def prefix(self): - return 'admin:{0}'.format(self.app_label) + return "admin:{0}".format(self.app_label) def setUp(self): org = self._create_org() t = self._create_topology(organization=org) self._create_node( - label='node1', addresses=['192.168.0.1'], topology=t, organization=org + label="node1", addresses=["192.168.0.1"], topology=t, organization=org ) self._create_node( - label='node2', addresses=['192.168.0.2'], topology=t, organization=org + label="node2", addresses=["192.168.0.2"], topology=t, organization=org ) - self.client.force_login(self.user_model.objects.get(username='admin')) - self.changelist_path = reverse('{0}_topology_changelist'.format(self.prefix)) + self.client.force_login(self.user_model.objects.get(username="admin")) + self.changelist_path = reverse("{0}_topology_changelist".format(self.prefix)) def test_unpublish_selected(self): t = self.topology_model.objects.first() self.assertEqual(t.published, True) self.client.post( self.changelist_path, - {'action': 'unpublish_selected', '_selected_action': str(t.pk)}, + {"action": "unpublish_selected", "_selected_action": str(t.pk)}, ) t.refresh_from_db() self.assertEqual(t.published, False) @@ -61,7 +61,7 @@ def test_publish_selected(self): t.save() self.client.post( self.changelist_path, - {'action': 'publish_selected', '_selected_action': str(t.pk)}, + {"action": "publish_selected", "_selected_action": str(t.pk)}, ) t.refresh_from_db() self.assertEqual(t.published, True) @@ -69,18 +69,18 @@ def test_publish_selected(self): @responses.activate def test_update_selected(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', - body=self._load('static/netjson-1-link.json'), - content_type='application/json', + "http://127.0.0.1:9090", + body=self._load("static/netjson-1-link.json"), + content_type="application/json", ) self.node_model.objects.all().delete() self.client.post( self.changelist_path, - {'action': 'update_selected', '_selected_action': str(t.pk)}, + {"action": "update_selected", "_selected_action": str(t.pk)}, ) self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) @@ -89,116 +89,116 @@ def test_update_selected(self): @responses.activate def test_update_selected_failed(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', + "http://127.0.0.1:9090", body='{"error": "not found"}', status=404, - content_type='application/json', + content_type="application/json", ) self.node_model.objects.all().delete() response = self.client.post( self.changelist_path, - {'action': 'update_selected', '_selected_action': str(t.pk)}, + {"action": "update_selected", "_selected_action": str(t.pk)}, follow=True, ) self.assertEqual(self.node_model.objects.count(), 0) self.assertEqual(self.link_model.objects.count(), 0) - message = list(response.context['messages'])[0] - self.assertEqual(message.tags, 'error') - self.assertIn('not updated', message.message) + message = list(response.context["messages"])[0] + self.assertEqual(message.tags, "error") + self.assertIn("not updated", message.message) def test_topology_viewonsite(self): topology = self.topology_model.objects.first() - path = reverse('{0}_topology_change'.format(self.prefix), args=[topology.pk]) + path = reverse("{0}_topology_change".format(self.prefix), args=[topology.pk]) response = self.client.get(path) - self.assertContains(response, 'View on site') + self.assertContains(response, "View on site") # Pattern for the link - pattern = '{0}{1}'.format(r'/admin/r/[0-9][0-9]?/', f'{topology.pk}') + pattern = "{0}{1}".format(r"/admin/r/[0-9][0-9]?/", f"{topology.pk}") self.assertTrue(bool(re.compile(pattern).search(str(response.content)))) def test_topology_receive_url(self): t = self.topology_model.objects.first() - t.strategy = 'receive' + t.strategy = "receive" t.save() - path = reverse('{0}_topology_change'.format(self.prefix), args=[t.pk]) + path = reverse("{0}_topology_change".format(self.prefix), args=[t.pk]) response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertContains(response, 'field-receive_url') + self.assertContains(response, "field-receive_url") def test_custom_topology_receive_url(self): t = self.topology_model.objects.first() - t.strategy = 'receive' + t.strategy = "receive" t.save() - path = reverse('{0}_topology_change'.format(self.prefix), args=[t.pk]) + path = reverse("{0}_topology_change".format(self.prefix), args=[t.pk]) # No change in URL Test - receive_path = f'network-topology/topology/{t.pk}/receive/' + receive_path = f"network-topology/topology/{t.pk}/receive/" response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertContains(response, 'field-receive_url') - self.assertContains(response, f'http://testserver/api/v1/{receive_path}') + self.assertContains(response, "field-receive_url") + self.assertContains(response, f"http://testserver/api/v1/{receive_path}") # Change URL Test - TopologyAdmin.receive_url_baseurl = 'http://changedurlbase' + TopologyAdmin.receive_url_baseurl = "http://changedurlbase" response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertContains(response, 'field-receive_url') - self.assertContains(response, f'http://changedurlbase/api/v1/{receive_path}') + self.assertContains(response, "field-receive_url") + self.assertContains(response, f"http://changedurlbase/api/v1/{receive_path}") # Change URLConf Test - TopologyAdmin.receive_url_urlconf = '{}.{}'.format( + TopologyAdmin.receive_url_urlconf = "{}.{}".format( self.module, self.api_urls_path ) response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertContains(response, 'field-receive_url') - self.assertContains(response, f'http://changedurlbase/{receive_path}') + self.assertContains(response, "field-receive_url") + self.assertContains(response, f"http://changedurlbase/{receive_path}") # Reset test options TopologyAdmin.receive_url_baseurl = None TopologyAdmin.receive_url_urlconf = None def test_node_change_form(self): n = self.node_model.objects.first() - path = reverse('{0}_node_change'.format(self.prefix), args=[n.pk]) + path = reverse("{0}_node_change".format(self.prefix), args=[n.pk]) response = self.client.get(path) - self.assertContains(response, 'Links to other nodes') - self.assertNotContains(response, 'organization_id') + self.assertContains(response, "Links to other nodes") + self.assertNotContains(response, "organization_id") self.assertContains(response, n.topology.organization.name) def test_node_change_list_queries(self): - path = reverse('{0}_node_changelist'.format(self.prefix)) + path = reverse("{0}_node_changelist".format(self.prefix)) with self.assertNumQueries(5): self.client.get(path) def test_link_change_list_queries(self): t = Topology.objects.first() - n1 = self._create_node(label='node1org1', topology=t) - n2 = self._create_node(label='node2org1', topology=t) + n1 = self._create_node(label="node1org1", topology=t) + n2 = self._create_node(label="node2org1", topology=t) self._create_link(topology=t, source=n1, target=n2) - path = reverse('{0}_link_changelist'.format(self.prefix)) + path = reverse("{0}_link_changelist".format(self.prefix)) with self.assertNumQueries(5): self.client.get(path) def test_link_change_form(self): t = Topology.objects.first() - n1 = self._create_node(label='node1org1', topology=t) - n2 = self._create_node(label='node2org1', topology=t) + n1 = self._create_node(label="node1org1", topology=t) + n2 = self._create_node(label="node2org1", topology=t) link = self._create_link(topology=t, source=n1, target=n2) - path = reverse('{0}_link_change'.format(self.prefix), args=[link.pk]) + path = reverse("{0}_link_change".format(self.prefix), args=[link.pk]) response = self.client.get(path) - self.assertNotContains(response, 'organization_id') + self.assertNotContains(response, "organization_id") self.assertContains(response, link.topology.organization.name) def test_node_add(self): - path = reverse('{0}_node_add'.format(self.prefix)) + path = reverse("{0}_node_add".format(self.prefix)) response = self.client.get(path) - self.assertNotContains(response, 'Links to other nodes') + self.assertNotContains(response, "Links to other nodes") def test_topology_change_form(self): topology = self.topology_model.objects.first() - path = reverse('{0}_topology_change'.format(self.prefix), args=[topology.pk]) + path = reverse("{0}_topology_change".format(self.prefix), args=[topology.pk]) response = self.client.get(path) - self.assertContains(response, 'View topology graph') + self.assertContains(response, "View topology graph") self.assertContains( response, '
{0}
'.format(topology.pk) ) @@ -206,71 +206,71 @@ def test_topology_change_form(self): def test_topology_visualize_view(self): t = self.topology_model.objects.first() - path = reverse('{0}_topology_visualize'.format(self.prefix), args=[t.pk]) + path = reverse("{0}_topology_visualize".format(self.prefix), args=[t.pk]) response = self.client.get(path) - self.assertContains(response, 'new NetJSONGraph(') + self.assertContains(response, "new NetJSONGraph(") def test_update_selected_receive_topology(self): t = self.topology_model.objects.first() - t.label = 'test receive' - t.parser = 'netdiff.NetJsonParser' - t.strategy = 'receive' + t.label = "test receive" + t.parser = "netdiff.NetJsonParser" + t.strategy = "receive" t.save() response = self.client.post( self.changelist_path, - {'action': 'update_selected', '_selected_action': str(t.pk)}, + {"action": "update_selected", "_selected_action": str(t.pk)}, follow=True, ) - message = list(response.context['messages'])[0] - self.assertEqual('warning', message.tags) - self.assertIn('1 topology was ignored', message.message) + message = list(response.context["messages"])[0] + self.assertEqual("warning", message.tags) + self.assertIn("1 topology was ignored", message.message) def _test_properties_field(self, model, obj): - with self.subTest('test old properties readonly'): + with self.subTest("test old properties readonly"): content = 'readonly_properties">' - r = self.client.get(reverse(f'{self.prefix}_{model}_add')) + r = self.client.get(reverse(f"{self.prefix}_{model}_add")) self.assertEqual(r.status_code, 200) self.assertContains(r, content) - with self.subTest('test old properties display in list'): - path = reverse(f'{self.prefix}_{model}_change', args=[obj.pk]) + with self.subTest("test old properties display in list"): + path = reverse(f"{self.prefix}_{model}_change", args=[obj.pk]) r = self.client.get(path) self.assertEqual(r.status_code, 200) - content = '

Gateway: False

' + content = "

Gateway: False

" self.assertContains(r, content) - with self.subTest('test user properties diplay in flat json widgets'): - r = self.client.get(reverse(f'{self.prefix}_{model}_add')) + with self.subTest("test user properties diplay in flat json widgets"): + r = self.client.get(reverse(f"{self.prefix}_{model}_add")) self.assertEqual(r.status_code, 200) - self.assertContains(r, 'flat-json-user_properties') + self.assertContains(r, "flat-json-user_properties") def test_node_user_properties_field(self): t = Topology.objects.first() - n = self._create_node(label='node1org1', topology=t) - n.properties = {'gateway': False} + n = self._create_node(label="node1org1", topology=t) + n.properties = {"gateway": False} n.full_clean() n.save() - self._test_properties_field('node', n) + self._test_properties_field("node", n) def test_link_user_properties_field(self): t = Topology.objects.first() - n1 = self._create_node(label='node1org1', topology=t) - n2 = self._create_node(label='node2org1', topology=t) + n1 = self._create_node(label="node1org1", topology=t) + n2 = self._create_node(label="node2org1", topology=t) li = self._create_link(topology=t, source=n1, target=n2) - li.properties = {'gateway': False} + li.properties = {"gateway": False} li.full_clean() li.save() - self._test_properties_field('link', li) + self._test_properties_field("link", li) def test_admin_menu_groups(self): # Test menu group (openwisp-utils menu group) for Topology and Link # and Node - self.client.force_login(self.user_model.objects.get(username='admin')) - response = self.client.get(reverse('admin:index')) - models = ['topology', 'node', 'link'] + self.client.force_login(self.user_model.objects.get(username="admin")) + response = self.client.get(reverse("admin:index")) + models = ["topology", "node", "link"] for model in models: - with self.subTest('test menu group link for {model} model'): - url = reverse(f'admin:{self.app_label}_{model}_changelist') + with self.subTest("test menu group link for {model} model"): + url = reverse(f"admin:{self.app_label}_{model}_changelist") self.assertContains(response, f'class="mg-link" href="{url}"') with self.subTest('test "networking topology" group is registered'): self.assertContains( @@ -287,29 +287,29 @@ class TestMultitenantAdmin( TestOrganizationMixin, TestCase, ): - app_label = 'topology' + app_label = "topology" topology_model = Topology node_model = Node link_model = Link def _create_multitenancy_test_env(self): - org1 = self._create_org(name='test1org') - org2 = self._create_org(name='test2org') - inactive = self._create_org(name='inactive-org', is_active=False) + org1 = self._create_org(name="test1org") + org2 = self._create_org(name="test2org") + inactive = self._create_org(name="inactive-org", is_active=False) operator = self._create_operator(organizations=[org1, inactive]) administrator = self._create_administrator(organizations=[org1, inactive]) - t1 = self._create_topology(label='topology1org', organization=org1) - t2 = self._create_topology(label='topology2org', organization=org2) - t3 = self._create_topology(label='topology3org', organization=inactive) - n11 = self._create_node(label='node1org1', topology=t1, organization=org1) - n12 = self._create_node(label='node2org1', topology=t1, organization=org1) - n21 = self._create_node(label='node1org2', topology=t2, organization=org2) - n22 = self._create_node(label='node2org2', topology=t2, organization=org2) + t1 = self._create_topology(label="topology1org", organization=org1) + t2 = self._create_topology(label="topology2org", organization=org2) + t3 = self._create_topology(label="topology3org", organization=inactive) + n11 = self._create_node(label="node1org1", topology=t1, organization=org1) + n12 = self._create_node(label="node2org1", topology=t1, organization=org1) + n21 = self._create_node(label="node1org2", topology=t2, organization=org2) + n22 = self._create_node(label="node2org2", topology=t2, organization=org2) n31 = self._create_node( - label='node1inactive', topology=t3, organization=inactive + label="node1inactive", topology=t3, organization=inactive ) n32 = self._create_node( - label='node2inactive', topology=t3, organization=inactive + label="node2inactive", topology=t3, organization=inactive ) l1 = self._create_link(topology=t1, organization=org1, source=n11, target=n12) l2 = self._create_link(topology=t2, organization=org2, source=n21, target=n22) @@ -338,122 +338,122 @@ def _create_multitenancy_test_env(self): return data def _get_autocomplete_view_path( - self, app_label, model_name, field_name, path='admin:ow-auto-filter' + self, app_label, model_name, field_name, path="admin:ow-auto-filter" ): return ( - f'{reverse(path)}?app_label={app_label}' - f'&model_name={model_name}&field_name={field_name}' + f"{reverse(path)}?app_label={app_label}" + f"&model_name={model_name}&field_name={field_name}" ) def test_topology_queryset(self): data = self._create_multitenancy_test_env() perm = Permission.objects.get_by_natural_key( - 'delete_topology', self.app_label, self.topology_model.__name__.lower() + "delete_topology", self.app_label, self.topology_model.__name__.lower() ) - data['operator'].user_permissions.remove(perm) + data["operator"].user_permissions.remove(perm) self._test_multitenant_admin( - url=reverse(f'admin:{self.app_label}_topology_changelist'), - visible=[data['t1'].label, data['org1'].name], - hidden=[data['t2'].label, data['org2'].name, data['t3_inactive'].label], + url=reverse(f"admin:{self.app_label}_topology_changelist"), + visible=[data["t1"].label, data["org1"].name], + hidden=[data["t2"].label, data["org2"].name, data["t3_inactive"].label], ) def test_topology_organization_fk_autocomplete_view(self): data = self._create_multitenancy_test_env() self._test_multitenant_admin( url=self._get_autocomplete_view_path( - self.app_label, 'topology', 'organization' + self.app_label, "topology", "organization" ), - visible=[data['org1'].name], - hidden=[data['org2'].name, data['inactive']], + visible=[data["org1"].name], + hidden=[data["org2"].name, data["inactive"]], administrator=True, ) def test_node_queryset(self): data = self._create_multitenancy_test_env() self._test_multitenant_admin( - url=reverse(f'admin:{self.app_label}_node_changelist'), - visible=[data['n11'].label, data['n12'].label, data['org1'].name], + url=reverse(f"admin:{self.app_label}_node_changelist"), + visible=[data["n11"].label, data["n12"].label, data["org1"].name], hidden=[ - data['n21'].label, - data['n22'].label, - data['org2'].name, - data['n31'].label, - data['n32'].label, - data['inactive'], + data["n21"].label, + data["n22"].label, + data["org2"].name, + data["n31"].label, + data["n32"].label, + data["inactive"], ], ) def test_node_organization_fk_queryset(self): self._create_multitenancy_test_env() - self._login(username='operator', password='tester') - response = self.client.get(reverse(f'admin:{self.app_label}_node_add')) - self.assertNotContains(response, 'organization_id') + self._login(username="operator", password="tester") + response = self.client.get(reverse(f"admin:{self.app_label}_node_add")) + self.assertNotContains(response, "organization_id") def test_link_queryset(self): data = self._create_multitenancy_test_env() self._test_multitenant_admin( - url=reverse(f'admin:{self.app_label}_link_changelist'), - visible=[str(data['l1']), data['org1'].name], - hidden=[str(data['l2']), data['org2'].name, str(data['l3_inactive'])], + url=reverse(f"admin:{self.app_label}_link_changelist"), + visible=[str(data["l1"]), data["org1"].name], + hidden=[str(data["l2"]), data["org2"].name, str(data["l3_inactive"])], ) def test_link_organization_fk_queryset(self): self._create_multitenancy_test_env() - self._login(username='operator', password='tester') - response = self.client.get(reverse(f'admin:{self.app_label}_link_add')) - self.assertNotContains(response, 'organization_id') + self._login(username="operator", password="tester") + response = self.client.get(reverse(f"admin:{self.app_label}_link_add")) + self.assertNotContains(response, "organization_id") def test_node_topology_fk_autocomplete_view(self): data = self._create_multitenancy_test_env() self._test_multitenant_admin( url=self._get_autocomplete_view_path( - self.app_label, 'node', 'topology', path='admin:autocomplete' + self.app_label, "node", "topology", path="admin:autocomplete" ), - visible=[data['t1'].label], - hidden=[data['t2'].label, data['t3_inactive'].label], + visible=[data["t1"].label], + hidden=[data["t2"].label, data["t3_inactive"].label], ) def test_link_topology_fk_autocomplete_view(self): data = self._create_multitenancy_test_env() self._test_multitenant_admin( url=self._get_autocomplete_view_path( - self.app_label, 'link', 'topology', path='admin:autocomplete' + self.app_label, "link", "topology", path="admin:autocomplete" ), - visible=[data['t1'].label], - hidden=[data['t2'].label, data['t3_inactive'].label], + visible=[data["t1"].label], + hidden=[data["t2"].label, data["t3_inactive"].label], ) def test_node_topology_autocomplete_filter(self): data = self._create_multitenancy_test_env() - t_special = self._create_topology(label='special', organization=data['org1']) + t_special = self._create_topology(label="special", organization=data["org1"]) self._test_multitenant_admin( - url=self._get_autocomplete_view_path(self.app_label, 'node', 'topology'), - visible=[data['t1'].label, t_special.label], - hidden=[data['t2'].label, data['t3_inactive'].label], + url=self._get_autocomplete_view_path(self.app_label, "node", "topology"), + visible=[data["t1"].label, t_special.label], + hidden=[data["t2"].label, data["t3_inactive"].label], ) def test_link_topology_autocomplete_filter(self): data = self._create_multitenancy_test_env() - t_special = self._create_topology(label='special', organization=data['org1']) + t_special = self._create_topology(label="special", organization=data["org1"]) self._test_multitenant_admin( - url=self._get_autocomplete_view_path(self.app_label, 'link', 'topology'), - visible=[data['t1'].label, t_special.label], - hidden=[data['t2'].label, data['t3_inactive'].label], + url=self._get_autocomplete_view_path(self.app_label, "link", "topology"), + visible=[data["t1"].label, t_special.label], + hidden=[data["t2"].label, data["t3_inactive"].label], ) - @patch.object(Topology, 'update') + @patch.object(Topology, "update") def test_update_selected_action_perms(self, *args): org = self._get_org() user = self._create_user(is_staff=True) self._create_org_user(user=user, organization=org, is_admin=True) topology = self._create_topology(organization=org) self._test_action_permission( - path=reverse(f'admin:{self.app_label}_topology_changelist'), - action='update_selected', + path=reverse(f"admin:{self.app_label}_topology_changelist"), + action="update_selected", user=user, obj=topology, - message='1 topology was successfully updated', - required_perms=['change'], + message="1 topology was successfully updated", + required_perms=["change"], ) def test_publish_selected_action_perms(self): @@ -462,12 +462,12 @@ def test_publish_selected_action_perms(self): self._create_org_user(user=user, organization=org, is_admin=True) topology = self._create_topology(organization=org) self._test_action_permission( - path=reverse(f'admin:{self.app_label}_topology_changelist'), - action='publish_selected', + path=reverse(f"admin:{self.app_label}_topology_changelist"), + action="publish_selected", user=user, obj=topology, - message='1 topology was successfully published', - required_perms=['change'], + message="1 topology was successfully published", + required_perms=["change"], ) def test_unpublish_selected_action_perms(self): @@ -476,10 +476,10 @@ def test_unpublish_selected_action_perms(self): self._create_org_user(user=user, organization=org, is_admin=True) topology = self._create_topology(organization=org) self._test_action_permission( - path=reverse(f'admin:{self.app_label}_topology_changelist'), - action='unpublish_selected', + path=reverse(f"admin:{self.app_label}_topology_changelist"), + action="unpublish_selected", user=user, obj=topology, - message='1 topology was successfully unpublished', - required_perms=['change'], + message="1 topology was successfully unpublished", + required_perms=["change"], ) diff --git a/openwisp_network_topology/tests/test_api.py b/openwisp_network_topology/tests/test_api.py index abd43ef0..93a25a51 100644 --- a/openwisp_network_topology/tests/test_api.py +++ b/openwisp_network_topology/tests/test_api.py @@ -13,13 +13,13 @@ from .utils import CreateGraphObjectsMixin, LoadMixin, UnpublishMixin -Link = swapper.load_model('topology', 'Link') -Node = swapper.load_model('topology', 'Node') -Snapshot = swapper.load_model('topology', 'Snapshot') -Topology = swapper.load_model('topology', 'Topology') -Organization = swapper.load_model('openwisp_users', 'Organization') -OrganizationUser = swapper.load_model('openwisp_users', 'OrganizationUser') -Group = swapper.load_model('openwisp_users', 'Group') +Link = swapper.load_model("topology", "Link") +Node = swapper.load_model("topology", "Node") +Snapshot = swapper.load_model("topology", "Snapshot") +Topology = swapper.load_model("topology", "Topology") +Organization = swapper.load_model("openwisp_users", "Organization") +OrganizationUser = swapper.load_model("openwisp_users", "OrganizationUser") +Group = swapper.load_model("openwisp_users", "Group") class TestApi( @@ -30,7 +30,7 @@ class TestApi( TestOrganizationMixin, TestCase, ): - list_url = reverse('network_collection') + list_url = reverse("network_collection") topology_model = Topology node_model = Node link_model = Link @@ -39,9 +39,9 @@ class TestApi( def setUp(self): org = self._get_org() self.topology = self._create_topology(organization=org) - user = self._create_user(username='tester', email='tester@email.com') - user.groups.set(Group.objects.filter(name='Operator')) - perm = Permission.objects.filter(codename__endswith='topology') + user = self._create_user(username="tester", email="tester@email.com") + user.groups.set(Group.objects.filter(name="Operator")) + perm = Permission.objects.filter(codename__endswith="topology") user.user_permissions.add(*perm) self._create_org_user(user=user, organization=org, is_admin=True) self.node1 = self._create_node( @@ -64,25 +64,25 @@ def setUp(self): @property def detail_url(self): t = self.topology_model.objects.first() - return reverse('network_graph', args=[t.pk]) + return reverse("network_graph", args=[t.pk]) @property def receive_url(self): t = self.topology_model.objects.first() - path = reverse('receive_topology', args=[t.pk]) - return f'{path}?key=test' + path = reverse("receive_topology", args=[t.pk]) + return f"{path}?key=test" @property def snapshot_url(self): t = self.topology_model.objects.first() - path = reverse('network_graph_history', args=[t.pk]) - return f'{path}?date={self.snapshot_date}' + path = reverse("network_graph_history", args=[t.pk]) + return f"{path}?date={self.snapshot_date}" def _set_receive(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' - t.strategy = 'receive' - t.key = 'test' + t.parser = "netdiff.NetJsonParser" + t.strategy = "receive" + t.key = "test" t.expiration_time = 0 t.save() @@ -93,201 +93,201 @@ def snapshot_date(self): def test_list(self): response = self.client.get(self.list_url) - self.assertEqual(response.data['type'], 'NetworkCollection') - self.assertEqual(len(response.data['collection']), 1) + self.assertEqual(response.data["type"], "NetworkCollection") + self.assertEqual(len(response.data["collection"]), 1) def test_list_topology_filter(self): admin = self._create_admin() self.client.force_login(admin) - org2 = self._create_org(name='org2') + org2 = self._create_org(name="org2") self._create_topology( - organization=org2, parser='netdiff.OpenvpnParser', strategy='receive' + organization=org2, parser="netdiff.OpenvpnParser", strategy="receive" ) - with self.subTest('Test without filter'): + with self.subTest("Test without filter"): response = self.client.get(self.list_url) - self.assertEqual(len(response.data['collection']), 2) + self.assertEqual(len(response.data["collection"]), 2) - with self.subTest('Test filter with parser'): - response = self.client.get(f'{self.list_url}?parser=netdiff.OpenvpnParser') - self.assertEqual(len(response.data['collection']), 1) + with self.subTest("Test filter with parser"): + response = self.client.get(f"{self.list_url}?parser=netdiff.OpenvpnParser") + self.assertEqual(len(response.data["collection"]), 1) - with self.subTest('Test filter with organization'): - response = self.client.get(f'{self.list_url}?organization={org2.pk}') - self.assertEqual(len(response.data['collection']), 1) + with self.subTest("Test filter with organization"): + response = self.client.get(f"{self.list_url}?organization={org2.pk}") + self.assertEqual(len(response.data["collection"]), 1) - with self.subTest('Test filter with organization slug'): - response = self.client.get(f'{self.list_url}?organization_slug={org2.slug}') - self.assertEqual(len(response.data['collection']), 1) + with self.subTest("Test filter with organization slug"): + response = self.client.get(f"{self.list_url}?organization_slug={org2.slug}") + self.assertEqual(len(response.data["collection"]), 1) - with self.subTest('Test filter with strategy'): - response = self.client.get(f'{self.list_url}?strategy=receive') - self.assertEqual(len(response.data['collection']), 1) + with self.subTest("Test filter with strategy"): + response = self.client.get(f"{self.list_url}?strategy=receive") + self.assertEqual(len(response.data["collection"]), 1) def test_detail(self): response = self.client.get(self.detail_url) - self.assertEqual(response.data['type'], 'NetworkGraph') + self.assertEqual(response.data["type"], "NetworkGraph") def test_list_include_unpublished(self): self._unpublish() - path = f'{self.list_url}?include_unpublished=true' + path = f"{self.list_url}?include_unpublished=true" response = self.client.get(path) - self.assertEqual(len(response.data['collection']), 1) + self.assertEqual(len(response.data["collection"]), 1) - path = f'{self.list_url}?published=true' + path = f"{self.list_url}?published=true" response = self.client.get(path) - self.assertEqual(len(response.data['collection']), 0) + self.assertEqual(len(response.data["collection"]), 0) def test_detail_unpublished_topology(self): self._unpublish() - with self.subTest('Test view returns 404 for unpublished topology'): + with self.subTest("Test view returns 404 for unpublished topology"): response = self.client.get(self.detail_url) self.assertEqual(response.status_code, 404) with self.subTest('Test "include_unpublished" filter'): - path = f'{self.detail_url}?include_unpublished=true' + path = f"{self.detail_url}?include_unpublished=true" response = self.client.get(path) self.assertEqual(response.status_code, 200) def test_receive(self): self._set_receive() self.node_model.objects.all().delete() - data = self._load('static/netjson-1-link.json') - response = self.client.post(self.receive_url, data, content_type='text/plain') + data = self._load("static/netjson-1-link.json") + response = self.client.post(self.receive_url, data, content_type="text/plain") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['detail'], 'data received successfully') + self.assertEqual(response.data["detail"], "data received successfully") self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) - @patch('openwisp_network_topology.tasks.handle_update_topology.delay') + @patch("openwisp_network_topology.tasks.handle_update_topology.delay") def test_background_topology_update_task(self, mocked_task): self._set_receive() self.node_model.objects.all().delete() topology = self.topology_model.objects.first() - data = self._load('static/netjson-1-link.json') - response = self.client.post(self.receive_url, data, content_type='text/plain') + data = self._load("static/netjson-1-link.json") + response = self.client.post(self.receive_url, data, content_type="text/plain") mocked_task.assert_called_once_with(topology.pk, topology.diff(data)) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['detail'], 'data received successfully') + self.assertEqual(response.data["detail"], "data received successfully") - @patch('openwisp_network_topology.tasks.logger.warning') + @patch("openwisp_network_topology.tasks.logger.warning") def test_background_topology_update_task_warning(self, mock_warn): invalid_topology_pk = str(uuid4()) - data = {'test_key': 'test_value'} + data = {"test_key": "test_value"} handle_update_topology(invalid_topology_pk, data) mock_warn.assert_called_once_with( f'handle_update_topology("{invalid_topology_pk}") failed: Topology matching query does not exist.' ) - @patch('openwisp_network_topology.tasks.handle_update_topology.delay') + @patch("openwisp_network_topology.tasks.handle_update_topology.delay") def test_handle_update_topology_400_unrecognized_format(self, mocked_task): self._set_receive() self.node_model.objects.all().delete() - data = 'WRONG' - response = self.client.post(self.receive_url, data, content_type='text/plain') + data = "WRONG" + response = self.client.post(self.receive_url, data, content_type="text/plain") mocked_task.assert_not_called() self.assertEqual(response.status_code, 400) - self.assertIn('not recognized', response.data['detail']) + self.assertIn("not recognized", response.data["detail"]) def test_receive_404(self): # topology is set to FETCH strategy - response = self.client.post(self.receive_url, content_type='text/plain') + response = self.client.post(self.receive_url, content_type="text/plain") self.assertEqual(response.status_code, 404) def test_receive_415(self): self._set_receive() - data = self._load('static/netjson-1-link.json') + data = self._load("static/netjson-1-link.json") response = self.client.post( - self.receive_url, data, content_type='application/xml' + self.receive_url, data, content_type="application/xml" ) self.assertEqual(response.status_code, 415) def test_receive_400_missing_key(self): self._set_receive() - data = self._load('static/netjson-1-link.json') + data = self._load("static/netjson-1-link.json") response = self.client.post( - self.receive_url.replace('?key=test', ''), data, content_type='text/plain' + self.receive_url.replace("?key=test", ""), data, content_type="text/plain" ) self.assertEqual(response.status_code, 400) - self.assertIn('missing required', response.data['detail']) + self.assertIn("missing required", response.data["detail"]) def test_receive_400_unrecognized_format(self): self._set_receive() self.node_model.objects.all().delete() - data = 'WRONG' - response = self.client.post(self.receive_url, data, content_type='text/plain') + data = "WRONG" + response = self.client.post(self.receive_url, data, content_type="text/plain") self.assertEqual(response.status_code, 400) - self.assertIn('not recognized', response.data['detail']) + self.assertIn("not recognized", response.data["detail"]) def test_receive_403(self): self._set_receive() - data = self._load('static/netjson-1-link.json') + data = self._load("static/netjson-1-link.json") response = self.client.post( - self.receive_url.replace('?key=test', '?key=wrong'), + self.receive_url.replace("?key=test", "?key=wrong"), data, - content_type='text/plain', + content_type="text/plain", ) self.assertEqual(response.status_code, 403) def test_receive_options(self): self._set_receive() response = self.client.options(self.receive_url) - self.assertEqual(response.data['parses'], ['text/plain']) + self.assertEqual(response.data["parses"], ["text/plain"]) def test_snapshot(self): response = self.client.get(self.snapshot_url) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['type'], 'NetworkGraph') + self.assertEqual(response.data["type"], "NetworkGraph") def test_snapshot_missing_date_400(self): date = self.snapshot_date response = self.client.get( - self.snapshot_url.replace('?date={0}'.format(date), '') + self.snapshot_url.replace("?date={0}".format(date), "") ) self.assertEqual(response.status_code, 400) - self.assertEqual(response.data['detail'], 'missing required "date" parameter') + self.assertEqual(response.data["detail"], 'missing required "date" parameter') def test_snapshot_invalid_date_403(self): date = self.snapshot_date - url = self.snapshot_url.replace('?date={0}'.format(date), '?date=wrong-date') + url = self.snapshot_url.replace("?date={0}".format(date), "?date=wrong-date") response = self.client.get(url) self.assertEqual(response.status_code, 403) - self.assertEqual(response.data['detail'], 'invalid date supplied') + self.assertEqual(response.data["detail"], "invalid date supplied") def test_snapshot_no_snapshot_404(self): date = self.snapshot_date - url = self.snapshot_url.replace('?date={0}'.format(date), '?date=2001-01-01') + url = self.snapshot_url.replace("?date={0}".format(date), "?date=2001-01-01") response = self.client.get(url) self.assertEqual(response.status_code, 404) - self.assertIn('no snapshot found', response.data['detail']) + self.assertIn("no snapshot found", response.data["detail"]) def _test_api_with_unauthenticated_user(self, url): self.client.logout() r = self.client.get(url) self.assertEqual(r.status_code, 403) self.assertEqual( - r.data['detail'], 'Authentication credentials were not provided.' + r.data["detail"], "Authentication credentials were not provided." ) self.assertEqual(len(r.data), 1) def _test_api_with_not_a_manager_user(self, user, url, has_detail=True): OrganizationUser.objects.filter(user=user).delete() - perm = Permission.objects.filter(codename__endswith='topology') + perm = Permission.objects.filter(codename__endswith="topology") user.user_permissions.add(*perm) self.client.force_login(user) r = self.client.get(url) if not has_detail: self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['type'], 'NetworkCollection') - self.assertEqual(len(r.data['collection']), 0) + self.assertEqual(r.data["type"], "NetworkCollection") + self.assertEqual(len(r.data["collection"]), 0) self.assertNotEqual(Topology.objects.all().count(), 0) else: detail = ( - 'User is not a manager of the organization to ' - 'which the requested resource belongs.' + "User is not a manager of the organization to " + "which the requested resource belongs." ) self.assertEqual(r.status_code, 403) - self.assertEqual(r.data['detail'], detail) + self.assertEqual(r.data["detail"], detail) self.assertEqual(len(r.data), 1) def _test_api_with_not_permitted_user(self, user, url): @@ -297,23 +297,23 @@ def _test_api_with_not_permitted_user(self, user, url): r = self.client.get(url) self.assertEqual(r.status_code, 403) self.assertEqual( - r.data['detail'], 'You do not have permission to perform this action.' + r.data["detail"], "You do not have permission to perform this action." ) self.assertEqual(len(r.data), 1) def test_modelpermission_class_with_change_perm(self): t = self.topology_model.objects.first() - user = self._create_user(username='list-user', email='list@email.com') + user = self._create_user(username="list-user", email="list@email.com") self._create_org_user(user=user, organization=t.organization, is_admin=True) - change_perm = Permission.objects.filter(codename='change_topology') + change_perm = Permission.objects.filter(codename="change_topology") user.user_permissions.add(*change_perm) self.client.force_login(user) - with self.subTest('List url'): + with self.subTest("List url"): url = self.list_url with self.assertNumQueries(7): response = self.client.get(url) self.assertEqual(response.status_code, 200) - with self.subTest('Detail url'): + with self.subTest("Detail url"): url = self.detail_url with self.assertNumQueries(7): response = self.client.get(url) @@ -321,17 +321,17 @@ def test_modelpermission_class_with_change_perm(self): def test_modelpermission_class_with_view_perm(self): t = self.topology_model.objects.first() - user = self._create_user(username='list-user', email='list@email.com') + user = self._create_user(username="list-user", email="list@email.com") self._create_org_user(user=user, organization=t.organization, is_admin=True) - view_perm = Permission.objects.filter(codename='view_topology') + view_perm = Permission.objects.filter(codename="view_topology") user.user_permissions.add(*view_perm) self.client.force_login(user) - with self.subTest('List url'): + with self.subTest("List url"): url = self.list_url with self.assertNumQueries(7): response = self.client.get(url) self.assertEqual(response.status_code, 200) - with self.subTest('Detail url'): + with self.subTest("Detail url"): url = self.detail_url with self.assertNumQueries(7): response = self.client.get(url) @@ -339,394 +339,394 @@ def test_modelpermission_class_with_view_perm(self): def test_modelpermission_class_with_no_perm(self): t = self.topology_model.objects.first() - user = self._create_user(username='list-user', email='list@email.com') + user = self._create_user(username="list-user", email="list@email.com") self._create_org_user(user=user, organization=t.organization, is_admin=True) self.client.force_login(user) - with self.subTest('List url'): + with self.subTest("List url"): url = self.list_url with self.assertNumQueries(4): response = self.client.get(url) self.assertEqual(response.status_code, 403) - with self.subTest('Detail url'): + with self.subTest("Detail url"): url = self.detail_url with self.assertNumQueries(4): response = self.client.get(url) self.assertEqual(response.status_code, 403) def test_list_with_auth_enabled(self): - user = self._create_user(username='list-user', email='list@email.com') - with self.subTest('test api with unauthenticated user'): + user = self._create_user(username="list-user", email="list@email.com") + with self.subTest("test api with unauthenticated user"): self._test_api_with_unauthenticated_user(self.list_url) - with self.subTest('test api with not a permitted user'): + with self.subTest("test api with not a permitted user"): self._test_api_with_not_permitted_user(user, self.list_url) - with self.subTest('test api with not a member user'): + with self.subTest("test api with not a member user"): self._test_api_with_not_a_manager_user(user, self.list_url) def test_detail_with_auth_enabled(self): - user = self._create_user(username='detail-user', email='detail@email.com') - with self.subTest('test api with unauthenticated user'): + user = self._create_user(username="detail-user", email="detail@email.com") + with self.subTest("test api with unauthenticated user"): self._test_api_with_unauthenticated_user(self.detail_url) - with self.subTest('test api with not a permitted user'): + with self.subTest("test api with not a permitted user"): self._test_api_with_not_permitted_user(user, self.detail_url) - with self.subTest('test api with not a member user'): + with self.subTest("test api with not a member user"): self._test_api_with_not_a_manager_user(user, self.detail_url) def test_snapshot_with_auth_enabled(self): - user = self._create_user(username='snapshot-user', email='snapshot@email.com') - with self.subTest('test api with unauthenticated user'): + user = self._create_user(username="snapshot-user", email="snapshot@email.com") + with self.subTest("test api with unauthenticated user"): self._test_api_with_unauthenticated_user(self.snapshot_url) - with self.subTest('test api with not a permitted user'): + with self.subTest("test api with not a permitted user"): self._test_api_with_not_permitted_user(user, self.snapshot_url) - with self.subTest('test api with not a member user'): + with self.subTest("test api with not a member user"): self._test_api_with_not_a_manager_user(user, self.snapshot_url) def _successful_api_tests(self): - with self.subTest('test receive'): + with self.subTest("test receive"): self._set_receive() self.node_model.objects.all().delete() - data = self._load('static/netjson-1-link.json') + data = self._load("static/netjson-1-link.json") response = self.client.post( - self.receive_url, data, content_type='text/plain' + self.receive_url, data, content_type="text/plain" ) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['detail'], 'data received successfully') + self.assertEqual(response.data["detail"], "data received successfully") self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) - with self.subTest('test history'): + with self.subTest("test history"): response = self.client.get(self.snapshot_url) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['type'], 'NetworkGraph') + self.assertEqual(response.data["type"], "NetworkGraph") - @patch.object(APIView, 'get_permissions', return_value=[]) - @patch.object(APIView, 'get_authenticators', return_value=[]) + @patch.object(APIView, "get_permissions", return_value=[]) + @patch.object(APIView, "get_authenticators", return_value=[]) def test_api_with_auth_disabled(self, perm_mocked, auth_mocked): - user = self._get_user(username='tester') + user = self._get_user(username="tester") self.client.logout() self._successful_api_tests() self.client.force_login(user) def test_superuser_with_api_auth_enabled(self): - user = self._create_admin(username='superapi', email='superapi@email.com') + user = self._create_admin(username="superapi", email="superapi@email.com") self.client.force_login(user) self._successful_api_tests() - @patch.object(APIView, 'get_permissions', return_value=[]) - @patch.object(APIView, 'get_authenticators', return_value=[]) + @patch.object(APIView, "get_permissions", return_value=[]) + @patch.object(APIView, "get_authenticators", return_value=[]) def test_superuser_with_api_auth_disabled(self, perm_mocked, auth_mocked): - user = self._create_admin(username='superapi', email='superapi@email.com') + user = self._create_admin(username="superapi", email="superapi@email.com") self.client.force_login(user) self._successful_api_tests() def test_fetch_topology_create_api(self): - path = reverse('network_collection') + path = reverse("network_collection") data = { - 'label': 'test-fetch-topology', - 'organization': self._get_org().id, - 'parser': 'netdiff.OlsrParser', - 'strategy': 'fetch', - 'url': 'http://127.0.0.1:9090', - 'published': True, + "label": "test-fetch-topology", + "organization": self._get_org().id, + "parser": "netdiff.OlsrParser", + "strategy": "fetch", + "url": "http://127.0.0.1:9090", + "published": True, } with self.assertNumQueries(12): - response = self.client.post(path, data, content_type='application/json') + response = self.client.post(path, data, content_type="application/json") self.assertEqual(response.status_code, 201) - self.assertEqual(response.data['label'], 'test-fetch-topology') - self.assertEqual(response.data['parser'], 'netdiff.OlsrParser') + self.assertEqual(response.data["label"], "test-fetch-topology") + self.assertEqual(response.data["parser"], "netdiff.OlsrParser") def test_receive_topology_create_api(self): - path = reverse('network_collection') + path = reverse("network_collection") data = { - 'label': 'test-receive-topology', - 'organization': self._get_org().pk, - 'parser': 'netdiff.OlsrParser', - 'strategy': 'receive', - 'key': 'A3DJ62jhd49', - 'expiration_time': 360, - 'published': True, + "label": "test-receive-topology", + "organization": self._get_org().pk, + "parser": "netdiff.OlsrParser", + "strategy": "receive", + "key": "A3DJ62jhd49", + "expiration_time": 360, + "published": True, } with self.assertNumQueries(12): - response = self.client.post(path, data, content_type='application/json') + response = self.client.post(path, data, content_type="application/json") self.assertEqual(response.status_code, 201) - self.assertEqual(response.data['label'], 'test-receive-topology') - self.assertEqual(response.data['parser'], 'netdiff.OlsrParser') - self.assertEqual(response.data['key'], 'A3DJ62jhd49') + self.assertEqual(response.data["label"], "test-receive-topology") + self.assertEqual(response.data["parser"], "netdiff.OlsrParser") + self.assertEqual(response.data["key"], "A3DJ62jhd49") def test_topology_detail_receive_url_api(self): org1 = self._get_org() topo = self._create_topology(organization=org1) - path = reverse('network_graph', args=(topo.pk,)) - r_path = reverse('receive_topology', args=[topo.pk]) - receive_url = 'http://testserver{0}?key={1}'.format(r_path, topo.key) + path = reverse("network_graph", args=(topo.pk,)) + r_path = reverse("receive_topology", args=[topo.pk]) + receive_url = "http://testserver{0}?key={1}".format(r_path, topo.key) with self.assertNumQueries(7): response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['receive_url'], receive_url) + self.assertEqual(response.data["receive_url"], receive_url) def test_topology_receive_no_key_create_api(self): - path = reverse('network_collection') + path = reverse("network_collection") data = { - 'label': 'test-receive-topology', - 'organization': self._get_org().pk, - 'parser': 'netdiff.OlsrParser', - 'strategy': 'receive', - 'key': '', - 'expiration_time': 360, - 'published': True, + "label": "test-receive-topology", + "organization": self._get_org().pk, + "parser": "netdiff.OlsrParser", + "strategy": "receive", + "key": "", + "expiration_time": 360, + "published": True, } with self.assertNumQueries(7): - response = self.client.post(path, data, content_type='application/json') + response = self.client.post(path, data, content_type="application/json") self.assertEqual(response.status_code, 400) self.assertIn( - 'a key must be specified when using RECEIVE strategy', str(response.content) + "a key must be specified when using RECEIVE strategy", str(response.content) ) def test_get_topology_detail_api(self): org1 = self._get_org() topo = self._create_topology(organization=org1) - path = reverse('network_graph', args=(topo.pk,)) + path = reverse("network_graph", args=(topo.pk,)) with self.assertNumQueries(7): response = self.client.get(path) self.assertEqual(response.status_code, 200) def test_get_topology_detail_with_link_api(self): - path = reverse('network_graph', args=(self.topology.pk,)) + path = reverse("network_graph", args=(self.topology.pk,)) with self.assertNumQueries(7): response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertNotEqual(response.data['links'], []) + self.assertNotEqual(response.data["links"], []) def test_put_topology_detail_api(self): - path = reverse('network_graph', args=[self.topology.pk]) + path = reverse("network_graph", args=[self.topology.pk]) data = { - 'label': 'ChangeTestNetwork', - 'organization': self._get_org().pk, - 'parser': 'netdiff.OlsrParser', + "label": "ChangeTestNetwork", + "organization": self._get_org().pk, + "parser": "netdiff.OlsrParser", } with self.assertNumQueries(12): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.topology.refresh_from_db() - self.assertEqual(self.topology.label, 'ChangeTestNetwork') + self.assertEqual(self.topology.label, "ChangeTestNetwork") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['label'], 'ChangeTestNetwork') - self.assertEqual(response.data['type'], 'NetworkGraph') + self.assertEqual(response.data["label"], "ChangeTestNetwork") + self.assertEqual(response.data["type"], "NetworkGraph") def test_change_strategy_fetch_api_400(self): org1 = self._get_org() topo = self._create_topology(organization=org1) - path = reverse('network_graph', args=(topo.pk,)) + path = reverse("network_graph", args=(topo.pk,)) data = { - 'label': 'ChangeTestNetwork', - 'organization': org1.pk, - 'parser': 'netdiff.OlsrParser', - 'strategy': 'fetch', - 'url': '', + "label": "ChangeTestNetwork", + "organization": org1.pk, + "parser": "netdiff.OlsrParser", + "strategy": "fetch", + "url": "", } with self.assertNumQueries(6): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 400) self.assertIn( - 'An url must be specified when using FETCH strategy', str(response.content) + "An url must be specified when using FETCH strategy", str(response.content) ) def test_change_strategy_receive_api_400(self): org1 = self._get_org() topo = self._create_topology(organization=org1) - path = reverse('network_graph', args=(topo.pk,)) + path = reverse("network_graph", args=(topo.pk,)) data = { - 'label': 'ChangeTestNetwork', - 'organization': self._get_org().pk, - 'parser': 'netdiff.OlsrParser', - 'strategy': 'receive', - 'key': '', + "label": "ChangeTestNetwork", + "organization": self._get_org().pk, + "parser": "netdiff.OlsrParser", + "strategy": "receive", + "key": "", } with self.assertNumQueries(6): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 400) self.assertIn( - 'A key must be specified when using RECEIVE strategy', str(response.content) + "A key must be specified when using RECEIVE strategy", str(response.content) ) def test_change_strategy_fetch_api_200(self): org1 = self._get_org() - topo = self._create_topology(organization=org1, strategy='receive') - path = reverse('network_graph', args=[topo.pk]) - self.assertEqual(topo.strategy, 'receive') + topo = self._create_topology(organization=org1, strategy="receive") + path = reverse("network_graph", args=[topo.pk]) + self.assertEqual(topo.strategy, "receive") data = { - 'label': 'ChangeTestNetwork', - 'organization': org1.pk, - 'parser': 'netdiff.OlsrParser', - 'strategy': 'fetch', - 'url': 'http://127.0.0.1:9090', + "label": "ChangeTestNetwork", + "organization": org1.pk, + "parser": "netdiff.OlsrParser", + "strategy": "fetch", + "url": "http://127.0.0.1:9090", } with self.assertNumQueries(12): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['strategy'], 'fetch') + self.assertEqual(response.data["strategy"], "fetch") topo.refresh_from_db() - self.assertEqual(topo.strategy, 'fetch') + self.assertEqual(topo.strategy, "fetch") def test_change_strategy_receive_api_200(self): org1 = self._get_org() - topo = self._create_topology(organization=org1, strategy='fetch') - path = reverse('network_graph', args=[topo.pk]) - self.assertEqual(topo.strategy, 'fetch') + topo = self._create_topology(organization=org1, strategy="fetch") + path = reverse("network_graph", args=[topo.pk]) + self.assertEqual(topo.strategy, "fetch") data = { - 'label': 'ChangeTestNetwork', - 'organization': org1.pk, - 'parser': 'netdiff.OlsrParser', - 'strategy': 'receive', - 'key': 12345, + "label": "ChangeTestNetwork", + "organization": org1.pk, + "parser": "netdiff.OlsrParser", + "strategy": "receive", + "key": 12345, } with self.assertNumQueries(12): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['strategy'], 'receive') + self.assertEqual(response.data["strategy"], "receive") topo.refresh_from_db() - self.assertEqual(topo.strategy, 'receive') + self.assertEqual(topo.strategy, "receive") def test_patch_topology_detail_api(self): - path = reverse('network_graph', args=(self.topology.pk,)) + path = reverse("network_graph", args=(self.topology.pk,)) data = { - 'label': 'ChangeTestNetwork', + "label": "ChangeTestNetwork", } with self.assertNumQueries(11): - response = self.client.patch(path, data, content_type='application/json') + response = self.client.patch(path, data, content_type="application/json") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['label'], 'ChangeTestNetwork') + self.assertEqual(response.data["label"], "ChangeTestNetwork") def test_delete_topology_api(self): - path = reverse('network_graph', args=(self.topology.pk,)) + path = reverse("network_graph", args=(self.topology.pk,)) response = self.client.delete(path) self.assertEqual(response.status_code, 204) def test_topology_filter_by_org_api(self): - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') - topo1 = self._create_topology(label='topo1', organization=org1) - topo2 = self._create_topology(label='topo2', organization=org2) - user1 = self._create_user(username='test-filter', email='test@filter.com') + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") + topo1 = self._create_topology(label="topo1", organization=org1) + topo2 = self._create_topology(label="topo2", organization=org2) + user1 = self._create_user(username="test-filter", email="test@filter.com") self._create_org_user(user=user1, organization=org1, is_admin=True) - view_perm = Permission.objects.filter(codename='view_topology') + view_perm = Permission.objects.filter(codename="view_topology") user1.user_permissions.add(*view_perm) self.client.force_login(user1) - with self.subTest('test network collection view'): - path = reverse('network_collection') + with self.subTest("test network collection view"): + path = reverse("network_collection") with self.assertNumQueries(7): response = self.client.get(path) self.assertEqual(response.status_code, 200) self.assertIn(str(topo1.id), str(response.content)) self.assertNotIn(str(topo2.id), str(response.content)) - with self.subTest('test network graph view'): + with self.subTest("test network graph view"): # Get the topology graph view of member org 200 - path = reverse('network_graph', args=(topo1.pk,)) + path = reverse("network_graph", args=(topo1.pk,)) with self.assertNumQueries(7): response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['id'], str(topo1.id)) + self.assertEqual(response.data["id"], str(topo1.id)) # Get the topology graph view of different org 404 - path = reverse('network_graph', args=(topo2.pk,)) + path = reverse("network_graph", args=(topo2.pk,)) with self.assertNumQueries(5): response = self.client.get(path) self.assertEqual(response.status_code, 404) def test_topology_filter_fields_by_org_api(self): org1 = self._get_org() - user1 = self._create_user(username='test-filter', email='test@filter.com') + user1 = self._create_user(username="test-filter", email="test@filter.com") self._create_org_user(user=user1, organization=org1, is_admin=True) - topo_perm = Permission.objects.filter(codename__endswith='topology') + topo_perm = Permission.objects.filter(codename__endswith="topology") user1.user_permissions.add(*topo_perm) self.client.force_login(user1) - with self.subTest('test network collection view'): - path = reverse('network_collection') + with self.subTest("test network collection view"): + path = reverse("network_collection") with self.assertNumQueries(8): - response = self.client.get(path, {'format': 'api'}) + response = self.client.get(path, {"format": "api"}) self.assertEqual(response.status_code, 200) self.assertEqual(Organization.objects.count(), 2) - self.assertContains(response, 'test org') - self.assertNotContains(response, 'default') + self.assertContains(response, "test org") + self.assertNotContains(response, "default") - with self.subTest('test network graph view'): - topo1 = self._create_topology(label='topo1', organization=org1) - path = reverse('network_graph', args=(topo1.pk,)) + with self.subTest("test network graph view"): + topo1 = self._create_topology(label="topo1", organization=org1) + path = reverse("network_graph", args=(topo1.pk,)) with self.assertNumQueries(14): - response = self.client.get(path, {'format': 'api'}) + response = self.client.get(path, {"format": "api"}) self.assertEqual(response.status_code, 200) self.assertEqual(Organization.objects.count(), 2) - self.assertContains(response, 'test org') - self.assertNotContains(response, 'default') + self.assertContains(response, "test org") + self.assertNotContains(response, "default") def test_node_list_api(self): - path = reverse('node_list') + path = reverse("node_list") with self.assertNumQueries(6): response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['count'], 2) + self.assertEqual(response.data["count"], 2) def test_node_list_multitenancy(self): - path = reverse('node_list') - org2 = self._create_org(name='org2') + path = reverse("node_list") + org2 = self._create_org(name="org2") t2 = self._create_topology(organization=org2) self._create_node( - label='node2', addresses=['192.168.0.2'], topology=t2, organization=org2 + label="node2", addresses=["192.168.0.2"], topology=t2, organization=org2 ) self.assertEqual(Node.objects.count(), 3) with self.assertNumQueries(6): response = self.client.get(path) self.assertEqual(response.status_code, 200) # Only nodes related to user's organization are returned - self.assertEqual(response.data['count'], 2) + self.assertEqual(response.data["count"], 2) def test_node_list_filter(self): admin = self._get_admin() self.client.force_login(admin) org1 = self._get_org() - org2 = self._create_org(name='org2') + org2 = self._create_org(name="org2") t2 = self._create_topology(organization=org2) self._create_node( - label='node2', addresses=['192.168.0.2'], topology=t2, organization=org2 + label="node2", addresses=["192.168.0.2"], topology=t2, organization=org2 ) - path = reverse('node_list') + path = reverse("node_list") - with self.subTest('Test list without filters'): + with self.subTest("Test list without filters"): response = self.client.get(path) - self.assertEqual(response.data['count'], 3) + self.assertEqual(response.data["count"], 3) - with self.subTest('Test filter by organization'): - response = self.client.get(f'{path}?organization={org1.pk}') - self.assertEqual(response.data['count'], 2) + with self.subTest("Test filter by organization"): + response = self.client.get(f"{path}?organization={org1.pk}") + self.assertEqual(response.data["count"], 2) - with self.subTest('Test filter by organization slug'): - response = self.client.get(f'{path}?organization_slug={org1.slug}') - self.assertEqual(response.data['count'], 2) + with self.subTest("Test filter by organization slug"): + response = self.client.get(f"{path}?organization_slug={org1.slug}") + self.assertEqual(response.data["count"], 2) - with self.subTest('Test filter by topology'): - response = self.client.get(f'{path}?topology={t2.pk}') - self.assertEqual(response.data['count'], 1) + with self.subTest("Test filter by topology"): + response = self.client.get(f"{path}?topology={t2.pk}") + self.assertEqual(response.data["count"], 1) def test_node_create_api(self): - path = reverse('node_list') + path = reverse("node_list") data = { - 'topology': self.topology.pk, - 'label': 'test-node', - 'addresses': ['192.168.0.1'], - 'properties': {}, - 'user_properties': {}, + "topology": self.topology.pk, + "label": "test-node", + "addresses": ["192.168.0.1"], + "properties": {}, + "user_properties": {}, } with self.assertNumQueries(13): - response = self.client.post(path, data, content_type='application/json') + response = self.client.post(path, data, content_type="application/json") self.assertEqual(response.status_code, 201) - self.assertEqual(response.data['topology'], self.topology.pk) - self.assertEqual(response.data['label'], 'test-node') - self.assertEqual(response.data['addresses'], ['192.168.0.1']) + self.assertEqual(response.data["topology"], self.topology.pk) + self.assertEqual(response.data["label"], "test-node") + self.assertEqual(response.data["addresses"], ["192.168.0.1"]) def test_node_create_api_shared_topology(self): admin = self._get_admin() @@ -734,197 +734,197 @@ def test_node_create_api_shared_topology(self): topology = self._create_topology(organization=None) org = self._get_org() data = { - 'topology': topology.pk, - 'label': 'test-node', - 'addresses': ['192.168.0.1'], - 'properties': {}, - 'user_properties': {}, - 'organization': org.pk, + "topology": topology.pk, + "label": "test-node", + "addresses": ["192.168.0.1"], + "properties": {}, + "user_properties": {}, + "organization": org.pk, } - path = reverse('node_list') + path = reverse("node_list") with self.assertNumQueries(11): - response = self.client.post(path, data, content_type='application/json') + response = self.client.post(path, data, content_type="application/json") self.assertEqual(response.status_code, 201) - self.assertEqual(response.data['topology'], topology.pk) - self.assertEqual(response.data['organization'], org.pk) + self.assertEqual(response.data["topology"], topology.pk) + self.assertEqual(response.data["organization"], org.pk) def test_node_detail_api(self): - path = reverse('node_detail', args=(self.node1.pk,)) + path = reverse("node_detail", args=(self.node1.pk,)) with self.assertNumQueries(6): response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['id'], str(self.node1.pk)) - self.assertEqual(response.data['label'], self.node1.label) - self.assertEqual(response.data['topology'], self.node1.topology.pk) - self.assertEqual(response.data['organization'], self.node1.organization.pk) - self.assertEqual(response.data['addresses'], self.node1.addresses) - self.assertEqual(response.data['properties'], self.node1.properties) - self.assertEqual(response.data['user_properties'], self.node1.user_properties) - self.assertIn('created', response.data) - self.assertIn('modified', response.data) + self.assertEqual(response.data["id"], str(self.node1.pk)) + self.assertEqual(response.data["label"], self.node1.label) + self.assertEqual(response.data["topology"], self.node1.topology.pk) + self.assertEqual(response.data["organization"], self.node1.organization.pk) + self.assertEqual(response.data["addresses"], self.node1.addresses) + self.assertEqual(response.data["properties"], self.node1.properties) + self.assertEqual(response.data["user_properties"], self.node1.user_properties) + self.assertIn("created", response.data) + self.assertIn("modified", response.data) def test_node_put_api(self): data = { - 'topology': self.topology.pk, - 'label': 'change-node', - 'addresses': ['192.168.0.1', '192.168.0.2'], - 'properties': {}, - 'user_properties': {}, + "topology": self.topology.pk, + "label": "change-node", + "addresses": ["192.168.0.1", "192.168.0.2"], + "properties": {}, + "user_properties": {}, } - path = reverse('node_detail', args=(self.node1.pk,)) + path = reverse("node_detail", args=(self.node1.pk,)) with self.assertNumQueries(13): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['label'], 'change-node') - self.assertEqual(response.data['addresses'], ['192.168.0.1', '192.168.0.2']) + self.assertEqual(response.data["label"], "change-node") + self.assertEqual(response.data["addresses"], ["192.168.0.1", "192.168.0.2"]) def test_node_patch_api(self): - path = reverse('node_detail', args=(self.node1.pk,)) - data = {'label': 'change-node'} + path = reverse("node_detail", args=(self.node1.pk,)) + data = {"label": "change-node"} with self.assertNumQueries(12): - response = self.client.patch(path, data, content_type='application/json') + response = self.client.patch(path, data, content_type="application/json") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['label'], 'change-node') + self.assertEqual(response.data["label"], "change-node") def test_node_delete_api(self): node = self._create_node( - label='delete-node', addresses=['192.168.0.1'], topology=self.topology + label="delete-node", addresses=["192.168.0.1"], topology=self.topology ) - path = reverse('node_detail', args=(node.pk,)) + path = reverse("node_detail", args=(node.pk,)) response = self.client.delete(path) self.assertEqual(response.status_code, 204) - self.assertEqual(Node.objects.filter(label='delete-node').count(), 0) + self.assertEqual(Node.objects.filter(label="delete-node").count(), 0) def test_link_list_api(self): - path = reverse('link_list') + path = reverse("link_list") with self.assertNumQueries(6): response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['count'], 1) + self.assertEqual(response.data["count"], 1) def test_link_list_filters(self): admin = self._get_admin() self.client.force_login(admin) org1 = self._get_org() - org2 = self._create_org(name='org2') + org2 = self._create_org(name="org2") t2 = self._create_topology(organization=org2) node1 = self._create_node( - label='node2', addresses=['192.168.0.2'], topology=t2, organization=org2 + label="node2", addresses=["192.168.0.2"], topology=t2, organization=org2 ) node2 = self._create_node( - label='node2', addresses=['192.168.0.3'], topology=t2, organization=org2 + label="node2", addresses=["192.168.0.3"], topology=t2, organization=org2 ) - self._create_link(source=node1, target=node2, topology=t2, status='down') - path = reverse('link_list') + self._create_link(source=node1, target=node2, topology=t2, status="down") + path = reverse("link_list") - with self.subTest('Test list without filters'): + with self.subTest("Test list without filters"): response = self.client.get(path) - self.assertEqual(response.data['count'], 2) + self.assertEqual(response.data["count"], 2) - with self.subTest('Test filter by organization'): - response = self.client.get(f'{path}?organization={org1.pk}') - self.assertEqual(response.data['count'], 1) + with self.subTest("Test filter by organization"): + response = self.client.get(f"{path}?organization={org1.pk}") + self.assertEqual(response.data["count"], 1) - with self.subTest('Test filter by organization slug'): - response = self.client.get(f'{path}?organization_slug={org1.slug}') - self.assertEqual(response.data['count'], 1) + with self.subTest("Test filter by organization slug"): + response = self.client.get(f"{path}?organization_slug={org1.slug}") + self.assertEqual(response.data["count"], 1) - with self.subTest('Test filter by topology'): - response = self.client.get(f'{path}?topology={t2.pk}') - self.assertEqual(response.data['count'], 1) + with self.subTest("Test filter by topology"): + response = self.client.get(f"{path}?topology={t2.pk}") + self.assertEqual(response.data["count"], 1) - with self.subTest('Test filter by status'): - response = self.client.get(f'{path}?status=down') - self.assertEqual(response.data['count'], 1) + with self.subTest("Test filter by status"): + response = self.client.get(f"{path}?status=down") + self.assertEqual(response.data["count"], 1) def test_link_create_api(self): - path = reverse('link_list') - node3 = self._create_node(label='node3', topology=self.topology) + path = reverse("link_list") + node3 = self._create_node(label="node3", topology=self.topology) data = { - 'topology': self.topology.pk, - 'source': self.node1.pk, - 'target': node3.pk, - 'cost': 1.0, - 'properties': {}, - 'user_properties': {}, + "topology": self.topology.pk, + "source": self.node1.pk, + "target": node3.pk, + "cost": 1.0, + "properties": {}, + "user_properties": {}, } with self.assertNumQueries(16): - response = self.client.post(path, data, content_type='application/json') + response = self.client.post(path, data, content_type="application/json") self.assertEqual(response.status_code, 201) - self.assertEqual(response.data['topology'], self.topology.pk) - self.assertEqual(response.data['status'], 'up') - self.assertEqual(response.data['source'], self.node1.pk) - self.assertEqual(response.data['target'], node3.pk) + self.assertEqual(response.data["topology"], self.topology.pk) + self.assertEqual(response.data["status"], "up") + self.assertEqual(response.data["source"], self.node1.pk) + self.assertEqual(response.data["target"], node3.pk) def test_link_create_with_wrong_value_format_api(self): - path = reverse('link_list') - node3 = self._create_node(label='node3', topology=self.topology) + path = reverse("link_list") + node3 = self._create_node(label="node3", topology=self.topology) data = { - 'topology': self.topology.pk, - 'source': self.node1.pk, - 'target': node3.pk, - 'cost': 1.0, - 'properties': 0, - 'user_properties': 122343, + "topology": self.topology.pk, + "source": self.node1.pk, + "target": node3.pk, + "cost": 1.0, + "properties": 0, + "user_properties": 122343, } with self.assertNumQueries(7): - response = self.client.post(path, data, content_type='application/json') + response = self.client.post(path, data, content_type="application/json") self.assertEqual(response.status_code, 400) self.assertEqual( - response.data['properties'][0].title(), - 'Value Must Be Valid Json Or Key, Valued Pair.', + response.data["properties"][0].title(), + "Value Must Be Valid Json Or Key, Valued Pair.", ) self.assertEqual( - response.data['user_properties'][0].title(), - 'Value Must Be Valid Json Or Key, Valued Pair.', + response.data["user_properties"][0].title(), + "Value Must Be Valid Json Or Key, Valued Pair.", ) def test_link_detail_api(self): - path = reverse('link_detail', args=(self.link.pk,)) + path = reverse("link_detail", args=(self.link.pk,)) with self.assertNumQueries(6): response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['id'], str(self.link.pk)) - self.assertEqual(response.data['topology'], self.topology.pk) - self.assertEqual(response.data['source'], self.node1.pk) - self.assertEqual(response.data['target'], self.node2.pk) - self.assertEqual(response.data['organization'], self.link.organization.pk) - self.assertEqual(response.data['status'], self.link.status) - self.assertEqual(response.data['cost'], self.link.cost) - self.assertEqual(response.data['cost_text'], self.link.cost_text) - self.assertEqual(response.data['properties'], self.link.properties) - self.assertEqual(response.data['user_properties'], self.link.user_properties) - self.assertIn('created', response.data) - self.assertIn('modified', response.data) + self.assertEqual(response.data["id"], str(self.link.pk)) + self.assertEqual(response.data["topology"], self.topology.pk) + self.assertEqual(response.data["source"], self.node1.pk) + self.assertEqual(response.data["target"], self.node2.pk) + self.assertEqual(response.data["organization"], self.link.organization.pk) + self.assertEqual(response.data["status"], self.link.status) + self.assertEqual(response.data["cost"], self.link.cost) + self.assertEqual(response.data["cost_text"], self.link.cost_text) + self.assertEqual(response.data["properties"], self.link.properties) + self.assertEqual(response.data["user_properties"], self.link.user_properties) + self.assertIn("created", response.data) + self.assertIn("modified", response.data) def test_link_put_api(self): - path = reverse('link_detail', args=(self.link.pk,)) + path = reverse("link_detail", args=(self.link.pk,)) data = { - 'topology': self.topology.pk, - 'source': self.node1.pk, - 'target': self.node2.pk, - 'cost': 21.0, - 'properties': {}, - 'user_properties': {'user': 'tester'}, + "topology": self.topology.pk, + "source": self.node1.pk, + "target": self.node2.pk, + "cost": 21.0, + "properties": {}, + "user_properties": {"user": "tester"}, } with self.assertNumQueries(16): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['cost'], 21.0) - self.assertEqual(response.data['properties'], {}) - self.assertEqual(response.data['user_properties'], {'user': 'tester'}) + self.assertEqual(response.data["cost"], 21.0) + self.assertEqual(response.data["properties"], {}) + self.assertEqual(response.data["user_properties"], {"user": "tester"}) def test_link_patch_api(self): - path = reverse('link_detail', args=(self.link.pk,)) - data = {'cost': 50.0} + path = reverse("link_detail", args=(self.link.pk,)) + data = {"cost": 50.0} with self.assertNumQueries(13): - response = self.client.patch(path, data, content_type='application/json') + response = self.client.patch(path, data, content_type="application/json") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['cost'], 50.0) + self.assertEqual(response.data["cost"], 50.0) def test_link_delete_api(self): self.assertEqual(Link.objects.count(), 1) - path = reverse('link_detail', args=(self.link.pk,)) + path = reverse("link_detail", args=(self.link.pk,)) response = self.client.delete(path) self.assertEqual(response.status_code, 204) self.assertEqual(Link.objects.count(), 0) diff --git a/openwisp_network_topology/tests/test_link.py b/openwisp_network_topology/tests/test_link.py index a18ac057..ceef3b37 100644 --- a/openwisp_network_topology/tests/test_link.py +++ b/openwisp_network_topology/tests/test_link.py @@ -8,9 +8,9 @@ from ..utils import link_status_changed from .utils import CreateGraphObjectsMixin -Link = swapper.load_model('topology', 'Link') -Node = swapper.load_model('topology', 'Node') -Topology = swapper.load_model('topology', 'Topology') +Link = swapper.load_model("topology", "Link") +Node = swapper.load_model("topology", "Node") +Topology = swapper.load_model("topology", "Topology") class TestLink(TestOrganizationMixin, CreateGraphObjectsMixin, TestCase): @@ -22,10 +22,10 @@ def setUp(self): org = self._create_org() t = self._create_topology(organization=org) self._create_node( - label='node1', addresses=['192.168.0.1'], topology=t, organization=org + label="node1", addresses=["192.168.0.1"], topology=t, organization=org ) self._create_node( - label='node2', addresses=['192.168.0.2'], topology=t, organization=org + label="node2", addresses=["192.168.0.2"], topology=t, organization=org ) def _get_nodes(self): @@ -48,7 +48,7 @@ def test_clean_properties_status(self): t = self.topology_model.objects.first() node1, node2 = self._get_nodes() link = t._create_link( - source=node1, target=node2, cost=1.0, properties={'status': 'up'} + source=node1, target=node2, cost=1.0, properties={"status": "up"} ) link.full_clean() # status must not be saved in properties or it will override the real status @@ -75,40 +75,40 @@ def test_json(self): source=node1, target=node2, cost=1.0, - cost_text='100mbit/s', + cost_text="100mbit/s", properties={"pretty": True}, ) self.assertEqual( dict(link.json(dict=True)), { - 'source': '192.168.0.1', - 'target': '192.168.0.2', - 'cost': 1.0, - 'cost_text': '100mbit/s', - 'properties': { - 'pretty': True, - 'status': 'up', - 'created': link.created, - 'modified': link.modified, - 'status_changed': link.status_changed, + "source": "192.168.0.1", + "target": "192.168.0.2", + "cost": 1.0, + "cost_text": "100mbit/s", + "properties": { + "pretty": True, + "status": "up", + "created": link.created, + "modified": link.modified, + "status_changed": link.status_changed, }, }, ) self.assertIsInstance(link.json(), str) - link.status = 'down' + link.status = "down" link.save() link.refresh_from_db() netjson = link.json(dict=True) - with self.subTest('status should not be saved in properties'): - self.assertNotIn('status', link.properties) - with self.subTest('status should not be overridden'): - self.assertEqual(netjson['properties']['status'], 'down') - with self.subTest('testing original=True'): + with self.subTest("status should not be saved in properties"): + self.assertNotIn("status", link.properties) + with self.subTest("status should not be overridden"): + self.assertEqual(netjson["properties"]["status"], "down") + with self.subTest("testing original=True"): netjson = link.json(dict=True, original=True) - self.assertNotIn('status', netjson['properties']) - self.assertNotIn('status_changed', netjson['properties']) - self.assertNotIn('created', netjson['properties']) - self.assertNotIn('modified', netjson['properties']) + self.assertNotIn("status", netjson["properties"]) + self.assertNotIn("status_changed", netjson["properties"]) + self.assertNotIn("created", netjson["properties"]) + self.assertNotIn("modified", netjson["properties"]) def test_get_from_nodes(self): t = self.topology_model.objects.first() @@ -117,14 +117,14 @@ def test_get_from_nodes(self): source=node1, target=node2, cost=1.0, - cost_text='100mbit/s', + cost_text="100mbit/s", properties='{"pretty": true}', ) link.full_clean() link.save() - link = self.link_model.get_from_nodes('192.168.0.1', '192.168.0.2', t) + link = self.link_model.get_from_nodes("192.168.0.1", "192.168.0.2", t) self.assertIsInstance(link, self.link_model) - link = self.link_model.get_from_nodes('wrong', 'wrong', t) + link = self.link_model.get_from_nodes("wrong", "wrong", t) self.assertIsNone(link) def test_status_change_signal_sent(self): @@ -132,77 +132,77 @@ def test_status_change_signal_sent(self): t = self.topology_model.objects.first() node1, node2 = self._get_nodes() with catch_signal(link_status_changed) as handler: - link = t._create_link(source=node1, target=node2, cost=1.0, status='up') + link = t._create_link(source=node1, target=node2, cost=1.0, status="up") link.save() handler.assert_not_called() with catch_signal(link_status_changed) as handler: - link.status = 'down' + link.status = "down" link.save() handler.assert_called_once_with( link=link, sender=self.link_model, signal=link_status_changed ) - self.assertEqual(link.status, 'down') + self.assertEqual(link.status, "down") def test_validate_organization(self): org1 = self._get_org() org1_topology = self.topology_model.objects.first() org1_node1, org1_node2 = self._get_nodes() - shared_topology = self._create_topology(label='shared', organization=None) - org2 = self._create_org(name='org2') + shared_topology = self._create_topology(label="shared", organization=None) + org2 = self._create_org(name="org2") org2_node1 = self._create_node( - label='org2_node1', - addresses=['192.168.0.3'], + label="org2_node1", + addresses=["192.168.0.3"], topology=shared_topology, organization=org2, ) - with self.subTest('Test toplogy and nodes belongs to different organizations'): + with self.subTest("Test toplogy and nodes belongs to different organizations"): # Test source node link = self.link_model( source=org2_node1, target=org2_node1, cost=1.0, - cost_text='100mbit/s', + cost_text="100mbit/s", topology=org1_topology, ) with self.assertRaises(ValidationError) as context_manager: link.full_clean() source_expected_error = ( - 'Source node and topology should have same organization.' + "Source node and topology should have same organization." ) target_expected_error = ( - 'Target node and topology should have same organization.' + "Target node and topology should have same organization." ) self.assertIn( - source_expected_error, context_manager.exception.message_dict['source'] + source_expected_error, context_manager.exception.message_dict["source"] ) self.assertIn( - target_expected_error, context_manager.exception.message_dict['target'] + target_expected_error, context_manager.exception.message_dict["target"] ) - with self.subTest('Test link gets organization of non-shared topology'): + with self.subTest("Test link gets organization of non-shared topology"): link = self.link_model( source=org1_node1, target=org1_node2, cost=1.0, - cost_text='100mbit/s', + cost_text="100mbit/s", topology=org1_topology, ) link.full_clean() self.assertEqual(link.organization, org1_topology.organization) - with self.subTest('Test non-shared link of shared topology'): + with self.subTest("Test non-shared link of shared topology"): # Since source and target nodes belong to the same organization, # link will get organization of the nodes source_node = self._create_node( - label='org1_node1_shared_topology', - addresses=['10.0.0.1'], + label="org1_node1_shared_topology", + addresses=["10.0.0.1"], topology=shared_topology, organization=org1, ) target_node = self._create_node( - label='org1_node2_shared_topology', - addresses=['10.0.0.2'], + label="org1_node2_shared_topology", + addresses=["10.0.0.2"], topology=shared_topology, organization=org1, ) @@ -210,24 +210,24 @@ def test_validate_organization(self): source=source_node, target=target_node, cost=1.0, - cost_text='100mbit/s', + cost_text="100mbit/s", topology=shared_topology, ) link.full_clean() self.assertEqual(link.organization, org1) - with self.subTest('Test shared link of shared topology'): + with self.subTest("Test shared link of shared topology"): # Since source and target nodes belongs to different organizations, # link will be shared. source_node = self._create_node( - label='org1_node1_shared_topology', - addresses=['10.0.0.3'], + label="org1_node1_shared_topology", + addresses=["10.0.0.3"], topology=shared_topology, organization=org1, ) target_node = self._create_node( - label='org2_node1_shared_topology', - addresses=['10.0.0.4'], + label="org2_node1_shared_topology", + addresses=["10.0.0.4"], topology=shared_topology, organization=org2, ) @@ -235,7 +235,7 @@ def test_validate_organization(self): source=source_node, target=target_node, cost=1.0, - cost_text='100mbit/s', + cost_text="100mbit/s", topology=shared_topology, ) link.full_clean() @@ -245,19 +245,19 @@ def test_user_properties_in_json(self): t = self.topology_model.objects.first() node1, node2 = self._get_nodes() link = self.link_model( - source=node1, target=node2, cost=1.0, cost_text='100mbit/s', topology=t + source=node1, target=node2, cost=1.0, cost_text="100mbit/s", topology=t ) - link.properties = {'wired': True} - link.user_properties = {'user_property': True} + link.properties = {"wired": True} + link.user_properties = {"user_property": True} link.full_clean() link.save() - with self.subTest('view json when original is False'): + with self.subTest("view json when original is False"): data = link.json(dict=True) - self.assertIn('wired', data['properties']) - self.assertIn('user_property', data['properties']) + self.assertIn("wired", data["properties"]) + self.assertIn("user_property", data["properties"]) - with self.subTest('view json when original is True'): + with self.subTest("view json when original is True"): data = link.json(dict=True, original=True) - self.assertIn('wired', data['properties']) - self.assertNotIn('user_property', data['properties']) + self.assertIn("wired", data["properties"]) + self.assertNotIn("user_property", data["properties"]) diff --git a/openwisp_network_topology/tests/test_node.py b/openwisp_network_topology/tests/test_node.py index b323d6f6..19719e6f 100644 --- a/openwisp_network_topology/tests/test_node.py +++ b/openwisp_network_topology/tests/test_node.py @@ -7,8 +7,8 @@ from .utils import CreateGraphObjectsMixin -Node = swapper.load_model('topology', 'Node') -Topology = swapper.load_model('topology', 'Topology') +Node = swapper.load_model("topology", "Node") +Topology = swapper.load_model("topology", "Topology") class TestNode(CreateGraphObjectsMixin, TestOrganizationMixin, TestCase): @@ -20,20 +20,20 @@ def setUp(self): org = self._get_org() t = self._create_topology(organization=org) self._create_node( - label='node1', addresses=['192.168.0.1'], topology=t, organization=org + label="node1", addresses=["192.168.0.1"], topology=t, organization=org ) self._create_node( - label='node2', addresses=['192.168.0.2'], topology=t, organization=org + label="node2", addresses=["192.168.0.2"], topology=t, organization=org ) def test_str(self): - n = self.node_model(addresses=['192.168.0.1'], label='test node') + n = self.node_model(addresses=["192.168.0.1"], label="test node") self.assertIsInstance(str(n), str) def test_clean_properties(self): t = self.topology_model.objects.first() n = t._create_node( - addresses=['192.168.0.1'], label='test node', properties=None + addresses=["192.168.0.1"], label="test node", properties=None ) n.full_clean() self.assertEqual(n.properties, {}) @@ -41,102 +41,102 @@ def test_clean_properties(self): def test_node_address_single(self): t = self.topology_model.objects.first() n = t._create_node( - addresses=['192.168.0.1'], label='test node', properties=None + addresses=["192.168.0.1"], label="test node", properties=None ) n.full_clean() n.save() - self.assertEqual(n.addresses[0], '192.168.0.1') - self.assertEqual(n.addresses, ['192.168.0.1']) + self.assertEqual(n.addresses[0], "192.168.0.1") + self.assertEqual(n.addresses, ["192.168.0.1"]) def test_node_address_multiple(self): t = self.topology_model.objects.first() n = t._create_node( - label='test node', - addresses=['192.168.0.1', '10.0.0.1', '10.0.0.2', '10.0.0.3'], + label="test node", + addresses=["192.168.0.1", "10.0.0.1", "10.0.0.2", "10.0.0.3"], ) self.assertEqual( - n.addresses, ['192.168.0.1', '10.0.0.1', '10.0.0.2', '10.0.0.3'] + n.addresses, ["192.168.0.1", "10.0.0.1", "10.0.0.2", "10.0.0.3"] ) def test_node_local_addresses(self): t = self.topology_model.objects.first() n = t._create_node( - label='test node', addresses=['192.168.0.1', '10.0.0.1', '10.0.0.2'] + label="test node", addresses=["192.168.0.1", "10.0.0.1", "10.0.0.2"] ) - self.assertEqual(n.local_addresses, ['10.0.0.1', '10.0.0.2']) + self.assertEqual(n.local_addresses, ["10.0.0.1", "10.0.0.2"]) def test_node_name(self): t = self.topology_model.objects.first() - n = t._create_node(addresses=['192.168.0.1', '10.0.0.1']) - self.assertEqual(n.name, '192.168.0.1') - n.label = 'test node' - self.assertEqual(n.name, 'test node') + n = t._create_node(addresses=["192.168.0.1", "10.0.0.1"]) + self.assertEqual(n.name, "192.168.0.1") + n.label = "test node" + self.assertEqual(n.name, "test node") def test_node_without_name(self): # According to the RFC, a node MAY not have name nor local_addresses defined t = self.topology_model.objects.first() n = t._create_node(addresses=[]) - self.assertEqual(n.name, '') + self.assertEqual(n.name, "") def test_json(self): t = self.topology_model.objects.first() n = t._create_node( - label='test node', - addresses=['192.168.0.1', '10.0.0.1'], + label="test node", + addresses=["192.168.0.1", "10.0.0.1"], properties={"gateway": True}, ) self.assertEqual( dict(n.json(dict=True)), { - 'id': '192.168.0.1', - 'label': 'test node', - 'local_addresses': ['10.0.0.1'], - 'properties': { - 'gateway': True, - 'created': JSONEncoder().default(n.created), - 'modified': JSONEncoder().default(n.modified), + "id": "192.168.0.1", + "label": "test node", + "local_addresses": ["10.0.0.1"], + "properties": { + "gateway": True, + "created": JSONEncoder().default(n.created), + "modified": JSONEncoder().default(n.modified), }, }, ) self.assertIsInstance(n.json(), str) - with self.subTest('testing original=True'): + with self.subTest("testing original=True"): netjson = n.json(dict=True, original=True) - self.assertNotIn('created', netjson['properties']) - self.assertNotIn('modified', netjson['properties']) + self.assertNotIn("created", netjson["properties"]) + self.assertNotIn("modified", netjson["properties"]) def test_get_from_address(self): t = self.topology_model.objects.first() - n = t._create_node(addresses=['192.168.0.1', '10.0.0.1']) + n = t._create_node(addresses=["192.168.0.1", "10.0.0.1"]) n.full_clean() n.save() self.assertIsInstance( - self.node_model.get_from_address('192.168.0.1', t), self.node_model + self.node_model.get_from_address("192.168.0.1", t), self.node_model ) self.assertIsInstance( - self.node_model.get_from_address('10.0.0.1', t), self.node_model + self.node_model.get_from_address("10.0.0.1", t), self.node_model ) - self.assertIsNone(self.node_model.get_from_address('wrong', t)) + self.assertIsNone(self.node_model.get_from_address("wrong", t)) def test_count_address(self): t = self.topology_model.objects.first() - self.assertEqual(self.node_model.count_address('192.168.0.1', t), 1) - self.assertEqual(self.node_model.count_address('0.0.0.0', t), 0) + self.assertEqual(self.node_model.count_address("192.168.0.1", t), 1) + self.assertEqual(self.node_model.count_address("0.0.0.0", t), 0) def test_count_address_issue_58(self): t = self.topology_model.objects.first() - n1 = t._create_node(addresses=['Benz_Kalloni']) + n1 = t._create_node(addresses=["Benz_Kalloni"]) n1.full_clean() n1.save() - n2 = t._create_node(addresses=['Kalloni']) + n2 = t._create_node(addresses=["Kalloni"]) n2.full_clean() n2.save() - self.assertEqual(self.node_model.count_address('Benz_Kalloni', t), 1) - self.assertEqual(self.node_model.count_address('Kalloni', t), 1) + self.assertEqual(self.node_model.count_address("Benz_Kalloni", t), 1) + self.assertEqual(self.node_model.count_address("Kalloni", t), 1) def test_node_auto_org(self): t = self.topology_model.objects.first() n = self.node_model( - label='TestNode', addresses=['192.168.0.1'], properties={}, topology=t + label="TestNode", addresses=["192.168.0.1"], properties={}, topology=t ) n.full_clean() self.assertEqual(n.organization, t.organization) @@ -144,60 +144,60 @@ def test_node_auto_org(self): def test_user_properties_in_json(self): t = self.topology_model.objects.first() n = t._create_node( - addresses=['192.168.0.1'], label='test node', properties=None + addresses=["192.168.0.1"], label="test node", properties=None ) - n.properties = {'gateway': True} - n.user_properties = {'user_property': True} + n.properties = {"gateway": True} + n.user_properties = {"user_property": True} n.full_clean() n.save() - with self.subTest('view json with original False'): + with self.subTest("view json with original False"): data = n.json(dict=True) - self.assertIn('gateway', data['properties']) - self.assertIn('user_property', data['properties']) + self.assertIn("gateway", data["properties"]) + self.assertIn("user_property", data["properties"]) - with self.subTest('view json with original True'): + with self.subTest("view json with original True"): data = n.json(dict=True, original=True) - self.assertIn('gateway', data['properties']) - self.assertNotIn('user_property', data['properties']) + self.assertIn("gateway", data["properties"]) + self.assertNotIn("user_property", data["properties"]) def test_get_organization(self): org = self._get_org() org_topology = Topology.objects.first() shared_topology = self._create_topology( - label='Shared Topology', organization=None + label="Shared Topology", organization=None ) - with self.subTest('Shared nodes can belong to shared topology'): + with self.subTest("Shared nodes can belong to shared topology"): try: shared_node = self._create_node( - label='shared_node', - addresses=['192.168.0.6'], + label="shared_node", + addresses=["192.168.0.6"], topology=shared_topology, organization=None, ) except ValidationError: - self.fail('Shared topology failed to include shared node.') + self.fail("Shared topology failed to include shared node.") shared_node.refresh_from_db() self.assertEqual(shared_node.organization, None) - with self.subTest('Non-shared nodes can belong to shared topology'): + with self.subTest("Non-shared nodes can belong to shared topology"): try: org_node = self._create_node( - label='org_node', - addresses=['192.168.0.7'], + label="org_node", + addresses=["192.168.0.7"], topology=shared_topology, organization=org, ) except ValidationError: - self.fail('Shared topology failed to include non-shared node.') + self.fail("Shared topology failed to include non-shared node.") org_node.refresh_from_db() self.assertEqual(org_node.organization, org) - with self.subTest('Node gets organization of non-shared topology'): + with self.subTest("Node gets organization of non-shared topology"): node = self._create_node( - label='node', - addresses=['192.168.0.8'], + label="node", + addresses=["192.168.0.8"], topology=org_topology, organization=None, ) @@ -205,12 +205,12 @@ def test_get_organization(self): self.assertEqual(node.organization, org) with self.subTest( - 'ValidationError on different organization node and topology' + "ValidationError on different organization node and topology" ): with self.assertRaises(ValidationError): self._create_node( - label='node', - addresses=['192.168.0.9'], + label="node", + addresses=["192.168.0.9"], topology=org_topology, - organization=self._get_org('default'), + organization=self._get_org("default"), ) diff --git a/openwisp_network_topology/tests/test_selenium.py b/openwisp_network_topology/tests/test_selenium.py index 1d55138e..1c62cf12 100644 --- a/openwisp_network_topology/tests/test_selenium.py +++ b/openwisp_network_topology/tests/test_selenium.py @@ -11,12 +11,12 @@ from .utils import CreateGraphObjectsMixin, LoadMixin -Link = load_model('topology', 'Link') -Node = load_model('topology', 'Node') -Topology = load_model('topology', 'Topology') +Link = load_model("topology", "Link") +Node = load_model("topology", "Node") +Topology = load_model("topology", "Topology") -@tag('selenium_tests') +@tag("selenium_tests") class TestTopologyGraphVisualizer( TestOrganizationMixin, CreateGraphObjectsMixin, @@ -24,15 +24,15 @@ class TestTopologyGraphVisualizer( SeleniumTestMixin, StaticLiveServerTestCase, ): - app_label = 'topology' + app_label = "topology" node_model = Node link_model = Link topology_model = Topology - _EXPECTED_KEYS = ['Label', 'Protocol', 'Version', 'Metric', 'Nodes', 'Links'] + _EXPECTED_KEYS = ["Label", "Protocol", "Version", "Metric", "Nodes", "Links"] @property def prefix(self): - return f'admin:{self.app_label}' + return f"admin:{self.app_label}" def setUp(self): org = self._create_org() @@ -41,14 +41,14 @@ def setUp(self): ) self.topology = self._create_topology(organization=org) self._create_node( - label='node1', - addresses=['192.168.0.1'], + label="node1", + addresses=["192.168.0.1"], topology=self.topology, organization=org, ) self._create_node( - label='node2', - addresses=['192.168.0.2'], + label="node2", + addresses=["192.168.0.2"], topology=self.topology, organization=org, ) @@ -59,27 +59,27 @@ def tearDown(self): def _assert_topology_graph(self, hide_loading_overlay=True): if hide_loading_overlay: - self.hide_loading_overlay('loadingContainer') - self.find_element(By.CLASS_NAME, 'sideBarHandle').click() - self.wait_for_visibility(By.CLASS_NAME, 'njg-metaInfoContainer') - self.wait_for_visibility(By.XPATH, '//div[1]/canvas') + self.hide_loading_overlay("loadingContainer") + self.find_element(By.CLASS_NAME, "sideBarHandle").click() + self.wait_for_visibility(By.CLASS_NAME, "njg-metaInfoContainer") + self.wait_for_visibility(By.XPATH, "//div[1]/canvas") self.assertEqual(self.get_browser_logs(), []) topology_graph_dict = self.topology.json(dict=True) - topology_graph_label_keys = self.find_elements(By.CSS_SELECTOR, '.njg-keyLabel') + topology_graph_label_keys = self.find_elements(By.CSS_SELECTOR, ".njg-keyLabel") topology_graph_label_values = self.find_elements( - By.CSS_SELECTOR, '.njg-valueLabel' + By.CSS_SELECTOR, ".njg-valueLabel" ) self.assertEqual(len(topology_graph_label_keys), len(self._EXPECTED_KEYS)) # ensure correct topology graph labels are present for key in topology_graph_label_keys: self.assertIn(key.text, self._EXPECTED_KEYS) expected_label_values = [ - topology_graph_dict['label'], - topology_graph_dict['protocol'], - topology_graph_dict['version'], - topology_graph_dict['metric'], - str(len(topology_graph_dict['nodes'])), - str(len(topology_graph_dict['links'])), + topology_graph_dict["label"], + topology_graph_dict["protocol"], + topology_graph_dict["version"], + topology_graph_dict["metric"], + str(len(topology_graph_dict["nodes"])), + str(len(topology_graph_dict["links"])), ] for label_value, expected_value in zip( topology_graph_label_values, expected_label_values @@ -87,56 +87,56 @@ def _assert_topology_graph(self, hide_loading_overlay=True): self.assertEqual(label_value.text, expected_value) def test_topology_admin_view_graph_visualizer(self): - path = reverse(f'{self.prefix}_topology_change', args=[self.topology.pk]) + path = reverse(f"{self.prefix}_topology_change", args=[self.topology.pk]) self.login() self.open(path) - self.find_element(By.CSS_SELECTOR, 'input.visualizelink').click() + self.find_element(By.CSS_SELECTOR, "input.visualizelink").click() self._assert_topology_graph() def test_unpublished_topology_admin_view_graph_visualizer(self): self.topology_model.objects.update(published=False) - path = reverse(f'{self.prefix}_topology_change', args=[self.topology.pk]) + path = reverse(f"{self.prefix}_topology_change", args=[self.topology.pk]) self.login() self.open(path) - self.find_element(By.CSS_SELECTOR, 'input.visualizelink').click() + self.find_element(By.CSS_SELECTOR, "input.visualizelink").click() self._assert_topology_graph() def test_topology_non_admin_view_graph_visualizer(self): self.login() - self.open(reverse('topology_list'), html_container='body.frontend') + self.open(reverse("topology_list"), html_container="body.frontend") self.find_element(By.XPATH, "//ul[@id='menu']/li/a").click() self._assert_topology_graph() def test_topology_admin_visualizer_multiple_close_btn_append(self): - path = reverse(f'{self.prefix}_topology_change', args=[self.topology.pk]) + path = reverse(f"{self.prefix}_topology_change", args=[self.topology.pk]) self.login() self.open(path) - self.find_element(By.CSS_SELECTOR, 'input.visualizelink').click() + self.find_element(By.CSS_SELECTOR, "input.visualizelink").click() self._assert_topology_graph() - self.find_element(By.CLASS_NAME, 'closeBtn').click() + self.find_element(By.CLASS_NAME, "closeBtn").click() # Now try to open visualizer # again and make sure only single # 'closeBtn' element is present in the DOM - self.find_element(By.CSS_SELECTOR, 'input.visualizelink').click() + self.find_element(By.CSS_SELECTOR, "input.visualizelink").click() self._assert_topology_graph() try: - self.find_element(By.CLASS_NAME, 'closeBtn').click() + self.find_element(By.CLASS_NAME, "closeBtn").click() except ElementClickInterceptedException: self.fail('Multiple "closeBtn" are present in the visualizer DOM') def test_topology_admin_esc_key_close_visualizer(self): - path = reverse(f'{self.prefix}_topology_change', args=[self.topology.pk]) + path = reverse(f"{self.prefix}_topology_change", args=[self.topology.pk]) self.login() self.open(path) - self.find_element(By.CSS_SELECTOR, 'input.visualizelink').click() + self.find_element(By.CSS_SELECTOR, "input.visualizelink").click() self._assert_topology_graph() # Try to close the visualizer with the "Esc" key. - body = self.find_element(By.TAG_NAME, 'body') + body = self.find_element(By.TAG_NAME, "body") body.send_keys(Keys.ESCAPE) # Open the visualizer again and make sure no JS errors # are thrown when the visualizer is closed again - self.find_element(By.CSS_SELECTOR, 'input.visualizelink').click() + self.find_element(By.CSS_SELECTOR, "input.visualizelink").click() self._assert_topology_graph(hide_loading_overlay=False) - self.find_element(By.CLASS_NAME, 'closeBtn').click() + self.find_element(By.CLASS_NAME, "closeBtn").click() console_logs = self.get_browser_logs() self.assertEqual(console_logs, []) diff --git a/openwisp_network_topology/tests/test_snapshot.py b/openwisp_network_topology/tests/test_snapshot.py index 1e6f0b93..e8f8745c 100644 --- a/openwisp_network_topology/tests/test_snapshot.py +++ b/openwisp_network_topology/tests/test_snapshot.py @@ -1,7 +1,7 @@ import swapper from django.test import TestCase -Snapshot = swapper.load_model('topology', 'Snapshot') +Snapshot = swapper.load_model("topology", "Snapshot") class TestSnapshot(TestCase): diff --git a/openwisp_network_topology/tests/test_topology.py b/openwisp_network_topology/tests/test_topology.py index 81b8bdb4..ec8f78fe 100644 --- a/openwisp_network_topology/tests/test_topology.py +++ b/openwisp_network_topology/tests/test_topology.py @@ -11,9 +11,9 @@ from .utils import CreateGraphObjectsMixin, CreateOrgMixin, LoadMixin -Link = swapper.load_model('topology', 'Link') -Node = swapper.load_model('topology', 'Node') -Topology = swapper.load_model('topology', 'Topology') +Link = swapper.load_model("topology", "Link") +Node = swapper.load_model("topology", "Node") +Topology = swapper.load_model("topology", "Topology") class TestTopology(CreateOrgMixin, CreateGraphObjectsMixin, LoadMixin, TestCase): @@ -26,20 +26,20 @@ def setUp(self): org = self._create_org() t = self._create_topology(organization=org) self._create_node( - label='node1', addresses=['192.168.0.1'], topology=t, organization=org + label="node1", addresses=["192.168.0.1"], topology=t, organization=org ) self._create_node( - label='node2', addresses=['192.168.0.2'], topology=t, organization=org + label="node2", addresses=["192.168.0.2"], topology=t, organization=org ) def _get_nodes(self): return self.node_model.objects.all() - def _set_receive(self, expiration_time=0, parser='netdiff.NetJsonParser'): + def _set_receive(self, expiration_time=0, parser="netdiff.NetJsonParser"): t = self.topology_model.objects.first() t.parser = parser - t.strategy = 'receive' - t.key = 'test' + t.strategy = "receive" + t.key = "test" t.expiration_time = expiration_time t.save() return t @@ -59,24 +59,24 @@ def test_json_empty(self): self.assertDictEqual( graph, { - 'type': 'NetworkGraph', - 'protocol': 'OLSR', - 'version': '0.8', - 'metric': 'ETX', - 'label': t.label, - 'id': str(t.id), - 'parser': t.parser, - 'created': t.created, - 'modified': t.modified, - 'nodes': [], - 'links': [], + "type": "NetworkGraph", + "protocol": "OLSR", + "version": "0.8", + "metric": "ETX", + "label": t.label, + "id": str(t.id), + "parser": t.parser, + "created": t.created, + "modified": t.modified, + "nodes": [], + "links": [], }, ) def test_json(self): node1, node2 = self._get_nodes() t = self.topology_model.objects.first() - node3 = t._create_node(addresses=['192.168.0.3'], label='node3') + node3 = t._create_node(addresses=["192.168.0.3"], label="node3") node3.save() link = t._create_link(source=node1, target=node2, cost=1) link.save() @@ -86,21 +86,21 @@ def test_json(self): self.assertDictEqual( dict(graph), { - 'type': 'NetworkGraph', - 'protocol': 'OLSR', - 'version': '0.8', - 'metric': 'ETX', - 'label': t.label, - 'id': str(t.id), - 'parser': t.parser, - 'created': t.created, - 'modified': t.modified, - 'nodes': [ + "type": "NetworkGraph", + "protocol": "OLSR", + "version": "0.8", + "metric": "ETX", + "label": t.label, + "id": str(t.id), + "parser": t.parser, + "created": t.created, + "modified": t.modified, + "nodes": [ dict(node1.json(dict=True)), dict(node2.json(dict=True)), dict(node3.json(dict=True)), ], - 'links': [dict(link.json(dict=True)), dict(l2.json(dict=True))], + "links": [dict(link.json(dict=True)), dict(l2.json(dict=True))], }, ) self.assertIsInstance(t.json(), str) @@ -108,48 +108,48 @@ def test_json(self): @responses.activate def test_empty_diff(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', + "http://127.0.0.1:9090", body=t.json(original=True), - content_type='application/json', + content_type="application/json", ) self.assertDictEqual( - t.diff(), {'added': None, 'removed': None, 'changed': None} + t.diff(), {"added": None, "removed": None, "changed": None} ) @responses.activate def test_update_all_attributes(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', - body=self._load('static/netjson-1-link.json'), - content_type='application/json', + "http://127.0.0.1:9090", + body=self._load("static/netjson-1-link.json"), + content_type="application/json", ) t.protocol = None t.version = None t.metric = None t.update() t.refresh_from_db() - self.assertEqual(t.protocol, 'OLSR') - self.assertEqual(t.version, '0.8') - self.assertEqual(t.metric, 'ETX') + self.assertEqual(t.protocol, "OLSR") + self.assertEqual(t.version, "0.8") + self.assertEqual(t.metric, "ETX") @responses.activate def test_update_added(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', - body=self._load('static/netjson-1-link.json'), - content_type='application/json', + "http://127.0.0.1:9090", + body=self._load("static/netjson-1-link.json"), + content_type="application/json", ) self.node_model.objects.all().delete() t.update() @@ -157,13 +157,13 @@ def test_update_added(self): self.assertEqual(self.link_model.objects.count(), 1) node1 = self.node_model.objects.get(addresses__contains='"192.168.0.1"') node2 = self.node_model.objects.get(addresses__contains='"192.168.0.2"') - self.assertEqual(node1.local_addresses, ['10.0.0.1']) - self.assertEqual(node1.properties, {'gateway': True}) + self.assertEqual(node1.local_addresses, ["10.0.0.1"]) + self.assertEqual(node1.properties, {"gateway": True}) link = self.link_model.objects.first() self.assertIn(link.source, [node1, node2]) self.assertIn(link.target, [node1, node2]) self.assertEqual(link.cost, 1.0) - self.assertEqual(link.properties, {'pretty': True}) + self.assertEqual(link.properties, {"pretty": True}) # ensure repeating the action is idempotent t.update() self.assertEqual(self.node_model.objects.count(), 2) @@ -172,120 +172,120 @@ def test_update_added(self): @responses.activate def test_update_changed(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', - body=self._load('static/netjson-1-link.json'), - content_type='application/json', + "http://127.0.0.1:9090", + body=self._load("static/netjson-1-link.json"), + content_type="application/json", ) self.node_model.objects.all().delete() t.update() - node = self.node_model.get_from_address('192.168.0.2', topology=t) - self.assertEqual(node.label, '') - self.assertEqual(node.addresses, ['192.168.0.2']) + node = self.node_model.get_from_address("192.168.0.2", topology=t) + self.assertEqual(node.label, "") + self.assertEqual(node.addresses, ["192.168.0.2"]) link = self.link_model.objects.first() - self.assertEqual(link.properties, {'pretty': True}) + self.assertEqual(link.properties, {"pretty": True}) self.assertEqual(link.cost, 1.0) - self.assertEqual(link.cost_text, '10 Mbps') + self.assertEqual(link.cost_text, "10 Mbps") # now change - t.url = t.url.replace('9090', '9091') + t.url = t.url.replace("9090", "9091") t.save() responses.add( responses.GET, - 'http://127.0.0.1:9091', - body=self._load('static/netjson-2-links.json'), - content_type='application/json', + "http://127.0.0.1:9091", + body=self._load("static/netjson-2-links.json"), + content_type="application/json", ) t.update() self.assertEqual(self.node_model.objects.count(), 3) # test changed properties for existing node - node = self.node_model.get_from_address('192.168.0.2', topology=t) - self.assertEqual(node.label, 'node2') - self.assertEqual(node.addresses, ['192.168.0.2', '10.0.0.2']) + node = self.node_model.get_from_address("192.168.0.2", topology=t) + self.assertEqual(node.label, "node2") + self.assertEqual(node.addresses, ["192.168.0.2", "10.0.0.2"]) # test added node with properties - node = self.node_model.get_from_address('192.168.0.3', topology=t) - self.assertEqual(node.label, 'node3') - self.assertEqual(node.addresses, ['192.168.0.3', '10.0.0.3']) + node = self.node_model.get_from_address("192.168.0.3", topology=t) + self.assertEqual(node.label, "node3") + self.assertEqual(node.addresses, ["192.168.0.3", "10.0.0.3"]) self.assertEqual(self.link_model.objects.count(), 2) link.refresh_from_db() - self.assertEqual(link.properties, {'pretty': True}) + self.assertEqual(link.properties, {"pretty": True}) self.assertEqual(link.cost, 1.5) - self.assertEqual(link.cost_text, '15 Mbps') + self.assertEqual(link.cost_text, "15 Mbps") link = self.link_model.get_from_nodes( - source='192.168.0.1', target='192.168.0.3', topology=t + source="192.168.0.1", target="192.168.0.3", topology=t ) - self.assertEqual(link.properties, {'pretty': False}) + self.assertEqual(link.properties, {"pretty": False}) self.assertEqual(link.cost, 2.0) - self.assertEqual(link.cost_text, '20 Mbps') + self.assertEqual(link.cost_text, "20 Mbps") @responses.activate def test_update_removed(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', - body=self._load('static/netjson-2-links.json'), - content_type='application/json', + "http://127.0.0.1:9090", + body=self._load("static/netjson-2-links.json"), + content_type="application/json", ) self.node_model.objects.all().delete() t.update() self.assertEqual(self.node_model.objects.count(), 3) self.assertEqual(self.link_model.objects.count(), 2) # now change - t.url = t.url.replace('9090', '9091') + t.url = t.url.replace("9090", "9091") t.save() responses.add( responses.GET, - 'http://127.0.0.1:9091', - body=self._load('static/netjson-1-link.json'), - content_type='application/json', + "http://127.0.0.1:9091", + body=self._load("static/netjson-1-link.json"), + content_type="application/json", ) t.update() self.assertEqual(self.node_model.objects.count(), 3) self.assertEqual(self.link_model.objects.count(), 2) - self.assertEqual(self.link_model.objects.filter(status='down').count(), 1) - link = self.link_model.objects.filter(status='down').first() - self.assertIn('192.168.0.3', [link.source.netjson_id, link.target.netjson_id]) + self.assertEqual(self.link_model.objects.filter(status="down").count(), 1) + link = self.link_model.objects.filter(status="down").first() + self.assertIn("192.168.0.3", [link.source.netjson_id, link.target.netjson_id]) self.assertEqual(link.cost, 2.0) @responses.activate def test_update_status_existing_link(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() n1 = self.node_model.objects.all()[0] n2 = self.node_model.objects.all()[1] link = t._create_link( - source=n1, target=n2, cost=1, status='down', properties={'pretty': True} + source=n1, target=n2, cost=1, status="down", properties={"pretty": True} ) link.full_clean() link.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', - body=self._load('static/netjson-1-link.json'), - content_type='application/json', + "http://127.0.0.1:9090", + body=self._load("static/netjson-1-link.json"), + content_type="application/json", ) t.update() self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) link.refresh_from_db() - self.assertEqual(link.status, 'up') + self.assertEqual(link.status, "up") def test_topology_url_empty(self): t = self.topology_model( - label='test', parser='netdiff.NetJsonParser', strategy='fetch' + label="test", parser="netdiff.NetJsonParser", strategy="fetch" ) with self.assertRaises(ValidationError): t.full_clean() def test_topology_key_empty(self): t = self.topology_model( - label='test', parser='netdiff.NetJsonParser', strategy='receive', key='' + label="test", parser="netdiff.NetJsonParser", strategy="receive", key="" ) with self.assertRaises(ValidationError): t.full_clean() @@ -293,19 +293,19 @@ def test_topology_key_empty(self): def _test_receive_added(self, expiration_time=0): self.node_model.objects.all().delete() t = self._set_receive(expiration_time) - data = self._load('static/netjson-1-link.json') + data = self._load("static/netjson-1-link.json") t.receive(data) self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) node1 = self.node_model.objects.get(addresses__contains='"192.168.0.1"') node2 = self.node_model.objects.get(addresses__contains='"192.168.0.2"') - self.assertEqual(node1.local_addresses, ['10.0.0.1']) - self.assertEqual(node1.properties, {'gateway': True}) + self.assertEqual(node1.local_addresses, ["10.0.0.1"]) + self.assertEqual(node1.properties, {"gateway": True}) link = self.link_model.objects.first() self.assertIn(link.source, [node1, node2]) self.assertIn(link.target, [node1, node2]) self.assertEqual(link.cost, 1.0) - self.assertEqual(link.properties, {'pretty': True}) + self.assertEqual(link.properties, {"pretty": True}) # ensure repeating the action is idempotent t.receive(data) self.assertEqual(self.node_model.objects.count(), 2) @@ -317,11 +317,11 @@ def test_receive_added(self): def _test_receive_changed(self, expiration_time=0): t = self._set_receive(expiration_time) self.node_model.objects.all().delete() - data = self._load('static/netjson-1-link.json') + data = self._load("static/netjson-1-link.json") t.receive(data) link = self.link_model.objects.first() # now change - data = self._load('static/netjson-2-links.json') + data = self._load("static/netjson-2-links.json") t.receive(data) link.refresh_from_db() self.assertEqual(self.node_model.objects.count(), 3) @@ -334,67 +334,67 @@ def test_receive_changed(self): def test_receive_removed(self): t = self._set_receive() self.node_model.objects.all().delete() - data = self._load('static/netjson-2-links.json') + data = self._load("static/netjson-2-links.json") t.receive(data) self.assertEqual(self.node_model.objects.count(), 3) self.assertEqual(self.link_model.objects.count(), 2) # now change - data = self._load('static/netjson-1-link.json') + data = self._load("static/netjson-1-link.json") t.receive(data) self.assertEqual(self.node_model.objects.count(), 3) self.assertEqual(self.link_model.objects.count(), 2) - self.assertEqual(self.link_model.objects.filter(status='down').count(), 1) - link = self.link_model.objects.filter(status='down').first() - self.assertIn('192.168.0.3', [link.source.netjson_id, link.target.netjson_id]) + self.assertEqual(self.link_model.objects.filter(status="down").count(), 1) + link = self.link_model.objects.filter(status="down").first() + self.assertIn("192.168.0.3", [link.source.netjson_id, link.target.netjson_id]) self.assertEqual(link.cost, 2.0) def test_receive_removed_bug(self): - t = self._set_receive(parser='netdiff.OpenvpnParser') + t = self._set_receive(parser="netdiff.OpenvpnParser") self.node_model.objects.all().delete() - data = self._load('static/openvpn.txt') + data = self._load("static/openvpn.txt") t.receive(data) self.assertEqual(self.node_model.objects.count(), 4) self.assertEqual(self.link_model.objects.count(), 3) - self.assertEqual(self.link_model.objects.filter(status='up').count(), 3) - self.assertEqual(self.link_model.objects.filter(status='down').count(), 0) - data = self._load('static/openvpn-0-links.txt') + self.assertEqual(self.link_model.objects.filter(status="up").count(), 3) + self.assertEqual(self.link_model.objects.filter(status="down").count(), 0) + data = self._load("static/openvpn-0-links.txt") t.receive(data) self.assertEqual(self.node_model.objects.count(), 4) self.assertEqual(self.link_model.objects.count(), 3) - self.assertEqual(self.link_model.objects.filter(status='down').count(), 3) - self.assertEqual(self.link_model.objects.filter(status='up').count(), 0) + self.assertEqual(self.link_model.objects.filter(status="down").count(), 3) + self.assertEqual(self.link_model.objects.filter(status="up").count(), 0) t.refresh_from_db() netjson = t.json(dict=True) for link in self.link_model.objects.all(): - for attr in ['status', 'status_changed', 'created', 'modified']: - with self.subTest(f'{attr} should not be saved in properties ({link})'): + for attr in ["status", "status_changed", "created", "modified"]: + with self.subTest(f"{attr} should not be saved in properties ({link})"): self.assertNotIn(attr, link.properties) - for link in netjson['links']: - with self.subTest(f'status should be down ({link})'): - self.assertEqual(link['properties']['status'], 'down') - for attr in ['status_changed', 'created', 'modified']: - with self.subTest(f'{attr} should be present in properties ({link})'): - self.assertIn(attr, link['properties']) - for node in netjson['nodes']: - for attr in ['created', 'modified']: - with self.subTest(f'{attr} should be present in properties ({node})'): - self.assertIn(attr, node['properties']) + for link in netjson["links"]: + with self.subTest(f"status should be down ({link})"): + self.assertEqual(link["properties"]["status"], "down") + for attr in ["status_changed", "created", "modified"]: + with self.subTest(f"{attr} should be present in properties ({link})"): + self.assertIn(attr, link["properties"]) + for node in netjson["nodes"]: + for attr in ["created", "modified"]: + with self.subTest(f"{attr} should be present in properties ({node})"): + self.assertIn(attr, node["properties"]) def test_receive_status_existing_link(self): t = self._set_receive() n1 = self.node_model.objects.all()[0] n2 = self.node_model.objects.all()[1] link = t._create_link( - source=n1, target=n2, cost=1, status='down', properties={'pretty': True} + source=n1, target=n2, cost=1, status="down", properties={"pretty": True} ) link.full_clean() link.save() - data = self._load('static/netjson-1-link.json') + data = self._load("static/netjson-1-link.json") t.receive(data) self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) link.refresh_from_db() - self.assertEqual(link.status, 'up') + self.assertEqual(link.status, "up") def test_multiple_receive_added(self): self._test_receive_added(expiration_time=50) @@ -405,55 +405,55 @@ def test_multiple_receive_changed(self): def test_multiple_receive_removed(self): with freeze_time() as frozen_time: t = self._set_receive(expiration_time=1) - data = self._load('static/netjson-2-links.json') + data = self._load("static/netjson-2-links.json") for sleep_time in [0, 1.2, 1]: frozen_time.tick(timedelta(seconds=sleep_time)) t.receive(data) self.assertEqual(self.node_model.objects.count(), 3) self.assertEqual(self.link_model.objects.count(), 2) self.assertEqual( - self.link_model.objects.filter(status='down').count(), 0 + self.link_model.objects.filter(status="down").count(), 0 ) # receive change - data = self._load('static/netjson-1-link.json') + data = self._load("static/netjson-1-link.json") t.receive(data) self.assertEqual(self.node_model.objects.count(), 3) self.assertEqual(self.link_model.objects.count(), 2) # expiration_time has not expired - self.assertEqual(self.link_model.objects.filter(status='down').count(), 0) + self.assertEqual(self.link_model.objects.filter(status="down").count(), 0) # expiration_time has now expired for 1 link frozen_time.tick(timedelta(seconds=1.2)) t.receive(data) - self.assertEqual(self.link_model.objects.filter(status='down').count(), 1) - link = self.link_model.objects.filter(status='down').first() + self.assertEqual(self.link_model.objects.filter(status="down").count(), 1) + link = self.link_model.objects.filter(status="down").first() self.assertIn( - '192.168.0.3', [link.source.netjson_id, link.target.netjson_id] + "192.168.0.3", [link.source.netjson_id, link.target.netjson_id] ) self.assertEqual(link.cost, 2.0) def test_multiple_receive_split_network(self): def _assert_split_topology(self, topology): self.assertEqual(self.node_model.objects.count(), 4) - self.assertEqual(self.link_model.objects.filter(status='up').count(), 2) - self.assertEqual(self.link_model.objects.filter(status='down').count(), 0) + self.assertEqual(self.link_model.objects.filter(status="up").count(), 2) + self.assertEqual(self.link_model.objects.filter(status="down").count(), 0) link = self.link_model.get_from_nodes( - '192.168.0.1', '192.168.0.2', topology + "192.168.0.1", "192.168.0.2", topology ) self.assertIsNotNone(link) - self.assertEqual(link.status, 'up') + self.assertEqual(link.status, "up") self.assertEqual(link.cost, 1.0) link = self.link_model.get_from_nodes( - '192.168.0.10', '192.168.0.20', topology + "192.168.0.10", "192.168.0.20", topology ) self.assertIsNotNone(link) - self.assertEqual(link.status, 'up') + self.assertEqual(link.status, "up") self.assertEqual(link.cost, 1.1) with freeze_time() as frozen_time: self.node_model.objects.all().delete() t = self._set_receive(expiration_time=1) - network1 = self._load('static/netjson-1-link.json') - network2 = self._load('static/split-network.json') + network1 = self._load("static/netjson-1-link.json") + network2 = self._load("static/split-network.json") t.receive(network1) t.receive(network2) _assert_split_topology(self, t) @@ -470,9 +470,9 @@ def test_very_long_addresses(self): see https://github.com/netjson/django-netjsongraph/issues/6 """ t = self._set_receive() - data = self._load('static/very-long-addresses.json') + data = self._load("static/very-long-addresses.json") t.receive(data) - n = self.node_model.get_from_address('2001:4e12:452a:1:172::10', t) + n = self.node_model.get_from_address("2001:4e12:452a:1:172::10", t) self.assertEqual(len("".join(n.addresses)), 568) def test_save_snapshot(self): @@ -486,46 +486,46 @@ def test_save_snapshot(self): self.assertFalse(s.created == s.modified) def test_label_addition(self): - t = self._set_receive(parser='netdiff.OpenvpnParser') + t = self._set_receive(parser="netdiff.OpenvpnParser") t.save() t.node_set.all().delete() - data = self._load('static/openvpn.txt') + data = self._load("static/openvpn.txt") t.receive(data) self.assertEqual(t.node_set.count(), 4) labels = [] for node in t.node_set.all(): labels.append(node.label) - self.assertIn('Syskrack', labels) - self.assertIn('Kali-Matera', labels) - self.assertIn('pomezia', labels) + self.assertIn("Syskrack", labels) + self.assertIn("Kali-Matera", labels) + self.assertIn("pomezia", labels) def test_issue_58(self): self.node_model.objects.all().delete() self.link_model.objects.all().delete() t = self._set_receive() - data = self._load('static/issue-58.json') + data = self._load("static/issue-58.json") t.receive(data) self.assertEqual(t.node_set.all().count(), 13) self.assertEqual(t.link_set.all().count(), 11) @capture_any_output() def test_update_added_items_regression_74_first(self): - t = self._set_receive(parser='netdiff.OpenvpnParser') + t = self._set_receive(parser="netdiff.OpenvpnParser") t.save() node = t.node_set.first() - items = {'nodes': [{'id': node.netjson_id}]} + items = {"nodes": [{"id": node.netjson_id}]} t._update_added_items(items) def test_update_added_items_regression_74_second(self): - t = self._set_receive(parser='netdiff.OpenvpnParser') + t = self._set_receive(parser="netdiff.OpenvpnParser") t.save() - items = {'nodes': [{'id': 'bogus'}]} + items = {"nodes": [{"id": "bogus"}]} t._update_changed_items(items) def test_receive_regression(self): - t = self._set_receive(parser='netdiff.OpenvpnParser', expiration_time=120) + t = self._set_receive(parser="netdiff.OpenvpnParser", expiration_time=120) t.save() - data = self._load('static/openvpn.txt') + data = self._load("static/openvpn.txt") pre_existing_nodes = Node.objects.count() node1 = Node.objects.first() node2 = Node.objects.last() @@ -535,9 +535,9 @@ def test_receive_regression(self): self.assertEqual(t.link_set.count(), 3) # create new link not included in the topology data link = self._create_link( - source=node1, target=node2, status='up', topology=t, cost=1 + source=node1, target=node2, status="up", topology=t, cost=1 ) - self.assertEqual(link.status, 'up') + self.assertEqual(link.status, "up") modified = link.modified # after 61 seconds we receive new (empty) data # we expect the link status to remain unchanged @@ -546,7 +546,7 @@ def test_receive_regression(self): frozen_time.tick(timedelta(seconds=61)) t.receive(data) link.refresh_from_db() - self.assertEqual(link.status, 'up') + self.assertEqual(link.status, "up") self.assertEqual(link.modified, modified) # hen the topology data is received again later # the expiration time is passed and we expect @@ -554,5 +554,5 @@ def test_receive_regression(self): frozen_time.tick(timedelta(seconds=61)) t.receive(data) link.refresh_from_db() - self.assertEqual(link.status, 'down') + self.assertEqual(link.status, "down") self.assertNotEqual(link.modified, modified) diff --git a/openwisp_network_topology/tests/test_upgrader_script.py b/openwisp_network_topology/tests/test_upgrader_script.py index 0dac84b0..3f1e4447 100644 --- a/openwisp_network_topology/tests/test_upgrader_script.py +++ b/openwisp_network_topology/tests/test_upgrader_script.py @@ -8,8 +8,8 @@ from swapper import load_model User = get_user_model() -Organization = load_model('openwisp_users', 'Organization') -Topology = load_model('topology', 'Topology') +Organization = load_model("openwisp_users", "Organization") +Topology = load_model("topology", "Topology") class TestUpgradeFromDjangoNetjsongraph(TestCase): @@ -17,26 +17,26 @@ def test_success(self): self.assertEqual(Topology.objects.count(), 0) commandOutput = StringIO() static_path = os.path.join( - os.path.dirname(__file__), 'static', 'upgrader_script' + os.path.dirname(__file__), "static", "upgrader_script" ) call_command( - 'upgrade_from_django_netjsongraph', backup=static_path, stdout=commandOutput + "upgrade_from_django_netjsongraph", backup=static_path, stdout=commandOutput ) - self.assertIn('Migration Process Complete!', commandOutput.getvalue()) - os.remove(os.path.join(static_path, 'group_loaded.json')) - os.remove(os.path.join(static_path, 'netjsongraph_loaded.json')) - os.remove(os.path.join(static_path, 'user_loaded.json')) + self.assertIn("Migration Process Complete!", commandOutput.getvalue()) + os.remove(os.path.join(static_path, "group_loaded.json")) + os.remove(os.path.join(static_path, "netjsongraph_loaded.json")) + os.remove(os.path.join(static_path, "user_loaded.json")) self.assertEqual(Topology.objects.count(), 1) try: - Topology.objects.get(label='default') + Topology.objects.get(label="default") except Topology.DoesNotExist: self.fail('Topology "default" not created!') try: - user = User.objects.get(username='sample') + user = User.objects.get(username="sample") group = Group.objects.filter(user=user).first() permissions = Permission.objects.filter(user=user).first() - self.assertEqual('sample', group.name) - self.assertEqual('add_logentry', permissions.codename) + self.assertEqual("sample", group.name) + self.assertEqual("add_logentry", permissions.codename) except User.DoesNotExist: self.fail('User "sample" not created!') @@ -44,7 +44,7 @@ def test_files_not_found(self): commandOutput = StringIO() with self.assertRaises(FileNotFoundError): call_command( - 'upgrade_from_django_netjsongraph', + "upgrade_from_django_netjsongraph", backup=os.path.join(os.path.dirname(__file__)), stdout=commandOutput, ) @@ -53,8 +53,8 @@ def test_organization_not_found(self): commandOutput = StringIO() with self.assertRaises(Organization.DoesNotExist): call_command( - 'upgrade_from_django_netjsongraph', - backup=os.path.join(os.path.dirname(__file__), 'static'), - organization='00000000-0000-0000-0000-000000000000', + "upgrade_from_django_netjsongraph", + backup=os.path.join(os.path.dirname(__file__), "static"), + organization="00000000-0000-0000-0000-000000000000", stdout=commandOutput, ) diff --git a/openwisp_network_topology/tests/test_users_integration.py b/openwisp_network_topology/tests/test_users_integration.py index 696bb344..385fb939 100644 --- a/openwisp_network_topology/tests/test_users_integration.py +++ b/openwisp_network_topology/tests/test_users_integration.py @@ -9,10 +9,10 @@ class TestUsersIntegration(TestBasicUsersIntegration): is_integration_test = True _notifications_params = { - 'notificationsetting_set-TOTAL_FORMS': 0, - 'notificationsetting_set-INITIAL_FORMS': 0, - 'notificationsetting_set-MIN_NUM_FORMS': 0, - 'notificationsetting_set-MAX_NUM_FORMS': 0, + "notificationsetting_set-TOTAL_FORMS": 0, + "notificationsetting_set-INITIAL_FORMS": 0, + "notificationsetting_set-MIN_NUM_FORMS": 0, + "notificationsetting_set-MAX_NUM_FORMS": 0, } def _get_org_edit_form_inline_params(self, user, org): diff --git a/openwisp_network_topology/tests/test_utils.py b/openwisp_network_topology/tests/test_utils.py index 6e114ce3..8f906b6e 100644 --- a/openwisp_network_topology/tests/test_utils.py +++ b/openwisp_network_topology/tests/test_utils.py @@ -13,11 +13,11 @@ from .. import settings as app_settings from .utils import CreateGraphObjectsMixin, CreateOrgMixin, LoadMixin -Organization = swapper.load_model('openwisp_users', 'Organization') -Link = swapper.load_model('topology', 'Link') -Node = swapper.load_model('topology', 'Node') -Snapshot = swapper.load_model('topology', 'Snapshot') -Topology = swapper.load_model('topology', 'Topology') +Organization = swapper.load_model("openwisp_users", "Organization") +Link = swapper.load_model("topology", "Link") +Node = swapper.load_model("topology", "Node") +Snapshot = swapper.load_model("topology", "Snapshot") +Topology = swapper.load_model("topology", "Topology") @contextmanager @@ -45,15 +45,15 @@ def setUp(self): org = self._create_org() t = self._create_topology(organization=org) self._create_node( - label='node1', addresses=['192.168.0.1'], topology=t, organization=org + label="node1", addresses=["192.168.0.1"], topology=t, organization=org ) self._create_node( - label='node2', addresses=['192.168.0.2'], topology=t, organization=org + label="node2", addresses=["192.168.0.2"], topology=t, organization=org ) def _create_link(self, **kwargs): org = self.organization_model.objects.first() - options = dict(cost='1', cost_text='one', properties={}, organization=org) + options = dict(cost="1", cost_text="one", properties={}, organization=org) options.update(kwargs) link = self.link_model(**options) link.full_clean() @@ -63,28 +63,28 @@ def _create_link(self, **kwargs): @responses.activate def test_update_all_method(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', - body=self._load('static/netjson-1-link.json'), - content_type='application/json', + "http://127.0.0.1:9090", + body=self._load("static/netjson-1-link.json"), + content_type="application/json", ) self.node_model.objects.all().delete() - self.topology_model.update_all('testnetwork') + self.topology_model.update_all("testnetwork") self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) # test exception - t.url = t.url.replace('9090', '9091') + t.url = t.url.replace("9090", "9091") t.save() self.node_model.objects.all().delete() self.link_model.objects.all().delete() responses.add( responses.GET, - 'http://127.0.0.1:9091', - body=self._load('static/netjson-invalid.json'), - content_type='application/json', + "http://127.0.0.1:9091", + body=self._load("static/netjson-invalid.json"), + content_type="application/json", ) # capture output output = StringIO() @@ -93,54 +93,54 @@ def test_update_all_method(self): self.assertEqual(self.node_model.objects.count(), 1) self.assertEqual(self.link_model.objects.count(), 0) - self.assertIn('Failed to', output.getvalue()) + self.assertIn("Failed to", output.getvalue()) @responses.activate def test_update_topology_command(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', - body=self._load('static/netjson-1-link.json'), - content_type='application/json', + "http://127.0.0.1:9090", + body=self._load("static/netjson-1-link.json"), + content_type="application/json", ) self.node_model.objects.all().delete() self.topology_model.update_all() self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) # test exception - t.url = t.url.replace('9090', '9091') + t.url = t.url.replace("9090", "9091") t.save() self.node_model.objects.all().delete() self.link_model.objects.all().delete() responses.add( responses.GET, - 'http://127.0.0.1:9091', - body=self._load('static/netjson-invalid.json'), - content_type='application/json', + "http://127.0.0.1:9091", + body=self._load("static/netjson-invalid.json"), + content_type="application/json", ) # capture output output = StringIO() with redirect_stdout(output): - call_command('update_topology') + call_command("update_topology") self.assertEqual(self.node_model.objects.count(), 1) self.assertEqual(self.link_model.objects.count(), 0) - self.assertIn('Failed to', output.getvalue()) + self.assertIn("Failed to", output.getvalue()) @responses.activate def test_update_all_method_unpublished(self): t = self.topology_model.objects.first() t.published = False - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() responses.add( responses.GET, - 'http://127.0.0.1:9090', - body=self._load('static/netjson-1-link.json'), - content_type='application/json', + "http://127.0.0.1:9090", + body=self._load("static/netjson-1-link.json"), + content_type="application/json", ) self.node_model.objects.all().delete() self.topology_model.update_all() @@ -150,14 +150,14 @@ def test_update_all_method_unpublished(self): @responses.activate def test_delete_expired_links(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() # should not delete almost_expired_date = now() - timedelta(days=app_settings.LINK_EXPIRATION - 10) n1 = self.node_model.objects.all()[0] n2 = self.node_model.objects.all()[1] link = self._create_link( - source=n1, target=n2, cost=1, status='down', topology=t + source=n1, target=n2, cost=1, status="down", topology=t ) self.link_model.objects.filter(pk=link.pk).update( created=almost_expired_date, modified=almost_expired_date @@ -174,11 +174,11 @@ def test_delete_expired_links(self): ) responses.add( responses.GET, - 'http://127.0.0.1:9090', + "http://127.0.0.1:9090", body=empty_topology, - content_type='application/json', + content_type="application/json", ) - self.topology_model.update_all('testnetwork') + self.topology_model.update_all("testnetwork") self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) # should delete @@ -186,18 +186,18 @@ def test_delete_expired_links(self): self.link_model.objects.filter(pk=link.pk).update( created=expired_date, modified=expired_date ) - self.topology_model.update_all('testnetwork') + self.topology_model.update_all("testnetwork") self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 0) @responses.activate def test_delete_expired_nodes(self): - NODE_EXPIRATION = getattr(app_settings, 'NODE_EXPIRATION') + NODE_EXPIRATION = getattr(app_settings, "NODE_EXPIRATION") # Test with the default value(False) # Should not delete - setattr(app_settings, 'NODE_EXPIRATION', False) + setattr(app_settings, "NODE_EXPIRATION", False) t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() expired_date = now() - timedelta(days=60) n1 = self.node_model.objects.all()[0] @@ -220,16 +220,16 @@ def test_delete_expired_nodes(self): ) responses.add( responses.GET, - 'http://127.0.0.1:9090', + "http://127.0.0.1:9090", body=empty_topology, - content_type='application/json', + content_type="application/json", ) - self.topology_model.update_all('testnetwork') + self.topology_model.update_all("testnetwork") self.assertEqual(self.node_model.objects.count(), 2) # Test with a custom value # Should delete - setattr(app_settings, 'NODE_EXPIRATION', 60) + setattr(app_settings, "NODE_EXPIRATION", 60) expired_date = now() - timedelta(days=app_settings.NODE_EXPIRATION + 10) self.node_model.objects.filter(pk=n1.pk).update( created=expired_date, modified=expired_date @@ -237,21 +237,21 @@ def test_delete_expired_nodes(self): self.node_model.objects.filter(pk=n2.pk).update( created=expired_date, modified=expired_date ) - self.topology_model.update_all('testnetwork') + self.topology_model.update_all("testnetwork") self.assertEqual(self.node_model.objects.count(), 0) self.assertEqual(self.link_model.objects.count(), 0) # Set the setting to it's original value - setattr(app_settings, 'NODE_EXPIRATION', NODE_EXPIRATION) + setattr(app_settings, "NODE_EXPIRATION", NODE_EXPIRATION) @responses.activate def test_delete_expired_disabled(self): t = self.topology_model.objects.first() - t.parser = 'netdiff.NetJsonParser' + t.parser = "netdiff.NetJsonParser" t.save() n1 = self.node_model.objects.all()[0] n2 = self.node_model.objects.all()[1] link = self._create_link( - source=n1, target=n2, cost=1, status='down', topology=t + source=n1, target=n2, cost=1, status="down", topology=t ) expired_date = now() - timedelta(days=app_settings.LINK_EXPIRATION + 10) self.link_model.objects.filter(pk=link.pk).update( @@ -269,13 +269,13 @@ def test_delete_expired_disabled(self): ) responses.add( responses.GET, - 'http://127.0.0.1:9090', + "http://127.0.0.1:9090", body=empty_topology, - content_type='application/json', + content_type="application/json", ) ORIGINAL_LINK_EXPIRATION = int(app_settings.LINK_EXPIRATION) app_settings.LINK_EXPIRATION = False - self.topology_model.update_all('testnetwork') + self.topology_model.update_all("testnetwork") self.assertEqual(self.node_model.objects.count(), 2) self.assertEqual(self.link_model.objects.count(), 1) app_settings.LINK_EXPIRATION = ORIGINAL_LINK_EXPIRATION @@ -285,7 +285,7 @@ def test_save_snapshot_all_method(self, **kwargs): options = dict(organization=org) options.update(**kwargs) self.assertEqual(self.snapshot_model.objects.count(), 0) - self.topology_model.save_snapshot_all('testnetwork') + self.topology_model.save_snapshot_all("testnetwork") self.assertEqual(self.snapshot_model.objects.count(), 1) self._create_topology(**options) self.topology_model.save_snapshot_all() @@ -295,5 +295,5 @@ def test_save_snapshot_command(self): self.assertEqual(self.snapshot_model.objects.count(), 0) output = StringIO() with redirect_stdout(output): - call_command('save_snapshot') + call_command("save_snapshot") self.assertEqual(self.snapshot_model.objects.count(), 1) diff --git a/openwisp_network_topology/tests/test_visualizer.py b/openwisp_network_topology/tests/test_visualizer.py index a105da24..66ac32bd 100644 --- a/openwisp_network_topology/tests/test_visualizer.py +++ b/openwisp_network_topology/tests/test_visualizer.py @@ -10,9 +10,9 @@ from .. import settings as app_settings from .utils import CreateGraphObjectsMixin, UnpublishMixin -Node = swapper.load_model('topology', 'Node') -Topology = swapper.load_model('topology', 'Topology') -OrganizationUser = swapper.load_model('openwisp_users', 'OrganizationUser') +Node = swapper.load_model("topology", "Node") +Topology = swapper.load_model("topology", "Topology") +OrganizationUser = swapper.load_model("openwisp_users", "OrganizationUser") class TestVisualizer( @@ -23,38 +23,38 @@ class TestVisualizer( def setUp(self): org = self._create_org() - user = self._create_admin(username='visualizer', email='visualizer@email.com') + user = self._create_admin(username="visualizer", email="visualizer@email.com") self._create_org_user(user=user, organization=org) self.client.force_login(user) t = self._create_topology(organization=org) self._create_node( - label='node1', addresses=['192.168.0.1'], topology=t, organization=org + label="node1", addresses=["192.168.0.1"], topology=t, organization=org ) self._create_node( - label='node2', addresses=['192.168.0.2'], topology=t, organization=org + label="node2", addresses=["192.168.0.2"], topology=t, organization=org ) def test_list(self): - response = self.client.get(reverse('topology_list')) - self.assertContains(response, 'TestNetwork') + response = self.client.get(reverse("topology_list")) + self.assertContains(response, "TestNetwork") def test_detail(self): t = self.topology_model.objects.first() - response = self.client.get(reverse('topology_detail', args=[t.pk])) + response = self.client.get(reverse("topology_detail", args=[t.pk])) self.assertContains(response, t.pk) # ensure switcher is present - self.assertContains(response, 'njg-switcher') - self.assertContains(response, 'njg-datepicker') + self.assertContains(response, "njg-switcher") + self.assertContains(response, "njg-datepicker") def test_list_unpublished(self): self._unpublish() - response = self.client.get(reverse('topology_list')) - self.assertNotContains(response, 'TestNetwork') + response = self.client.get(reverse("topology_list")) + self.assertNotContains(response, "TestNetwork") def test_detail_unpublished(self): self._unpublish() t = self.topology_model.objects.first() - response = self.client.get(reverse('topology_detail', args=[t.pk])) + response = self.client.get(reverse("topology_detail", args=[t.pk])) self.assertEqual(response.status_code, 404) def test_detail_uuid_exception(self): @@ -63,7 +63,7 @@ def test_detail_uuid_exception(self): """ t = self.topology_model.objects.first() response = self.client.get( - reverse('topology_detail', args=['{0}-wrong'.format(t.pk)]) + reverse("topology_detail", args=["{0}-wrong".format(t.pk)]) ) self.assertEqual(response.status_code, 404) @@ -71,24 +71,24 @@ def _test_visualizer_with_unauthenticated_user(self, url): self.client.logout() r = self.client.get(url) self.assertContains( - r, 'Authentication credentials were not provided.', status_code=403 + r, "Authentication credentials were not provided.", status_code=403 ) def _test_visualizer_with_not_a_manager_user(self, user, url, is_detail=False): OrganizationUser.objects.filter(user=user).delete() - perm = Permission.objects.filter(codename__endswith='topology') + perm = Permission.objects.filter(codename__endswith="topology") user.user_permissions.add(*perm) self.client.force_login(user) r = self.client.get(url) if is_detail: detail = ( - 'User is not a manager of the organization to ' - 'which the requested resource belongs.' + "User is not a manager of the organization to " + "which the requested resource belongs." ) self.assertContains(r, detail, status_code=403) else: self.assertEqual(r.status_code, 200) - self.assertNotContains(r, 'TestNetwork') + self.assertNotContains(r, "TestNetwork") self.assertNotEqual(Topology.objects.all().count(), 0) def _test_visualizer_with_not_permitted_user(self, user, url): @@ -97,57 +97,57 @@ def _test_visualizer_with_not_permitted_user(self, user, url): self.client.force_login(user) r = self.client.get(url) self.assertContains( - r, 'You do not have permission to perform this action.', status_code=403 + r, "You do not have permission to perform this action.", status_code=403 ) def test_list_visualizer_with_auth_enabled(self): - user = self._create_user(username='list-user', email='list@email.com') - url = reverse('topology_list') + user = self._create_user(username="list-user", email="list@email.com") + url = reverse("topology_list") - with self.subTest('test list visualizer with not auth user'): + with self.subTest("test list visualizer with not auth user"): self._test_visualizer_with_unauthenticated_user(url) - with self.subTest('test list visualizer with not permitted user'): + with self.subTest("test list visualizer with not permitted user"): self._test_visualizer_with_not_permitted_user(user, url) - with self.subTest('test list visualizer with not a manager user'): + with self.subTest("test list visualizer with not a manager user"): self._test_visualizer_with_not_a_manager_user(user, url) def test_detail_visualizer_with_auth_enabled(self): - user = self._create_user(username='detail-user', email='detail@email.com') + user = self._create_user(username="detail-user", email="detail@email.com") t = self.topology_model.objects.first() - url = reverse('topology_detail', args=[t.pk]) + url = reverse("topology_detail", args=[t.pk]) - with self.subTest('test detail visualizer with not auth user'): + with self.subTest("test detail visualizer with not auth user"): self._test_visualizer_with_unauthenticated_user(url) - with self.subTest('test detail visualizer with not permitted user'): + with self.subTest("test detail visualizer with not permitted user"): self._test_visualizer_with_not_permitted_user(user, url) - with self.subTest('test detail visualizer with not a manager user'): + with self.subTest("test detail visualizer with not a manager user"): self._test_visualizer_with_not_a_manager_user(user, url, is_detail=True) def _successful_visualizer_tests(self): - with self.subTest('test superuser on list visualizer'): - r = self.client.get(reverse('topology_list')) - self.assertContains(r, 'TestNetwork') + with self.subTest("test superuser on list visualizer"): + r = self.client.get(reverse("topology_list")) + self.assertContains(r, "TestNetwork") - with self.subTest('test superuser on detail visualizer'): + with self.subTest("test superuser on detail visualizer"): t = self.topology_model.objects.first() - r = self.client.get(reverse('topology_detail', args=[t.pk])) + r = self.client.get(reverse("topology_detail", args=[t.pk])) self.assertContains(r, t.pk) def test_visualizer_with_auth_enabled_superuser(self): - user = self._create_admin(username='super-visualizer', email='s@email.com') + user = self._create_admin(username="super-visualizer", email="s@email.com") self.client.force_login(user) self._successful_visualizer_tests() - @patch.object(app_settings, 'TOPOLOGY_API_AUTH_REQUIRED', return_value=False) + @patch.object(app_settings, "TOPOLOGY_API_AUTH_REQUIRED", return_value=False) def test_visualizer_with_auth_disabled(self, mocked): self._successful_visualizer_tests() - @patch.object(app_settings, 'TOPOLOGY_API_AUTH_REQUIRED', return_value=False) + @patch.object(app_settings, "TOPOLOGY_API_AUTH_REQUIRED", return_value=False) def test_visualizer_with_auth_disabled_superuser(self, mocked): - user = self._create_admin(username='super-visualizer', email='s@email.com') + user = self._create_admin(username="super-visualizer", email="s@email.com") self.client.force_login(user) self._successful_visualizer_tests() diff --git a/openwisp_network_topology/tests/test_websockets.py b/openwisp_network_topology/tests/test_websockets.py index d0c1a027..6b2e2159 100644 --- a/openwisp_network_topology/tests/test_websockets.py +++ b/openwisp_network_topology/tests/test_websockets.py @@ -14,9 +14,9 @@ from .. import settings as app_settings from .utils import CreateGraphObjectsMixin -Topology = load_model('topology', 'Topology') -Node = load_model('topology', 'Node') -Link = load_model('topology', 'Link') +Topology = load_model("topology", "Topology") +Node = load_model("topology", "Node") +Link = load_model("topology", "Link") @pytest.mark.asyncio @@ -25,17 +25,17 @@ class TestTopologySockets(CreateGraphObjectsMixin, TestOrganizationMixin): node_model = Node link_model = Link topology_model = Topology - application = import_string(getattr(settings, 'ASGI_APPLICATION')) + application = import_string(getattr(settings, "ASGI_APPLICATION")) async def _get_communicator(self, admin_client, topology_id): - session_id = admin_client.cookies['sessionid'].value + session_id = admin_client.cookies["sessionid"].value communicator = WebsocketCommunicator( self.application, - path=f'ws/network-topology/topology/{topology_id}/', + path=f"ws/network-topology/topology/{topology_id}/", headers=[ ( - b'cookie', - f'sessionid={session_id}'.encode('ascii'), + b"cookie", + f"sessionid={session_id}".encode("ascii"), ) ], ) @@ -52,7 +52,7 @@ async def _assert_connection_superuser(self, admin_client, conn=True): async def _assert_connection_org_manager(self, client, view_perm=True, conn=True): org = await database_sync_to_async(self._create_org)() test_user = await database_sync_to_async(self._create_user)( - username='test-user-org-manager', email='test@orgmanger.com', is_staff=True + username="test-user-org-manager", email="test@orgmanger.com", is_staff=True ) await database_sync_to_async(self._create_org_user)( is_admin=True, user=test_user, organization=org @@ -60,7 +60,7 @@ async def _assert_connection_org_manager(self, client, view_perm=True, conn=True t = await database_sync_to_async(self._create_topology)(organization=org) if view_perm: topology_view_permission = await Permission.objects.aget( - codename='view_topology' + codename="view_topology" ) await test_user.user_permissions.aadd(topology_view_permission) await database_sync_to_async(client.force_login)(test_user) @@ -70,7 +70,7 @@ async def _assert_connection_org_manager(self, client, view_perm=True, conn=True await communicator.disconnect() async def _assert_connection_unauth_user(self, client, conn=True): - client.cookies['sessionid'] = 'unauthenticated_user' + client.cookies["sessionid"] = "unauthenticated_user" org = await database_sync_to_async(self._create_org)() t = await database_sync_to_async(self._create_topology)(organization=org) communicator = await self._get_communicator(client, t.pk) @@ -90,15 +90,15 @@ async def test_consumer_connection_org_manager_without_view_perm(self, client): async def test_consumer_connection_unauth_user(self, client): await self._assert_connection_unauth_user(client, conn=False) - @patch.object(app_settings, 'TOPOLOGY_API_AUTH_REQUIRED', False) + @patch.object(app_settings, "TOPOLOGY_API_AUTH_REQUIRED", False) async def test_consumer_connection_auth_disabled_superuser(self, admin_client): await self._assert_connection_superuser(admin_client) - @patch.object(app_settings, 'TOPOLOGY_API_AUTH_REQUIRED', False) + @patch.object(app_settings, "TOPOLOGY_API_AUTH_REQUIRED", False) async def test_consumer_connection_auth_disabled_org_manager(self, client): await self._assert_connection_org_manager(client) - @patch.object(app_settings, 'TOPOLOGY_API_AUTH_REQUIRED', False) + @patch.object(app_settings, "TOPOLOGY_API_AUTH_REQUIRED", False) async def test_consumer_connection_auth_disabled_unauth_user(self, client): await self._assert_connection_unauth_user(client) @@ -123,20 +123,20 @@ async def test_node_topology_update(self, admin_user, admin_client): ) response = await communicator.receive_json_from() expected_response = await database_sync_to_async(topo.json)() - assert response['topology'] is not None - assert response['topology'] == expected_response - node.label = 'test' + assert response["topology"] is not None + assert response["topology"] == expected_response + node.label = "test" await database_sync_to_async(node.full_clean)() await database_sync_to_async(node.save)() expected_response = await database_sync_to_async(topo.json)() response = await communicator.receive_json_from() - assert response['topology'] is not None - assert response['topology'] == expected_response + assert response["topology"] is not None + assert response["topology"] == expected_response await node.adelete() expected_response = await database_sync_to_async(topo.json)() response = await communicator.receive_json_from() - assert response['topology'] is not None - assert response['topology'] == expected_response + assert response["topology"] is not None + assert response["topology"] == expected_response await communicator.disconnect() async def test_link_topology_update(self, admin_user, admin_client): @@ -146,11 +146,11 @@ async def test_link_topology_update(self, admin_user, admin_client): connected, _ = await communicator.connect() assert connected is True node1 = await database_sync_to_async(self._create_node)( - topology=topo, label='node-0', organization=org + topology=topo, label="node-0", organization=org ) response = await communicator.receive_json_from() node2 = await database_sync_to_async(self._create_node)( - topology=topo, label='node-1', organization=org + topology=topo, label="node-1", organization=org ) response = await communicator.receive_json_from() link = await database_sync_to_async(self._create_link)( @@ -158,20 +158,20 @@ async def test_link_topology_update(self, admin_user, admin_client): ) response = await communicator.receive_json_from() expected_response = await database_sync_to_async(topo.json)() - assert response['topology'] is not None - assert response['topology'] == expected_response - link.status = 'down' + assert response["topology"] is not None + assert response["topology"] == expected_response + link.status = "down" await database_sync_to_async(link.full_clean)() await link.asave() expected_response = await database_sync_to_async(topo.json)() response = await communicator.receive_json_from() - assert response['topology'] is not None - assert response['topology'] == expected_response + assert response["topology"] is not None + assert response["topology"] == expected_response await link.adelete() expected_response = await database_sync_to_async(topo.json)() response = await communicator.receive_json_from() - assert response['topology'] is not None - assert response['topology'] == expected_response + assert response["topology"] is not None + assert response["topology"] == expected_response await communicator.disconnect() async def test_topology_properties_update(self, admin_user, admin_client): @@ -180,11 +180,11 @@ async def test_topology_properties_update(self, admin_user, admin_client): communicator = await self._get_communicator(admin_client, topo.pk) connected, _ = await communicator.connect() assert connected is True - topo.name = 'test' + topo.name = "test" await database_sync_to_async(topo.full_clean)() await topo.asave() expected_response = await database_sync_to_async(topo.json)() response = await communicator.receive_json_from() - assert response['topology'] is not None - assert response['topology'] == expected_response + assert response["topology"] is not None + assert response["topology"] == expected_response await communicator.disconnect() diff --git a/openwisp_network_topology/tests/utils.py b/openwisp_network_topology/tests/utils.py index 7ca8f993..6140ce42 100644 --- a/openwisp_network_topology/tests/utils.py +++ b/openwisp_network_topology/tests/utils.py @@ -5,12 +5,12 @@ from openwisp_utils.tests import TimeLoggingTestRunner -Organization = swapper.load_model('openwisp_users', 'Organization') +Organization = swapper.load_model("openwisp_users", "Organization") class CreateOrgMixin(object): def _create_org(self, **kwargs): - options = dict(name='test-organization') + options = dict(name="test-organization") options.update(kwargs) org = Organization(**options) org.save() @@ -20,15 +20,15 @@ def _create_org(self, **kwargs): class CreateGraphObjectsMixin(object): def _create_topology(self, **kwargs): options = dict( - label='TestNetwork', - parser='netdiff.OlsrParser', - strategy='fetch', - url='http://127.0.0.1:9090', - protocol='OLSR', - version='0.8', - metric='ETX', - created='2017-07-10T20:02:52.483Z', - modified='2015-07-14T20:02:52.483Z', + label="TestNetwork", + parser="netdiff.OlsrParser", + strategy="fetch", + url="http://127.0.0.1:9090", + protocol="OLSR", + version="0.8", + metric="ETX", + created="2017-07-10T20:02:52.483Z", + modified="2015-07-14T20:02:52.483Z", ) options.update(kwargs) t = self.topology_model(**options) @@ -38,10 +38,10 @@ def _create_topology(self, **kwargs): def _create_node(self, **kwargs): options = dict( - label='TestNode', - addresses=['192.168.0.1'], - created='2017-07-10T20:02:52.483Z', - modified='2017-07-14T20:02:52.483Z', + label="TestNode", + addresses=["192.168.0.1"], + created="2017-07-10T20:02:52.483Z", + modified="2017-07-14T20:02:52.483Z", properties={}, ) options.update(kwargs) @@ -51,7 +51,7 @@ def _create_node(self, **kwargs): return n def _create_link(self, **kwargs): - options = dict(cost='1', cost_text='one', properties={}) + options = dict(cost="1", cost_text="one", properties={}) options.update(kwargs) link = self.link_model(**options) link.full_clean() diff --git a/openwisp_network_topology/urls.py b/openwisp_network_topology/urls.py index cc673180..ecf8e96b 100644 --- a/openwisp_network_topology/urls.py +++ b/openwisp_network_topology/urls.py @@ -4,7 +4,7 @@ from .visualizer import urls as visualizer_urls urlpatterns = [ - path('accounts/', include('openwisp_users.accounts.urls')), - path('api/v1/', include(api)), - path('topology/', include(visualizer_urls)), + path("accounts/", include("openwisp_users.accounts.urls")), + path("api/v1/", include(api)), + path("topology/", include(visualizer_urls)), ] diff --git a/openwisp_network_topology/utils.py b/openwisp_network_topology/utils.py index e28801a4..78ce9000 100644 --- a/openwisp_network_topology/utils.py +++ b/openwisp_network_topology/utils.py @@ -16,15 +16,15 @@ def print_info(message): # pragma no cover """ print info message if calling from management command ``update_all`` """ - if 'update_topology' in sys.argv: - print('{0}\n'.format(message)) + if "update_topology" in sys.argv: + print("{0}\n".format(message)) def get_object_or_404(model, pk, **kwargs): """ retrieves topology with specified arguments or raises 404 """ - kwargs.update({'pk': pk, 'published': True}) + kwargs.update({"pk": pk, "published": True}) try: return get_obj_or_404(model, **kwargs) except ValidationError: @@ -37,36 +37,36 @@ def get_api_urls(views_module): """ urls = [ path( - 'network-topology/topology/', + "network-topology/topology/", views_module.network_collection, - name='network_collection', + name="network_collection", ), path( - 'network-topology/topology//', + "network-topology/topology//", views_module.network_graph, - name='network_graph', + name="network_graph", ), path( - 'network-topology/topology//history/', + "network-topology/topology//history/", views_module.network_graph_history, - name='network_graph_history', + name="network_graph_history", ), path( - 'network-topology/topology//receive/', + "network-topology/topology//receive/", views_module.receive_topology, - name='receive_topology', + name="receive_topology", ), - path('network-topology/node/', views_module.node_list, name='node_list'), + path("network-topology/node/", views_module.node_list, name="node_list"), path( - 'network-topology/node//', + "network-topology/node//", views_module.node_detail, - name='node_detail', + name="node_detail", ), - path('network-topology/link/', views_module.link_list, name='link_list'), + path("network-topology/link/", views_module.link_list, name="link_list"), path( - 'network-topology/link//', + "network-topology/link//", views_module.link_detail, - name='link_detail', + name="link_detail", ), ] return urls @@ -77,11 +77,11 @@ def get_visualizer_urls(views_module): used by third party apps to reduce boilerplate """ urls = [ - path('', views_module.topology_list, name='topology_list'), + path("", views_module.topology_list, name="topology_list"), re_path( - r'^topology/(?P[^/]+)/$', + r"^topology/(?P[^/]+)/$", views_module.topology_detail, - name='topology_detail', + name="topology_detail", ), ] return urls diff --git a/openwisp_network_topology/visualizer/__init__.py b/openwisp_network_topology/visualizer/__init__.py index 4045c0fd..194a8829 100644 --- a/openwisp_network_topology/visualizer/__init__.py +++ b/openwisp_network_topology/visualizer/__init__.py @@ -5,18 +5,18 @@ class GraphVisualizerUrls: def get_graph_urls(self, request, pk): - graph_path = reverse('network_graph', urlconf=TOPOLOGY_API_URLCONF, args=[pk]) + graph_path = reverse("network_graph", urlconf=TOPOLOGY_API_URLCONF, args=[pk]) history_path = reverse( - 'network_graph_history', urlconf=TOPOLOGY_API_URLCONF, args=[pk] + "network_graph_history", urlconf=TOPOLOGY_API_URLCONF, args=[pk] ) if TOPOLOGY_API_BASEURL: - graph_url = '{}{}'.format(TOPOLOGY_API_BASEURL, graph_path) - history_url = '{}{}'.format(TOPOLOGY_API_BASEURL, history_path) + graph_url = "{}{}".format(TOPOLOGY_API_BASEURL, graph_path) + history_url = "{}{}".format(TOPOLOGY_API_BASEURL, history_path) else: - graph_url = '{0}://{1}{2}'.format( + graph_url = "{0}://{1}{2}".format( request.scheme, request.get_host(), graph_path ) - history_url = '{0}://{1}{2}'.format( + history_url = "{0}://{1}{2}".format( request.scheme, request.get_host(), history_path ) return graph_url, history_url diff --git a/openwisp_network_topology/visualizer/views.py b/openwisp_network_topology/visualizer/views.py index f11f15ba..741e8b16 100644 --- a/openwisp_network_topology/visualizer/views.py +++ b/openwisp_network_topology/visualizer/views.py @@ -8,7 +8,7 @@ from ..utils import get_object_or_404 from . import GraphVisualizerUrls -Topology = swapper.load_model('topology', 'Topology') +Topology = swapper.load_model("topology", "Topology") class TopologyListView(View): @@ -24,8 +24,8 @@ def get(self, request): topologies = topologies.filter(organization__in=user.organizations_managed) return render( request, - 'netjsongraph/list.html', - {'topologies': topologies, 'VISUALIZER_CSS': VISUALIZER_CSS}, + "netjsongraph/list.html", + {"topologies": topologies, "VISUALIZER_CSS": VISUALIZER_CSS}, ) @@ -41,19 +41,19 @@ def get(self, request, pk): return auth_perm if not user.is_manager(topology.organization): detail = _( - 'User is not a manager of the organization to ' - 'which the requested resource belongs.' + "User is not a manager of the organization to " + "which the requested resource belongs." ) return HttpResponseForbidden(detail) graph_url, history_url = self.get_graph_urls(request, pk) return render( request, - 'netjsongraph/detail.html', + "netjsongraph/detail.html", { - 'topology': topology, - 'graph_url': graph_url, - 'history_url': history_url, - 'VISUALIZER_CSS': VISUALIZER_CSS, + "topology": topology, + "graph_url": graph_url, + "history_url": history_url, + "VISUALIZER_CSS": VISUALIZER_CSS, }, ) @@ -62,13 +62,13 @@ def check_auth_perm(request): user = request.user app_label = Topology._meta.app_label.lower() model = Topology._meta.object_name.lower() - change_perm = f'{app_label}.change_{model}' - view_perm = f'{app_label}.view_{model}' + change_perm = f"{app_label}.change_{model}" + view_perm = f"{app_label}.view_{model}" if not user.is_authenticated: - return HttpResponseForbidden(_('Authentication credentials were not provided.')) + return HttpResponseForbidden(_("Authentication credentials were not provided.")) if not (user.has_perm(change_perm) or user.has_perm(view_perm)): return HttpResponseForbidden( - _('You do not have permission to perform this action.') + _("You do not have permission to perform this action.") ) diff --git a/runtests.py b/runtests.py index 4ca53d15..4b7be7e2 100755 --- a/runtests.py +++ b/runtests.py @@ -3,27 +3,27 @@ import os import sys -sys.path.insert(0, 'tests') -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.settings') +sys.path.insert(0, "tests") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openwisp2.settings") -if __name__ == '__main__': +if __name__ == "__main__": import pytest from django.core.management import execute_from_command_line args = sys.argv - args.insert(1, 'test') - if not os.environ.get('SAMPLE_APP', False): - args.insert(2, 'openwisp_network_topology') - args.insert(3, 'openwisp_network_topology.integrations.device') + args.insert(1, "test") + if not os.environ.get("SAMPLE_APP", False): + args.insert(2, "openwisp_network_topology") + args.insert(3, "openwisp_network_topology.integrations.device") else: - args.insert(2, 'openwisp2') - if os.environ.get('WIFI_MESH', False): - args.extend(['--tag', 'wifi_mesh']) + args.insert(2, "openwisp2") + if os.environ.get("WIFI_MESH", False): + args.extend(["--tag", "wifi_mesh"]) else: - args.extend(['--exclude-tag', 'wifi_mesh']) + args.extend(["--exclude-tag", "wifi_mesh"]) execute_from_command_line(args) sys.exit( pytest.main( - [os.path.join('openwisp_network_topology', 'tests', 'test_websockets.py')] + [os.path.join("openwisp_network_topology", "tests", "test_websockets.py")] ) ) diff --git a/setup.py b/setup.py index a7b873f1..500368e4 100644 --- a/setup.py +++ b/setup.py @@ -9,13 +9,13 @@ def get_install_requires(): parse requirements.txt, ignore links, exclude comments """ requirements = [] - for line in open('requirements.txt').readlines(): + for line in open("requirements.txt").readlines(): # skip to next iteration if comment or empty line if ( - line.startswith('#') - or line == '' - or line.startswith('http') - or line.startswith('git') + line.startswith("#") + or line == "" + or line.startswith("http") + or line.startswith("git") ): continue # add line to requirements @@ -24,30 +24,30 @@ def get_install_requires(): setup( - name='openwisp-network-topology', + name="openwisp-network-topology", version=get_version(), - license='BSD-3-Clause', - author='OpenWISP', - author_email='support@openwisp.io', - description='OpenWISP Network Topology', - long_description=open('README.rst').read(), - url='http://openwisp.org', - download_url='https://github.com/openwisp/openwisp-network-topology/releases', - platforms=['Platform Independent'], - keywords=['django', 'netjson', 'openwrt', 'networking', 'openwisp'], - packages=find_packages(exclude=['tests*', 'docs*']), + license="BSD-3-Clause", + author="OpenWISP", + author_email="support@openwisp.io", + description="OpenWISP Network Topology", + long_description=open("README.rst").read(), + url="http://openwisp.org", + download_url="https://github.com/openwisp/openwisp-network-topology/releases", + platforms=["Platform Independent"], + keywords=["django", "netjson", "openwrt", "networking", "openwisp"], + packages=find_packages(exclude=["tests*", "docs*"]), include_package_data=True, zip_safe=False, install_requires=get_install_requires(), classifiers=[ - 'Development Status :: 5 - Production/Stable ', - 'Environment :: Web Environment', - 'Topic :: Internet :: WWW/HTTP', - 'Topic :: System :: Networking', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Framework :: Django', - 'Programming Language :: Python :: 3', + "Development Status :: 5 - Production/Stable ", + "Environment :: Web Environment", + "Topic :: Internet :: WWW/HTTP", + "Topic :: System :: Networking", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Framework :: Django", + "Programming Language :: Python :: 3", ], ) diff --git a/tests/manage.py b/tests/manage.py index be1b9ff3..33876c7b 100755 --- a/tests/manage.py +++ b/tests/manage.py @@ -2,8 +2,8 @@ import os import sys -if __name__ == '__main__': - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.settings') +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openwisp2.settings") from django.core.management import execute_from_command_line diff --git a/tests/openwisp2/__init__.py b/tests/openwisp2/__init__.py index cd042640..370372af 100644 --- a/tests/openwisp2/__init__.py +++ b/tests/openwisp2/__init__.py @@ -1,3 +1,3 @@ from .celery import app as celery_app -__all__ = ['celery_app'] +__all__ = ["celery_app"] diff --git a/tests/openwisp2/asgi.py b/tests/openwisp2/asgi.py index 18b6e0c6..4ef2e996 100644 --- a/tests/openwisp2/asgi.py +++ b/tests/openwisp2/asgi.py @@ -3,7 +3,7 @@ from django.conf import settings from django.core.asgi import get_asgi_application -if 'openwisp_controller.geo' in settings.INSTALLED_APPS: +if "openwisp_controller.geo" in settings.INSTALLED_APPS: from openwisp_controller.routing import get_routes as get_controller_routes else: from openwisp_notifications.websockets.routing import ( @@ -22,12 +22,12 @@ def get_controller_routes(): application = ProtocolTypeRouter( { - 'websocket': AuthMiddlewareStack( + "websocket": AuthMiddlewareStack( URLRouter( openwisp_network_topology.routing.websocket_urlpatterns + get_controller_routes() ) ), - 'http': get_asgi_application(), + "http": get_asgi_application(), } ) diff --git a/tests/openwisp2/celery.py b/tests/openwisp2/celery.py index e3c1425d..311028e8 100644 --- a/tests/openwisp2/celery.py +++ b/tests/openwisp2/celery.py @@ -2,8 +2,8 @@ from celery import Celery -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openwisp2.settings") -app = Celery('openwisp2') -app.config_from_object('django.conf:settings', namespace='CELERY') +app = Celery("openwisp2") +app.config_from_object("django.conf:settings", namespace="CELERY") app.autodiscover_tasks() diff --git a/tests/openwisp2/sample_integration_device/apps.py b/tests/openwisp2/sample_integration_device/apps.py index 12618cfc..7e63ecc6 100644 --- a/tests/openwisp2/sample_integration_device/apps.py +++ b/tests/openwisp2/sample_integration_device/apps.py @@ -4,8 +4,8 @@ class OpenwispTopologyDeviceConfig(BaseAppConfig): - name = 'openwisp2.sample_integration_device' - label = 'sample_integration_device' + name = "openwisp2.sample_integration_device" + label = "sample_integration_device" del BaseAppConfig diff --git a/tests/openwisp2/sample_integration_device/celery.py b/tests/openwisp2/sample_integration_device/celery.py index e3c1425d..311028e8 100644 --- a/tests/openwisp2/sample_integration_device/celery.py +++ b/tests/openwisp2/sample_integration_device/celery.py @@ -2,8 +2,8 @@ from celery import Celery -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openwisp2.settings") -app = Celery('openwisp2') -app.config_from_object('django.conf:settings', namespace='CELERY') +app = Celery("openwisp2") +app.config_from_object("django.conf:settings", namespace="CELERY") app.autodiscover_tasks() diff --git a/tests/openwisp2/sample_integration_device/migrations/0001_initial.py b/tests/openwisp2/sample_integration_device/migrations/0001_initial.py index e8e2b589..a32b62ba 100644 --- a/tests/openwisp2/sample_integration_device/migrations/0001_initial.py +++ b/tests/openwisp2/sample_integration_device/migrations/0001_initial.py @@ -12,15 +12,15 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.CONFIG_DEVICE_MODEL), - ('sample_network_topology', '0002_json_properties'), + ("sample_network_topology", "0002_json_properties"), ] operations = [ migrations.CreateModel( - name='DeviceNode', + name="DeviceNode", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -28,29 +28,29 @@ class Migration(migrations.Migration): serialize=False, ), ), - ('is_test', models.BooleanField(default=True)), + ("is_test", models.BooleanField(default=True)), ( - 'device', + "device", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, to=settings.CONFIG_DEVICE_MODEL, ), ), ( - 'node', + "node", models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, - to='sample_network_topology.Node', + to="sample_network_topology.Node", ), ), ], - options={'abstract': False, 'unique_together': {('node', 'device')}}, + options={"abstract": False, "unique_together": {("node", "device")}}, ), migrations.CreateModel( - name='WifiMesh', + name="WifiMesh", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -58,10 +58,10 @@ class Migration(migrations.Migration): serialize=False, ), ), - ('mesh_id', models.CharField(max_length=32, verbose_name='Mesh ID')), - ('is_test', models.BooleanField(default=True)), + ("mesh_id", models.CharField(max_length=32, verbose_name="Mesh ID")), + ("is_test", models.BooleanField(default=True)), ( - 'topology', + "topology", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, to=settings.TOPOLOGY_TOPOLOGY_MODEL, @@ -69,7 +69,7 @@ class Migration(migrations.Migration): ), ], options={ - 'abstract': False, + "abstract": False, }, ), ] diff --git a/tests/openwisp2/sample_integration_device/tests.py b/tests/openwisp2/sample_integration_device/tests.py index 18b4d749..15152a52 100644 --- a/tests/openwisp2/sample_integration_device/tests.py +++ b/tests/openwisp2/sample_integration_device/tests.py @@ -10,7 +10,7 @@ TestMonitoringIntegration as BaseTestMonitoringIntegration, ) -DeviceNode = swapper.load_model('topology_device', 'DeviceNode') +DeviceNode = swapper.load_model("topology_device", "DeviceNode") class TestControllerIntegration(BaseTestControllerIntegration): @@ -23,8 +23,8 @@ class TestMonitoringIntegration(BaseTestMonitoringIntegration): class TestAdmin(BaseTestAdmin): - module = 'openwisp2.sample_network_topology' - app_label = 'sample_network_topology' + module = "openwisp2.sample_network_topology" + app_label = "sample_network_topology" del BaseTestControllerIntegration diff --git a/tests/openwisp2/sample_network_topology/apps.py b/tests/openwisp2/sample_network_topology/apps.py index f7301cc1..6c621235 100644 --- a/tests/openwisp2/sample_network_topology/apps.py +++ b/tests/openwisp2/sample_network_topology/apps.py @@ -2,8 +2,8 @@ class SampleNetworkTopologyConfig(OpenwispNetworkTopologyConfig): - name = 'openwisp2.sample_network_topology' - label = 'sample_network_topology' + name = "openwisp2.sample_network_topology" + label = "sample_network_topology" del OpenwispNetworkTopologyConfig diff --git a/tests/openwisp2/sample_network_topology/migrations/0001_initial.py b/tests/openwisp2/sample_network_topology/migrations/0001_initial.py index ca4c422d..5a90f691 100644 --- a/tests/openwisp2/sample_network_topology/migrations/0001_initial.py +++ b/tests/openwisp2/sample_network_topology/migrations/0001_initial.py @@ -22,15 +22,15 @@ class Migration(migrations.Migration): initial = True dependencies = [ - dependency(*split(settings.AUTH_USER_MODEL), version='0004_default_groups'), + dependency(*split(settings.AUTH_USER_MODEL), version="0004_default_groups"), ] operations = [ migrations.CreateModel( - name='Topology', + name="Topology", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -39,139 +39,139 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('label', models.CharField(max_length=64, verbose_name='label')), + ("label", models.CharField(max_length=64, verbose_name="label")), ( - 'parser', + "parser", models.CharField( choices=[ - ('netdiff.OlsrParser', 'OLSRd (txtinfo/jsoninfo)'), + ("netdiff.OlsrParser", "OLSRd (txtinfo/jsoninfo)"), ( - 'netdiff.BatmanParser', - 'batman-advanced (jsondoc/txtinfo)', + "netdiff.BatmanParser", + "batman-advanced (jsondoc/txtinfo)", ), - ('netdiff.BmxParser', 'BMX6 (q6m)'), - ('netdiff.NetJsonParser', 'NetJSON NetworkGraph'), - ('netdiff.CnmlParser', 'CNML 1.0'), - ('netdiff.OpenvpnParser', 'OpenVPN'), - ('netdiff.WireguardParser', 'Wireguard'), - ('netdiff.ZeroTierParser', 'ZeroTier'), + ("netdiff.BmxParser", "BMX6 (q6m)"), + ("netdiff.NetJsonParser", "NetJSON NetworkGraph"), + ("netdiff.CnmlParser", "CNML 1.0"), + ("netdiff.OpenvpnParser", "OpenVPN"), + ("netdiff.WireguardParser", "Wireguard"), + ("netdiff.ZeroTierParser", "ZeroTier"), ], - help_text='Select topology format', + help_text="Select topology format", max_length=128, - verbose_name='format', + verbose_name="format", ), ), ( - 'strategy', + "strategy", models.CharField( - choices=[('fetch', 'FETCH'), ('receive', 'RECEIVE')], + choices=[("fetch", "FETCH"), ("receive", "RECEIVE")], db_index=True, - default='fetch', + default="fetch", max_length=16, - verbose_name='strategy', + verbose_name="strategy", ), ), ( - 'url', + "url", models.URLField( blank=True, - help_text='Topology data will be fetched from this URL (FETCH strategy)', - verbose_name='url', + help_text="Topology data will be fetched from this URL (FETCH strategy)", + verbose_name="url", ), ), ( - 'key', + "key", openwisp_utils.base.KeyField( blank=True, default=openwisp_utils.utils.get_random_key, - help_text='key needed to update topology from nodes ', + help_text="key needed to update topology from nodes ", max_length=64, validators=[ django.core.validators.RegexValidator( - re.compile('^[^\\s/\\.]+$'), - code='invalid', - message='This value must not contain spaces, dots or slashes.', + re.compile("^[^\\s/\\.]+$"), + code="invalid", + message="This value must not contain spaces, dots or slashes.", ) ], - verbose_name='key', + verbose_name="key", ), ), ( - 'expiration_time', + "expiration_time", models.PositiveIntegerField( default=0, help_text=( '"Expiration Time" in seconds: setting this to 0 will immediately ' - 'mark missing links as down; a value higher than 0 will delay marking ' + "mark missing links as down; a value higher than 0 will delay marking " 'missing links as down until the "modified" field of a link is ' 'older than "Expiration Time"' ), - verbose_name='expiration time', + verbose_name="expiration time", ), ), ( - 'published', + "published", models.BooleanField( default=True, help_text="Unpublished topologies won't be updated or shown in the visualizer", - verbose_name='published', + verbose_name="published", ), ), ( - 'protocol', + "protocol", models.CharField( - blank=True, max_length=64, verbose_name='protocol' + blank=True, max_length=64, verbose_name="protocol" ), ), ( - 'version', - models.CharField(blank=True, max_length=24, verbose_name='version'), + "version", + models.CharField(blank=True, max_length=24, verbose_name="version"), ), ( - 'revision', + "revision", models.CharField( - blank=True, max_length=64, verbose_name='revision' + blank=True, max_length=64, verbose_name="revision" ), ), ( - 'metric', - models.CharField(blank=True, max_length=24, verbose_name='metric'), + "metric", + models.CharField(blank=True, max_length=24, verbose_name="metric"), ), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ( - 'organization', + "organization", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ], - options={'verbose_name_plural': 'topologies', 'abstract': False}, + options={"verbose_name_plural": "topologies", "abstract": False}, bases=(openwisp_users.mixins.ValidateOrgMixin, models.Model), ), migrations.CreateModel( - name='Snapshot', + name="Snapshot", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -180,40 +180,40 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('data', models.TextField()), - ('date', models.DateField(auto_now=True)), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("data", models.TextField()), + ("date", models.DateField(auto_now=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ( - 'topology', + "topology", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to='sample_network_topology.Topology', + to="sample_network_topology.Topology", ), ), ], - options={'verbose_name_plural': 'snapshots', 'abstract': False}, + options={"verbose_name_plural": "snapshots", "abstract": False}, bases=(openwisp_users.mixins.ValidateOrgMixin, models.Model), ), migrations.CreateModel( - name='Node', + name="Node", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -222,59 +222,59 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('label', models.CharField(blank=True, max_length=64)), - ('addresses', jsonfield.fields.JSONField(default=[])), + ("label", models.CharField(blank=True, max_length=64)), + ("addresses", jsonfield.fields.JSONField(default=[])), ( - 'properties', + "properties", jsonfield.fields.JSONField( blank=True, default=dict, - dump_kwargs={'indent': 4}, - load_kwargs={'object_pairs_hook': collections.OrderedDict}, + dump_kwargs={"indent": 4}, + load_kwargs={"object_pairs_hook": collections.OrderedDict}, ), ), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ( - 'organization', + "organization", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ( - 'topology', + "topology", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to='sample_network_topology.Topology', + to="sample_network_topology.Topology", ), ), ], - options={'abstract': False}, + options={"abstract": False}, bases=(openwisp_users.mixins.ValidateOrgMixin, models.Model), ), migrations.CreateModel( - name='Link', + name="Link", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -283,78 +283,78 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('cost', models.FloatField()), - ('cost_text', models.CharField(blank=True, max_length=24)), + ("cost", models.FloatField()), + ("cost_text", models.CharField(blank=True, max_length=24)), ( - 'status', + "status", model_utils.fields.StatusField( - choices=[('up', 'up'), ('down', 'down')], - default='up', + choices=[("up", "up"), ("down", "down")], + default="up", max_length=100, no_check_for_status=True, ), ), ( - 'properties', + "properties", jsonfield.fields.JSONField( blank=True, default=dict, - dump_kwargs={'indent': 4}, - load_kwargs={'object_pairs_hook': collections.OrderedDict}, + dump_kwargs={"indent": 4}, + load_kwargs={"object_pairs_hook": collections.OrderedDict}, ), ), - ('status_changed', models.DateTimeField(auto_now=True)), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("status_changed", models.DateTimeField(auto_now=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ( - 'organization', + "organization", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ( - 'source', + "source", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='source_link_set', - to='sample_network_topology.Node', + related_name="source_link_set", + to="sample_network_topology.Node", ), ), ( - 'target', + "target", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='target_link_set', - to='sample_network_topology.Node', + related_name="target_link_set", + to="sample_network_topology.Node", ), ), ( - 'topology', + "topology", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to='sample_network_topology.Topology', + to="sample_network_topology.Topology", ), ), ], - options={'abstract': False}, + options={"abstract": False}, bases=(openwisp_users.mixins.ValidateOrgMixin, models.Model), ), ] diff --git a/tests/openwisp2/sample_network_topology/migrations/0002_json_properties.py b/tests/openwisp2/sample_network_topology/migrations/0002_json_properties.py index 8c9a9a54..60576bd1 100644 --- a/tests/openwisp2/sample_network_topology/migrations/0002_json_properties.py +++ b/tests/openwisp2/sample_network_topology/migrations/0002_json_properties.py @@ -8,38 +8,38 @@ class Migration(migrations.Migration): - dependencies = [('sample_network_topology', '0001_initial')] + dependencies = [("sample_network_topology", "0001_initial")] operations = [ migrations.AlterField( - model_name='link', - name='properties', + model_name="link", + name="properties", field=jsonfield.fields.JSONField( blank=True, default=dict, dump_kwargs={ - 'cls': rest_framework.utils.encoders.JSONEncoder, - 'indent': 4, + "cls": rest_framework.utils.encoders.JSONEncoder, + "indent": 4, }, - load_kwargs={'object_pairs_hook': collections.OrderedDict}, + load_kwargs={"object_pairs_hook": collections.OrderedDict}, ), ), migrations.AlterField( - model_name='node', - name='addresses', + model_name="node", + name="addresses", field=jsonfield.fields.JSONField(blank=True, default=[]), ), migrations.AlterField( - model_name='node', - name='properties', + model_name="node", + name="properties", field=jsonfield.fields.JSONField( blank=True, default=dict, dump_kwargs={ - 'cls': rest_framework.utils.encoders.JSONEncoder, - 'indent': 4, + "cls": rest_framework.utils.encoders.JSONEncoder, + "indent": 4, }, - load_kwargs={'object_pairs_hook': collections.OrderedDict}, + load_kwargs={"object_pairs_hook": collections.OrderedDict}, ), ), ] diff --git a/tests/openwisp2/sample_network_topology/migrations/0003_add_user_defined_properties_field.py b/tests/openwisp2/sample_network_topology/migrations/0003_add_user_defined_properties_field.py index 4665ada4..cee12e3f 100644 --- a/tests/openwisp2/sample_network_topology/migrations/0003_add_user_defined_properties_field.py +++ b/tests/openwisp2/sample_network_topology/migrations/0003_add_user_defined_properties_field.py @@ -8,37 +8,37 @@ class Migration(migrations.Migration): - dependencies = [('sample_network_topology', '0002_json_properties')] + dependencies = [("sample_network_topology", "0002_json_properties")] operations = [ migrations.AddField( - model_name='link', - name='user_properties', + model_name="link", + name="user_properties", field=jsonfield.fields.JSONField( blank=True, default=dict, dump_kwargs={ - 'cls': rest_framework.utils.encoders.JSONEncoder, - 'indent': 4, + "cls": rest_framework.utils.encoders.JSONEncoder, + "indent": 4, }, - help_text='If you need to add additional data to this link use this field', - load_kwargs={'object_pairs_hook': collections.OrderedDict}, - verbose_name='user defined properties', + help_text="If you need to add additional data to this link use this field", + load_kwargs={"object_pairs_hook": collections.OrderedDict}, + verbose_name="user defined properties", ), ), migrations.AddField( - model_name='node', - name='user_properties', + model_name="node", + name="user_properties", field=jsonfield.fields.JSONField( blank=True, default=dict, dump_kwargs={ - 'cls': rest_framework.utils.encoders.JSONEncoder, - 'indent': 4, + "cls": rest_framework.utils.encoders.JSONEncoder, + "indent": 4, }, - help_text='If you need to add additional data to this node use this field', - load_kwargs={'object_pairs_hook': collections.OrderedDict}, - verbose_name='user defined properties', + help_text="If you need to add additional data to this node use this field", + load_kwargs={"object_pairs_hook": collections.OrderedDict}, + verbose_name="user defined properties", ), ), ] diff --git a/tests/openwisp2/sample_network_topology/migrations/0004_default_groups.py b/tests/openwisp2/sample_network_topology/migrations/0004_default_groups.py index 91c47520..b245f579 100644 --- a/tests/openwisp2/sample_network_topology/migrations/0004_default_groups.py +++ b/tests/openwisp2/sample_network_topology/migrations/0004_default_groups.py @@ -14,14 +14,14 @@ def create_default_permissions(apps, schema_editor): def assign_permissions_to_groups(apps, schema_editor): create_default_permissions(apps, schema_editor) - operators_and_admin_can_change = ['link', 'node'] - operators_read_only_admins_manage = ['topology'] - manage_operations = ['add', 'change', 'delete'] - Group = apps.get_model('openwisp_users', 'Group') + operators_and_admin_can_change = ["link", "node"] + operators_read_only_admins_manage = ["topology"] + manage_operations = ["add", "change", "delete"] + Group = apps.get_model("openwisp_users", "Group") try: - admin = Group.objects.get(name='Administrator') - operator = Group.objects.get(name='Operator') + admin = Group.objects.get(name="Administrator") + operator = Group.objects.get(name="Operator") # consider failures custom cases # that do not have to be dealt with except Group.DoesNotExist: @@ -30,7 +30,7 @@ def assign_permissions_to_groups(apps, schema_editor): for model_name in operators_and_admin_can_change: for operation in manage_operations: permission = Permission.objects.get( - codename='{}_{}'.format(operation, model_name) + codename="{}_{}".format(operation, model_name) ) admin.permissions.add(permission.pk) operator.permissions.add(permission.pk) @@ -50,8 +50,8 @@ def assign_permissions_to_groups(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - dependency(*split(settings.AUTH_USER_MODEL), version='0004_default_groups'), - ('sample_network_topology', '0003_add_user_defined_properties_field'), + dependency(*split(settings.AUTH_USER_MODEL), version="0004_default_groups"), + ("sample_network_topology", "0003_add_user_defined_properties_field"), ] operations = [ migrations.RunPython( diff --git a/tests/openwisp2/sample_network_topology/tests.py b/tests/openwisp2/sample_network_topology/tests.py index 876a65fa..c690da89 100644 --- a/tests/openwisp2/sample_network_topology/tests.py +++ b/tests/openwisp2/sample_network_topology/tests.py @@ -24,12 +24,12 @@ class TestAdmin(BaseTestAdmin): - module = 'openwisp2.sample_network_topology' - app_label = 'sample_network_topology' + module = "openwisp2.sample_network_topology" + app_label = "sample_network_topology" class TestMultitenantAdmin(BaseTestMultitenantAdmin): - app_label = 'sample_network_topology' + app_label = "sample_network_topology" class TestApi(BaseTestApi): diff --git a/tests/openwisp2/settings.py b/tests/openwisp2/settings.py index 0872aacb..a1f0b6ac 100644 --- a/tests/openwisp2/settings.py +++ b/tests/openwisp2/settings.py @@ -2,172 +2,172 @@ import sys BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -TESTING = 'test' in sys.argv +TESTING = "test" in sys.argv DEBUG = True -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = ["*"] DATABASES = { - 'default': { - 'ENGINE': 'openwisp_utils.db.backends.spatialite', - 'NAME': os.path.join(BASE_DIR, 'openwisp_network_topology.db'), + "default": { + "ENGINE": "openwisp_utils.db.backends.spatialite", + "NAME": os.path.join(BASE_DIR, "openwisp_network_topology.db"), } } -SECRET_KEY = '@q4z-^s=mv59#o=uutv4*m=h@)ik4%zp1)-k^_(!_7*x_&+ze$' +SECRET_KEY = "@q4z-^s=mv59#o=uutv4*m=h@)ik4%zp1)-k^_(!_7*x_&+ze$" INSTALLED_APPS = [ - 'daphne', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'openwisp_utils.admin_theme', + "daphne", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "openwisp_utils.admin_theme", # all-auth - 'django.contrib.sites', - 'openwisp_users.accounts', - 'allauth', - 'allauth.account', - 'allauth.socialaccount', + "django.contrib.sites", + "openwisp_users.accounts", + "allauth", + "allauth.account", + "allauth.socialaccount", # controller (needed to test integration) - 'openwisp_controller.pki', - 'openwisp_controller.config', - 'openwisp_controller.connection', - 'openwisp_controller.geo', - 'openwisp_notifications', - 'openwisp_ipam', - 'reversion', - 'sortedm2m', - 'flat_json_widget', + "openwisp_controller.pki", + "openwisp_controller.config", + "openwisp_controller.connection", + "openwisp_controller.geo", + "openwisp_notifications", + "openwisp_ipam", + "reversion", + "sortedm2m", + "flat_json_widget", # network topology - 'openwisp_network_topology', - 'openwisp_network_topology.integrations.device', - 'openwisp_users', + "openwisp_network_topology", + "openwisp_network_topology.integrations.device", + "openwisp_users", # admin - 'import_export', - 'admin_auto_filters', - 'django.contrib.admin', - 'django.forms', + "import_export", + "admin_auto_filters", + "django.contrib.admin", + "django.forms", # rest framework - 'rest_framework', - 'drf_yasg', - 'django_filters', - 'rest_framework.authtoken', - 'django_extensions', + "rest_framework", + "drf_yasg", + "django_filters", + "rest_framework.authtoken", + "django_extensions", # 'debug_toolbar', # channels - 'channels', + "channels", ] -EXTENDED_APPS = ['django_x509', 'django_loci'] +EXTENDED_APPS = ["django_x509", "django_loci"] -AUTH_USER_MODEL = 'openwisp_users.User' +AUTH_USER_MODEL = "openwisp_users.User" SITE_ID = 1 STATICFILES_FINDERS = [ - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'openwisp_utils.staticfiles.DependencyFinder', + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "openwisp_utils.staticfiles.DependencyFinder", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'allauth.account.middleware.AccountMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "allauth.account.middleware.AccountMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", # 'debug_toolbar.middleware.DebugToolbarMiddleware', ] # INTERNAL_IPS = ['127.0.0.1'] -ROOT_URLCONF = 'openwisp2.urls' +ROOT_URLCONF = "openwisp2.urls" -ASGI_APPLICATION = 'openwisp2.asgi.application' +ASGI_APPLICATION = "openwisp2.asgi.application" -CHANNEL_LAYERS = {'default': {'BACKEND': 'channels.layers.InMemoryChannelLayer'}} -FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' +CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}} +FORM_RENDERER = "django.forms.renderers.TemplatesSetting" -LANGUAGE_CODE = 'en-gb' -TIME_ZONE = 'UTC' +LANGUAGE_CODE = "en-gb" +TIME_ZONE = "UTC" USE_I18N = True USE_L10N = True USE_TZ = True -STATIC_URL = '/static/' +STATIC_URL = "/static/" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'OPTIONS': { - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - 'openwisp_utils.loaders.DependencyLoader', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "OPTIONS": { + "loaders": [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", + "openwisp_utils.loaders.DependencyLoader", ], - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'openwisp_utils.admin_theme.context_processor.menu_groups', + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "openwisp_utils.admin_theme.context_processor.menu_groups", ], }, } ] LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'filters': { - 'require_debug_false': {'()': 'django.utils.log.RequireDebugFalse'}, - 'require_debug_true': {'()': 'django.utils.log.RequireDebugTrue'}, + "version": 1, + "disable_existing_loggers": False, + "filters": { + "require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}, + "require_debug_true": {"()": "django.utils.log.RequireDebugTrue"}, }, - 'formatters': { - 'simple': {'format': '[%(levelname)s] %(message)s'}, - 'verbose': { - 'format': '\n\n[%(levelname)s %(asctime)s] module: %(module)s, process: %(process)d, thread: %(thread)d\n%(message)s' + "formatters": { + "simple": {"format": "[%(levelname)s] %(message)s"}, + "verbose": { + "format": "\n\n[%(levelname)s %(asctime)s] module: %(module)s, process: %(process)d, thread: %(thread)d\n%(message)s" }, }, - 'handlers': { - 'console': { - 'level': 'DEBUG', - 'class': 'logging.StreamHandler', - 'filters': ['require_debug_true'], - 'formatter': 'simple', + "handlers": { + "console": { + "level": "DEBUG", + "class": "logging.StreamHandler", + "filters": ["require_debug_true"], + "formatter": "simple", }, - 'mail_admins': { - 'level': 'ERROR', - 'filters': ['require_debug_false'], - 'class': 'django.utils.log.AdminEmailHandler', + "mail_admins": { + "level": "ERROR", + "filters": ["require_debug_false"], + "class": "django.utils.log.AdminEmailHandler", }, - 'main_log': { - 'level': 'ERROR', - 'class': 'logging.handlers.RotatingFileHandler', - 'formatter': 'verbose', - 'filename': os.path.join(BASE_DIR, 'error.log'), - 'maxBytes': 5242880.0, - 'backupCount': 3, + "main_log": { + "level": "ERROR", + "class": "logging.handlers.RotatingFileHandler", + "formatter": "verbose", + "filename": os.path.join(BASE_DIR, "error.log"), + "maxBytes": 5242880.0, + "backupCount": 3, }, }, - 'root': {'level': 'INFO', 'handlers': ['main_log', 'console', 'mail_admins']}, - 'loggers': {'py.warnings': {'handlers': ['console']}}, + "root": {"level": "INFO", "handlers": ["main_log", "console", "mail_admins"]}, + "loggers": {"py.warnings": {"handlers": ["console"]}}, } -TEST_RUNNER = 'openwisp_network_topology.tests.utils.LoggingDisabledTestRunner' +TEST_RUNNER = "openwisp_network_topology.tests.utils.LoggingDisabledTestRunner" # during development only -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -LOGIN_REDIRECT_URL = 'admin:index' +LOGIN_REDIRECT_URL = "admin:index" ACCOUNT_LOGOUT_REDIRECT_URL = LOGIN_REDIRECT_URL OPENWISP_ORGANIZATION_USER_ADMIN = True @@ -179,63 +179,63 @@ # and should not be used in a production environment CELERY_TASK_ALWAYS_EAGER = True CELERY_TASK_EAGER_PROPAGATES = True -CELERY_BROKER_URL = 'memory://' +CELERY_BROKER_URL = "memory://" -if not TESTING and any(['shell' in sys.argv, 'shell_plus' in sys.argv]): +if not TESTING and any(["shell" in sys.argv, "shell_plus" in sys.argv]): LOGGING.update( { - 'loggers': { - 'django.db.backends': { - 'handlers': ['console'], - 'level': 'DEBUG', - 'propagate': False, + "loggers": { + "django.db.backends": { + "handlers": ["console"], + "level": "DEBUG", + "propagate": False, } } } ) # Avoid adding unnecessary dependency to speedup tests. -if not TESTING or (TESTING and os.environ.get('WIFI_MESH', False)): +if not TESTING or (TESTING and os.environ.get("WIFI_MESH", False)): OPENWISP_NETWORK_TOPOLOGY_WIFI_MESH_INTEGRATION = True - openwisp_ipam_index = INSTALLED_APPS.index('openwisp_ipam') - INSTALLED_APPS.insert(openwisp_ipam_index, 'leaflet') - INSTALLED_APPS.insert(openwisp_ipam_index, 'nested_admin') - INSTALLED_APPS.insert(openwisp_ipam_index, 'openwisp_monitoring.check') - INSTALLED_APPS.insert(openwisp_ipam_index, 'openwisp_monitoring.device') - INSTALLED_APPS.insert(openwisp_ipam_index, 'openwisp_monitoring.monitoring') + openwisp_ipam_index = INSTALLED_APPS.index("openwisp_ipam") + INSTALLED_APPS.insert(openwisp_ipam_index, "leaflet") + INSTALLED_APPS.insert(openwisp_ipam_index, "nested_admin") + INSTALLED_APPS.insert(openwisp_ipam_index, "openwisp_monitoring.check") + INSTALLED_APPS.insert(openwisp_ipam_index, "openwisp_monitoring.device") + INSTALLED_APPS.insert(openwisp_ipam_index, "openwisp_monitoring.monitoring") TIMESERIES_DATABASE = { - 'BACKEND': 'openwisp_monitoring.db.backends.influxdb', - 'USER': 'openwisp', - 'PASSWORD': 'openwisp', - 'NAME': 'openwisp2', - 'HOST': os.getenv('INFLUXDB_HOST', 'localhost'), - 'PORT': '8086', + "BACKEND": "openwisp_monitoring.db.backends.influxdb", + "USER": "openwisp", + "PASSWORD": "openwisp", + "NAME": "openwisp2", + "HOST": os.getenv("INFLUXDB_HOST", "localhost"), + "PORT": "8086", } OPENWISP_MONITORING_MAC_VENDOR_DETECTION = False CACHES = { - 'default': { - 'BACKEND': 'django_redis.cache.RedisCache', - 'LOCATION': 'redis://localhost/9', - 'OPTIONS': { - 'CLIENT_CLASS': 'django_redis.client.DefaultClient', + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://localhost/9", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", }, } } -if os.environ.get('SAMPLE_APP', False): - INSTALLED_APPS.remove('openwisp_network_topology') - INSTALLED_APPS.remove('openwisp_network_topology.integrations.device') - EXTENDED_APPS.append('openwisp_network_topology') +if os.environ.get("SAMPLE_APP", False): + INSTALLED_APPS.remove("openwisp_network_topology") + INSTALLED_APPS.remove("openwisp_network_topology.integrations.device") + EXTENDED_APPS.append("openwisp_network_topology") INSTALLED_APPS += [ - 'openwisp2.sample_network_topology', - 'openwisp2.sample_integration_device', + "openwisp2.sample_network_topology", + "openwisp2.sample_integration_device", ] - TOPOLOGY_LINK_MODEL = 'sample_network_topology.Link' - TOPOLOGY_NODE_MODEL = 'sample_network_topology.Node' - TOPOLOGY_SNAPSHOT_MODEL = 'sample_network_topology.Snapshot' - TOPOLOGY_TOPOLOGY_MODEL = 'sample_network_topology.Topology' - TOPOLOGY_DEVICE_DEVICENODE_MODEL = 'sample_integration_device.DeviceNode' - TOPOLOGY_DEVICE_WIFIMESH_MODEL = 'sample_integration_device.WifiMesh' + TOPOLOGY_LINK_MODEL = "sample_network_topology.Link" + TOPOLOGY_NODE_MODEL = "sample_network_topology.Node" + TOPOLOGY_SNAPSHOT_MODEL = "sample_network_topology.Snapshot" + TOPOLOGY_TOPOLOGY_MODEL = "sample_network_topology.Topology" + TOPOLOGY_DEVICE_DEVICENODE_MODEL = "sample_integration_device.DeviceNode" + TOPOLOGY_DEVICE_WIFIMESH_MODEL = "sample_integration_device.WifiMesh" # local settings must be imported before test runner otherwise they'll be ignored try: diff --git a/tests/openwisp2/urls.py b/tests/openwisp2/urls.py index b387351a..70f23b33 100644 --- a/tests/openwisp2/urls.py +++ b/tests/openwisp2/urls.py @@ -9,27 +9,27 @@ urlpatterns = [] -if os.environ.get('SAMPLE_APP', False): +if os.environ.get("SAMPLE_APP", False): from openwisp_network_topology.utils import get_visualizer_urls from .sample_network_topology.visualizer import views - urlpatterns += [path('topology/', include(get_visualizer_urls(views)))] + urlpatterns += [path("topology/", include(get_visualizer_urls(views)))] urlpatterns += [ - path('', include('openwisp_network_topology.urls')), + path("", include("openwisp_network_topology.urls")), # needed to test integrations - path('', include('openwisp_controller.urls')), - path('admin/', admin.site.urls), - path('api/v1/', include('openwisp_utils.api.urls')), - path('api/v1/', include(get_api_urls())), + path("", include("openwisp_controller.urls")), + path("admin/", admin.site.urls), + path("api/v1/", include("openwisp_utils.api.urls")), + path("api/v1/", include(get_api_urls())), ] urlpatterns += staticfiles_urlpatterns() -if 'openwisp_monitoring.monitoring' in settings.INSTALLED_APPS: - urlpatterns.append(path('', include('openwisp_monitoring.urls'))) +if "openwisp_monitoring.monitoring" in settings.INSTALLED_APPS: + urlpatterns.append(path("", include("openwisp_monitoring.urls"))) -if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS: +if settings.DEBUG and "debug_toolbar" in settings.INSTALLED_APPS: import debug_toolbar - urlpatterns += [path('__debug__/', include(debug_toolbar.urls))] + urlpatterns += [path("__debug__/", include(debug_toolbar.urls))]