netbox-atlassian 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- netbox_atlassian/__init__.py +73 -0
- netbox_atlassian/atlassian_client.py +346 -0
- netbox_atlassian/forms.py +101 -0
- netbox_atlassian/navigation.py +22 -0
- netbox_atlassian/templates/netbox_atlassian/device_tab.html +178 -0
- netbox_atlassian/templates/netbox_atlassian/settings.html +274 -0
- netbox_atlassian/templates/netbox_atlassian/vm_tab.html +178 -0
- netbox_atlassian/urls.py +17 -0
- netbox_atlassian/views.py +306 -0
- netbox_atlassian-0.1.0.dist-info/METADATA +186 -0
- netbox_atlassian-0.1.0.dist-info/RECORD +13 -0
- netbox_atlassian-0.1.0.dist-info/WHEEL +5 -0
- netbox_atlassian-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Views for NetBox Atlassian Plugin
|
|
3
|
+
|
|
4
|
+
Registers custom tabs on Device detail views to show Jira issues and Confluence pages.
|
|
5
|
+
Provides settings configuration UI.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
from dcim.models import Device
|
|
11
|
+
from django.conf import settings
|
|
12
|
+
from django.contrib import messages
|
|
13
|
+
from django.http import JsonResponse
|
|
14
|
+
from django.shortcuts import render
|
|
15
|
+
from django.views import View
|
|
16
|
+
from netbox.views import generic
|
|
17
|
+
from utilities.views import ViewTab, register_model_view
|
|
18
|
+
from virtualization.models import VirtualMachine
|
|
19
|
+
|
|
20
|
+
from .atlassian_client import get_client
|
|
21
|
+
from .forms import AtlassianSettingsForm
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_device_attribute(device, attribute_path: str):
|
|
25
|
+
"""
|
|
26
|
+
Get a device attribute by dot-separated path.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
device: Device instance
|
|
30
|
+
attribute_path: Dot-separated attribute path (e.g., "role.name", "primary_ip4.address")
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Attribute value or None if not found
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
value = device
|
|
37
|
+
for part in attribute_path.split("."):
|
|
38
|
+
value = getattr(value, part, None)
|
|
39
|
+
if value is None:
|
|
40
|
+
return None
|
|
41
|
+
# Convert to string for IP addresses
|
|
42
|
+
if hasattr(value, "ip"):
|
|
43
|
+
return str(value.ip)
|
|
44
|
+
return str(value) if value else None
|
|
45
|
+
except Exception:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_search_terms(device) -> list[str]:
|
|
50
|
+
"""
|
|
51
|
+
Get search terms from device based on configured search fields.
|
|
52
|
+
|
|
53
|
+
Returns list of non-empty values from enabled search fields.
|
|
54
|
+
"""
|
|
55
|
+
config = settings.PLUGINS_CONFIG.get("netbox_atlassian", {})
|
|
56
|
+
search_fields = config.get("search_fields", [])
|
|
57
|
+
|
|
58
|
+
terms = []
|
|
59
|
+
for field in search_fields:
|
|
60
|
+
if not field.get("enabled", True):
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
attribute = field.get("attribute", "")
|
|
64
|
+
if not attribute:
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
value = get_device_attribute(device, attribute)
|
|
68
|
+
if value and value.strip():
|
|
69
|
+
terms.append(value.strip())
|
|
70
|
+
|
|
71
|
+
return terms
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def should_show_atlassian_tab(device) -> bool:
|
|
75
|
+
"""
|
|
76
|
+
Determine if the Atlassian tab should be visible for this device.
|
|
77
|
+
|
|
78
|
+
Shows tab if:
|
|
79
|
+
- Device has at least one searchable field value
|
|
80
|
+
- Device type matches configured filters (if any)
|
|
81
|
+
"""
|
|
82
|
+
config = settings.PLUGINS_CONFIG.get("netbox_atlassian", {})
|
|
83
|
+
device_types = config.get("device_types", [])
|
|
84
|
+
|
|
85
|
+
# Check device type filter if configured
|
|
86
|
+
if device_types and device.device_type:
|
|
87
|
+
manufacturer_slug = device.device_type.manufacturer.slug.lower() if device.device_type.manufacturer else ""
|
|
88
|
+
manufacturer_name = device.device_type.manufacturer.name.lower() if device.device_type.manufacturer else ""
|
|
89
|
+
|
|
90
|
+
matches = False
|
|
91
|
+
for pattern in device_types:
|
|
92
|
+
try:
|
|
93
|
+
if re.search(pattern.lower(), manufacturer_slug) or re.search(pattern.lower(), manufacturer_name):
|
|
94
|
+
matches = True
|
|
95
|
+
break
|
|
96
|
+
except re.error:
|
|
97
|
+
if pattern.lower() in manufacturer_slug or pattern.lower() in manufacturer_name:
|
|
98
|
+
matches = True
|
|
99
|
+
break
|
|
100
|
+
|
|
101
|
+
if not matches:
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
# Check if we have any search terms
|
|
105
|
+
terms = get_search_terms(device)
|
|
106
|
+
return len(terms) > 0
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@register_model_view(Device, name="atlassian", path="atlassian")
|
|
110
|
+
class DeviceAtlassianView(generic.ObjectView):
|
|
111
|
+
"""Display Jira issues and Confluence pages for a Device."""
|
|
112
|
+
|
|
113
|
+
queryset = Device.objects.all()
|
|
114
|
+
template_name = "netbox_atlassian/device_tab.html"
|
|
115
|
+
|
|
116
|
+
tab = ViewTab(
|
|
117
|
+
label="Atlassian",
|
|
118
|
+
weight=9100,
|
|
119
|
+
permission="dcim.view_device",
|
|
120
|
+
hide_if_empty=False,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def get(self, request, pk):
|
|
124
|
+
"""Handle GET request for the Atlassian tab."""
|
|
125
|
+
device = Device.objects.get(pk=pk)
|
|
126
|
+
|
|
127
|
+
config = settings.PLUGINS_CONFIG.get("netbox_atlassian", {})
|
|
128
|
+
client = get_client()
|
|
129
|
+
|
|
130
|
+
# Get search terms from device
|
|
131
|
+
search_terms = get_search_terms(device)
|
|
132
|
+
|
|
133
|
+
# Get configured search fields for display
|
|
134
|
+
search_fields = config.get("search_fields", [])
|
|
135
|
+
enabled_fields = [f for f in search_fields if f.get("enabled", True)]
|
|
136
|
+
|
|
137
|
+
# Search Jira and Confluence
|
|
138
|
+
jira_results = {"issues": [], "total": 0, "error": None}
|
|
139
|
+
confluence_results = {"pages": [], "total": 0, "error": None}
|
|
140
|
+
|
|
141
|
+
if search_terms:
|
|
142
|
+
jira_max = config.get("jira_max_results", 10)
|
|
143
|
+
confluence_max = config.get("confluence_max_results", 10)
|
|
144
|
+
|
|
145
|
+
jira_results = client.search_jira(search_terms, max_results=jira_max)
|
|
146
|
+
confluence_results = client.search_confluence(search_terms, max_results=confluence_max)
|
|
147
|
+
|
|
148
|
+
# Get URLs for external links
|
|
149
|
+
jira_url = config.get("jira_url", "").rstrip("/")
|
|
150
|
+
confluence_url = config.get("confluence_url", "").rstrip("/")
|
|
151
|
+
|
|
152
|
+
return render(
|
|
153
|
+
request,
|
|
154
|
+
self.template_name,
|
|
155
|
+
{
|
|
156
|
+
"object": device,
|
|
157
|
+
"tab": self.tab,
|
|
158
|
+
"search_terms": search_terms,
|
|
159
|
+
"enabled_fields": enabled_fields,
|
|
160
|
+
"jira_results": jira_results,
|
|
161
|
+
"confluence_results": confluence_results,
|
|
162
|
+
"jira_url": jira_url,
|
|
163
|
+
"confluence_url": confluence_url,
|
|
164
|
+
"jira_configured": bool(jira_url),
|
|
165
|
+
"confluence_configured": bool(confluence_url),
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@register_model_view(VirtualMachine, name="atlassian", path="atlassian")
|
|
171
|
+
class VirtualMachineAtlassianView(generic.ObjectView):
|
|
172
|
+
"""Display Jira issues and Confluence pages for a VirtualMachine."""
|
|
173
|
+
|
|
174
|
+
queryset = VirtualMachine.objects.all()
|
|
175
|
+
template_name = "netbox_atlassian/vm_tab.html"
|
|
176
|
+
|
|
177
|
+
tab = ViewTab(
|
|
178
|
+
label="Atlassian",
|
|
179
|
+
weight=9100,
|
|
180
|
+
permission="virtualization.view_virtualmachine",
|
|
181
|
+
hide_if_empty=False,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def get(self, request, pk):
|
|
185
|
+
"""Handle GET request for the Atlassian tab."""
|
|
186
|
+
vm = VirtualMachine.objects.get(pk=pk)
|
|
187
|
+
|
|
188
|
+
config = settings.PLUGINS_CONFIG.get("netbox_atlassian", {})
|
|
189
|
+
client = get_client()
|
|
190
|
+
|
|
191
|
+
# For VMs, search by name and primary IP
|
|
192
|
+
search_terms = []
|
|
193
|
+
if vm.name:
|
|
194
|
+
search_terms.append(vm.name)
|
|
195
|
+
if vm.primary_ip4:
|
|
196
|
+
search_terms.append(str(vm.primary_ip4.address.ip))
|
|
197
|
+
|
|
198
|
+
# Get configured search fields for display
|
|
199
|
+
search_fields = config.get("search_fields", [])
|
|
200
|
+
enabled_fields = [{"name": "Name", "attribute": "name", "enabled": True}]
|
|
201
|
+
if vm.primary_ip4:
|
|
202
|
+
enabled_fields.append({"name": "Primary IP", "attribute": "primary_ip4", "enabled": True})
|
|
203
|
+
|
|
204
|
+
# Search Jira and Confluence
|
|
205
|
+
jira_results = {"issues": [], "total": 0, "error": None}
|
|
206
|
+
confluence_results = {"pages": [], "total": 0, "error": None}
|
|
207
|
+
|
|
208
|
+
if search_terms:
|
|
209
|
+
jira_max = config.get("jira_max_results", 10)
|
|
210
|
+
confluence_max = config.get("confluence_max_results", 10)
|
|
211
|
+
|
|
212
|
+
jira_results = client.search_jira(search_terms, max_results=jira_max)
|
|
213
|
+
confluence_results = client.search_confluence(search_terms, max_results=confluence_max)
|
|
214
|
+
|
|
215
|
+
# Get URLs for external links
|
|
216
|
+
jira_url = config.get("jira_url", "").rstrip("/")
|
|
217
|
+
confluence_url = config.get("confluence_url", "").rstrip("/")
|
|
218
|
+
|
|
219
|
+
return render(
|
|
220
|
+
request,
|
|
221
|
+
self.template_name,
|
|
222
|
+
{
|
|
223
|
+
"object": vm,
|
|
224
|
+
"tab": self.tab,
|
|
225
|
+
"search_terms": search_terms,
|
|
226
|
+
"enabled_fields": enabled_fields,
|
|
227
|
+
"jira_results": jira_results,
|
|
228
|
+
"confluence_results": confluence_results,
|
|
229
|
+
"jira_url": jira_url,
|
|
230
|
+
"confluence_url": confluence_url,
|
|
231
|
+
"jira_configured": bool(jira_url),
|
|
232
|
+
"confluence_configured": bool(confluence_url),
|
|
233
|
+
},
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class AtlassianSettingsView(View):
|
|
238
|
+
"""View for configuring Atlassian plugin settings."""
|
|
239
|
+
|
|
240
|
+
template_name = "netbox_atlassian/settings.html"
|
|
241
|
+
|
|
242
|
+
def get_current_config(self):
|
|
243
|
+
"""Get current plugin configuration."""
|
|
244
|
+
return settings.PLUGINS_CONFIG.get("netbox_atlassian", {})
|
|
245
|
+
|
|
246
|
+
def get(self, request):
|
|
247
|
+
"""Display the settings form."""
|
|
248
|
+
config = self.get_current_config()
|
|
249
|
+
form = AtlassianSettingsForm(initial=config)
|
|
250
|
+
|
|
251
|
+
return render(
|
|
252
|
+
request,
|
|
253
|
+
self.template_name,
|
|
254
|
+
{
|
|
255
|
+
"form": form,
|
|
256
|
+
"config": config,
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def post(self, request):
|
|
261
|
+
"""Handle settings form submission."""
|
|
262
|
+
form = AtlassianSettingsForm(request.POST)
|
|
263
|
+
|
|
264
|
+
if form.is_valid():
|
|
265
|
+
messages.warning(
|
|
266
|
+
request,
|
|
267
|
+
"Settings must be configured in NetBox's configuration.py file. "
|
|
268
|
+
"See the README for configuration instructions.",
|
|
269
|
+
)
|
|
270
|
+
else:
|
|
271
|
+
messages.error(request, "Invalid settings provided.")
|
|
272
|
+
|
|
273
|
+
return render(
|
|
274
|
+
request,
|
|
275
|
+
self.template_name,
|
|
276
|
+
{
|
|
277
|
+
"form": form,
|
|
278
|
+
"config": self.get_current_config(),
|
|
279
|
+
},
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class TestJiraConnectionView(View):
|
|
284
|
+
"""Test connection to Jira API."""
|
|
285
|
+
|
|
286
|
+
def post(self, request):
|
|
287
|
+
"""Test the Jira connection and return result."""
|
|
288
|
+
client = get_client()
|
|
289
|
+
success, message = client.test_jira_connection()
|
|
290
|
+
|
|
291
|
+
if success:
|
|
292
|
+
return JsonResponse({"success": True, "message": message})
|
|
293
|
+
return JsonResponse({"success": False, "error": message}, status=400)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class TestConfluenceConnectionView(View):
|
|
297
|
+
"""Test connection to Confluence API."""
|
|
298
|
+
|
|
299
|
+
def post(self, request):
|
|
300
|
+
"""Test the Confluence connection and return result."""
|
|
301
|
+
client = get_client()
|
|
302
|
+
success, message = client.test_confluence_connection()
|
|
303
|
+
|
|
304
|
+
if success:
|
|
305
|
+
return JsonResponse({"success": True, "message": message})
|
|
306
|
+
return JsonResponse({"success": False, "error": message}, status=400)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: netbox-atlassian
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: NetBox plugin to display Jira issues and Confluence pages related to devices
|
|
5
|
+
Author-email: sieteunoseis <jeremy.worden@gmail.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/sieteunoseis/netbox-atlassian
|
|
8
|
+
Project-URL: Repository, https://github.com/sieteunoseis/netbox-atlassian
|
|
9
|
+
Project-URL: Issues, https://github.com/sieteunoseis/netbox-atlassian/issues
|
|
10
|
+
Keywords: netbox,jira,confluence,atlassian,plugin
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Framework :: Django
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: requests>=2.28.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: black; extra == "dev"
|
|
24
|
+
Requires-Dist: flake8; extra == "dev"
|
|
25
|
+
Requires-Dist: isort; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# NetBox Atlassian Plugin
|
|
29
|
+
|
|
30
|
+
Display Jira issues and Confluence pages related to devices in NetBox.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **Device Tab** - Shows Jira issues and Confluence pages mentioning device attributes
|
|
35
|
+
- **VM Tab** - Same functionality for Virtual Machines
|
|
36
|
+
- **Configurable Search Fields** - Search by hostname, serial, asset tag, role, IP, etc.
|
|
37
|
+
- **OR Search Logic** - Finds content matching any configured field
|
|
38
|
+
- **On-Premise Support** - Works with Jira Server/Data Center and Confluence Server/Data Center
|
|
39
|
+
- **Cloud Ready** - Prepared for Atlassian Cloud (future release)
|
|
40
|
+
- **Caching** - Results cached to reduce API calls
|
|
41
|
+
- **Project/Space Filtering** - Limit searches to specific Jira projects or Confluence spaces
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
- NetBox 4.0+
|
|
46
|
+
- Python 3.10+
|
|
47
|
+
- Access to Jira and/or Confluence REST APIs
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install netbox-atlassian
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Add to `configuration.py`:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
PLUGINS = [
|
|
59
|
+
"netbox_atlassian",
|
|
60
|
+
]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
PLUGINS_CONFIG = {
|
|
67
|
+
"netbox_atlassian": {
|
|
68
|
+
# Jira settings (on-prem)
|
|
69
|
+
"jira_url": "https://jira.example.com",
|
|
70
|
+
"jira_username": "api_user",
|
|
71
|
+
"jira_password": "api_token_or_password",
|
|
72
|
+
"jira_verify_ssl": True,
|
|
73
|
+
"jira_projects": [], # Empty = all projects
|
|
74
|
+
|
|
75
|
+
# Confluence settings (on-prem)
|
|
76
|
+
"confluence_url": "https://confluence.example.com",
|
|
77
|
+
"confluence_username": "api_user",
|
|
78
|
+
"confluence_password": "api_token_or_password",
|
|
79
|
+
"confluence_verify_ssl": True,
|
|
80
|
+
"confluence_spaces": [], # Empty = all spaces
|
|
81
|
+
|
|
82
|
+
# Search configuration
|
|
83
|
+
"search_fields": [
|
|
84
|
+
{"name": "Hostname", "attribute": "name", "enabled": True},
|
|
85
|
+
{"name": "Serial", "attribute": "serial", "enabled": True},
|
|
86
|
+
{"name": "Asset Tag", "attribute": "asset_tag", "enabled": False},
|
|
87
|
+
{"name": "Role", "attribute": "role.name", "enabled": False},
|
|
88
|
+
{"name": "Primary IP", "attribute": "primary_ip4.address", "enabled": False},
|
|
89
|
+
],
|
|
90
|
+
|
|
91
|
+
# Results limits
|
|
92
|
+
"jira_max_results": 10,
|
|
93
|
+
"confluence_max_results": 10,
|
|
94
|
+
|
|
95
|
+
# General
|
|
96
|
+
"timeout": 30,
|
|
97
|
+
"cache_timeout": 300,
|
|
98
|
+
"device_types": [], # Filter by manufacturer (empty = all)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Search Fields
|
|
104
|
+
|
|
105
|
+
The `search_fields` configuration defines which device attributes are searched. Searches use **OR logic** - content matching any enabled field will be returned.
|
|
106
|
+
|
|
107
|
+
| Field | Attribute Path | Description |
|
|
108
|
+
|-------|---------------|-------------|
|
|
109
|
+
| Hostname | `name` | Device name |
|
|
110
|
+
| Serial | `serial` | Serial number |
|
|
111
|
+
| Asset Tag | `asset_tag` | Asset tag |
|
|
112
|
+
| Role | `role.name` | Device role name |
|
|
113
|
+
| Primary IP | `primary_ip4.address` | Primary IPv4 address |
|
|
114
|
+
| Site | `site.name` | Site name |
|
|
115
|
+
| Tenant | `tenant.name` | Tenant name |
|
|
116
|
+
|
|
117
|
+
### Custom Fields
|
|
118
|
+
|
|
119
|
+
You can search custom fields using dot notation:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
{"name": "CMDB ID", "attribute": "custom_field_data.cmdb_id", "enabled": True}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Screenshots
|
|
126
|
+
|
|
127
|
+
### Device Tab
|
|
128
|
+
Shows Jira issues and Confluence pages in a split view:
|
|
129
|
+
- Left: Jira issues with key, summary, status, and type
|
|
130
|
+
- Right: Confluence pages with title, space, and breadcrumb
|
|
131
|
+
|
|
132
|
+
### Settings Page
|
|
133
|
+
View current configuration and test connections to Jira/Confluence.
|
|
134
|
+
|
|
135
|
+
## API Endpoints
|
|
136
|
+
|
|
137
|
+
The plugin adds these endpoints:
|
|
138
|
+
|
|
139
|
+
| Endpoint | Method | Description |
|
|
140
|
+
|----------|--------|-------------|
|
|
141
|
+
| `/plugins/atlassian/settings/` | GET | View settings |
|
|
142
|
+
| `/plugins/atlassian/test-jira/` | POST | Test Jira connection |
|
|
143
|
+
| `/plugins/atlassian/test-confluence/` | POST | Test Confluence connection |
|
|
144
|
+
|
|
145
|
+
## Troubleshooting
|
|
146
|
+
|
|
147
|
+
### Connection Errors
|
|
148
|
+
|
|
149
|
+
1. Verify URLs are correct and accessible from NetBox server
|
|
150
|
+
2. Check username/password or API token
|
|
151
|
+
3. For on-prem, ensure `verify_ssl` matches your certificate setup
|
|
152
|
+
4. Check firewall rules allow outbound HTTPS
|
|
153
|
+
|
|
154
|
+
### No Results
|
|
155
|
+
|
|
156
|
+
1. Verify search fields are enabled in configuration
|
|
157
|
+
2. Check that Jira/Confluence content contains the device attributes
|
|
158
|
+
3. Review project/space filters if configured
|
|
159
|
+
4. Check API user has read permissions
|
|
160
|
+
|
|
161
|
+
### Slow Performance
|
|
162
|
+
|
|
163
|
+
1. Increase `cache_timeout` to reduce API calls
|
|
164
|
+
2. Reduce `max_results` values
|
|
165
|
+
3. Use project/space filters to limit search scope
|
|
166
|
+
|
|
167
|
+
## Development
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
git clone https://github.com/sieteunoseis/netbox-atlassian.git
|
|
171
|
+
cd netbox-atlassian
|
|
172
|
+
pip install -e ".[dev]"
|
|
173
|
+
|
|
174
|
+
# Run linting
|
|
175
|
+
black netbox_atlassian/
|
|
176
|
+
isort netbox_atlassian/
|
|
177
|
+
flake8 netbox_atlassian/
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
Apache 2.0
|
|
183
|
+
|
|
184
|
+
## Author
|
|
185
|
+
|
|
186
|
+
sieteunoseis
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
netbox_atlassian/__init__.py,sha256=caHvJRtHTHUx1tRKQyNXzYvMihUJ71fFWkJPqgunTIs,2754
|
|
2
|
+
netbox_atlassian/atlassian_client.py,sha256=oO4maq4AWknbf-HEin72BS30gM7HcRsunC0xv9O9YUI,12859
|
|
3
|
+
netbox_atlassian/forms.py,sha256=b8MKsU3Ou-c0s7Cga_72m7E3UCCliZF-17woybN66dk,3385
|
|
4
|
+
netbox_atlassian/navigation.py,sha256=tpRgS8qESmxm5eEuCOd1UjEVXEDTMM12H4lEaZk1BRw,497
|
|
5
|
+
netbox_atlassian/urls.py,sha256=kIiL-G8wfMd1tdSrnWeWcTuHZMx6CCc5mLr_WjKn8Rs,454
|
|
6
|
+
netbox_atlassian/views.py,sha256=EQlU2hcHNcRfwsD5C73L1X5u82WeB26XqSPcMVxl9S4,10118
|
|
7
|
+
netbox_atlassian/templates/netbox_atlassian/device_tab.html,sha256=Vs1QMlCa6vcWF0HrL1VlV6mHrl9iaCC_R3IsjH1BcJQ,9521
|
|
8
|
+
netbox_atlassian/templates/netbox_atlassian/settings.html,sha256=bmiIzzJ6m1vwT5AOcOrdJ7uD3V31GbKUpT4M6TIP3wE,11709
|
|
9
|
+
netbox_atlassian/templates/netbox_atlassian/vm_tab.html,sha256=Q_Psv-08lyLys_CAF4oJvAUIWcg_2OnkcMFbdnKg8vk,9539
|
|
10
|
+
netbox_atlassian-0.1.0.dist-info/METADATA,sha256=Zw0phWkrxAmDEXe0oySURhi5xLQ7RRBamQcpXqDIlfY,5675
|
|
11
|
+
netbox_atlassian-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
netbox_atlassian-0.1.0.dist-info/top_level.txt,sha256=VaKVLTW0o2SE7896PzRTWUaEeYt8CWsryBXI6Ij4ECg,17
|
|
13
|
+
netbox_atlassian-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
netbox_atlassian
|