Skip to content

Commit

Permalink
added functional sync feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Reimann, Timo committed Sep 14, 2021
1 parent e6ca38a commit 2ab5485
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 44 deletions.
1 change: 0 additions & 1 deletion develop/ACS.json

This file was deleted.

187 changes: 151 additions & 36 deletions netbox_cisco_support/management/commands/sync_eox_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import django.utils.text

from django.core.management.base import BaseCommand, CommandError
from datetime import datetime
from requests import api
from dcim.models import Manufacturer
from dcim.models import Device, DeviceType
from netbox_cisco_support.models import CiscoDeviceTypeSupport


class Command(BaseCommand):
Expand All @@ -22,30 +24,127 @@ def add_arguments(self, parser):
)

def update_device_type_eox_data(self, pid, eox_data):
print(eox_data['EOXRecord'][0]['EOXInputValue'])

end_of_sale_date = eox_data['EOXRecord'][0]['EndOfSaleDate']['value']
end_of_sw_maintenance_releases = eox_data['EOXRecord'][0]['EndOfSWMaintenanceReleases']['value']
end_of_security_vul_support_date = eox_data['EOXRecord'][0]['EndOfSecurityVulSupportDate']['value']
end_of_routine_failure_analysis_date = eox_data['EOXRecord'][0]['EndOfRoutineFailureAnalysisDate']['value']
end_of_service_contract_renewal = eox_data['EOXRecord'][0]['EndOfServiceContractRenewal']['value']
last_date_of_support = eox_data['EOXRecord'][0]['LastDateOfSupport']['value']
end_of_svc_attach_date = eox_data['EOXRecord'][0]['EndOfSvcAttachDate']['value']

print("End Of Sale: %s" % end_of_sale_date)
print("End Of SW Maintenance: %s" % end_of_sw_maintenance_releases)
print("End Of Security Vuln Support: %s" % end_of_security_vul_support_date)
print("End Of Routine Failure Analysis: %s" % end_of_routine_failure_analysis_date)
print("End Of Service Contract Renewal: %s" % end_of_service_contract_renewal)
print("Last Date Of Support: %s" % last_date_of_support)
print("End Of Service Attach Date: %s" % end_of_svc_attach_date)
# Get the device type object for the supplied PID
dt = DeviceType.objects.get(part_number=pid)

return
# Check if CiscoDeviceTypeSupport record already exists
try:
dts = CiscoDeviceTypeSupport.objects.get(device_type=dt)
# If not, create a new one for this Device Type
except CiscoDeviceTypeSupport.DoesNotExist:
dts = CiscoDeviceTypeSupport(device_type=dt)

def get_product_ids(self, manufacturer):
i = 0
product_ids = []
# Only save if something has changed
value_changed = False

try:
# Check if JSON contains EndOfSaleDate with value field
if not eox_data["EOXRecord"][0]["EndOfSaleDate"]["value"]:
self.stdout.write(self.style.NOTICE("%s has no end_of_sale_date" % pid))
else:
end_of_sale_date_string = eox_data["EOXRecord"][0]["EndOfSaleDate"]["value"]
# Cast this value to datetime.date object
end_of_sale_date = datetime.strptime(end_of_sale_date_string, '%Y-%m-%d').date()
self.stdout.write(self.style.SUCCESS("%s - end_of_sale_date: %s" % (pid, end_of_sale_date)))

# Check if CiscoDeviceTypeSupport end_of_sale_date differs from JSON EndOfSaleDate, update if true
if dts.end_of_sale_date != end_of_sale_date:
dts.end_of_sale_date = end_of_sale_date
value_changed = True
# Do nothing when JSON field does not exist
except KeyError:
self.stdout.write(self.style.NOTICE("%s has no end_of_sale_date" % pid))

try:
if not eox_data["EOXRecord"][0]["EndOfSWMaintenanceReleases"]["value"]:
self.stdout.write(self.style.NOTICE("%s has no end_of_sw_maintenance_releases" % pid))
else:
end_of_sw_maintenance_releases_string = eox_data["EOXRecord"][0]["EndOfSaleDate"]["value"]
end_of_sw_maintenance_releases = datetime.strptime(end_of_sw_maintenance_releases_string, '%Y-%m-%d').date()
self.stdout.write(self.style.SUCCESS("%s - end_of_sw_maintenance_releases: %s" % (pid, end_of_sw_maintenance_releases)))

if dts.end_of_sw_maintenance_releases != end_of_sw_maintenance_releases:
dts.end_of_sw_maintenance_releases = end_of_sw_maintenance_releases
value_changed = True
except KeyError:
self.stdout.write(self.style.NOTICE("%s has no end_of_sw_maintenance_releases" % pid))

try:
if not eox_data["EOXRecord"][0]["EndOfSecurityVulSupportDate"]["value"]:
self.stdout.write(self.style.NOTICE("%s has no end_of_security_vul_support_date" % pid))
else:
end_of_security_vul_support_date_string = eox_data["EOXRecord"][0]["EndOfSecurityVulSupportDate"]["value"]
end_of_security_vul_support_date = datetime.strptime(end_of_security_vul_support_date_string, '%Y-%m-%d').date()
self.stdout.write(self.style.SUCCESS("%s - end_of_security_vul_support_date: %s" % (pid, end_of_security_vul_support_date)))

if dts.end_of_security_vul_support_date != end_of_security_vul_support_date:
dts.end_of_security_vul_support_date = end_of_security_vul_support_date
value_changed = True
except KeyError:
self.stdout.write(self.style.NOTICE("%s has no end_of_security_vul_support_date" % pid))

try:
if not eox_data["EOXRecord"][0]["EndOfRoutineFailureAnalysisDate"]["value"]:
self.stdout.write(self.style.NOTICE("%s has no end_of_routine_failure_analysis_date" % pid))
else:
end_of_routine_failure_analysis_date_string = eox_data["EOXRecord"][0]["EndOfRoutineFailureAnalysisDate"]["value"]
end_of_routine_failure_analysis_date = datetime.strptime(end_of_routine_failure_analysis_date_string, '%Y-%m-%d').date()
self.stdout.write(self.style.SUCCESS("%s - end_of_routine_failure_analysis_date: %s" % (pid, end_of_routine_failure_analysis_date)))

if dts.end_of_routine_failure_analysis_date != end_of_routine_failure_analysis_date:
dts.end_of_routine_failure_analysis_date = end_of_routine_failure_analysis_date
value_changed = True
except KeyError:
self.stdout.write(self.style.NOTICE("%s has no end_of_routine_failure_analysis_date" % pid))

try:
if not eox_data["EOXRecord"][0]["EndOfServiceContractRenewal"]["value"]:
self.stdout.write(self.style.NOTICE("%s has no end_of_service_contract_renewal" % pid))
else:
end_of_service_contract_renewal_string = eox_data["EOXRecord"][0]["EndOfServiceContractRenewal"]["value"]
end_of_service_contract_renewal = datetime.strptime(end_of_service_contract_renewal_string, '%Y-%m-%d').date()
self.stdout.write(self.style.SUCCESS("%s - end_of_service_contract_renewal: %s" % (pid, end_of_service_contract_renewal)))

if dts.end_of_service_contract_renewal != end_of_service_contract_renewal:
dts.end_of_service_contract_renewal = end_of_service_contract_renewal
value_changed = True
except KeyError:
self.stdout.write(self.style.NOTICE("%s has no end_of_service_contract_renewal" % pid))

try:
if not eox_data["EOXRecord"][0]["LastDateOfSupport"]["value"]:
self.stdout.write(self.style.NOTICE("%s has no last_date_of_support" % pid))
else:
last_date_of_support_string = eox_data["EOXRecord"][0]["EndOfSaleDate"]["value"]
last_date_of_support = datetime.strptime(last_date_of_support_string, '%Y-%m-%d').date()
self.stdout.write(self.style.SUCCESS("%s - last_date_of_support: %s" % (pid, last_date_of_support)))

if dts.last_date_of_support != last_date_of_support:
dts.last_date_of_support = last_date_of_support
value_changed = True
except KeyError:
self.stdout.write(self.style.NOTICE("%s has no last_date_of_support" % pid))

try:
if not eox_data["EOXRecord"][0]["EndOfSvcAttachDate"]["value"]:
self.stdout.write(self.style.NOTICE("%s has no end_of_svc_attach_date" % pid))
else:
end_of_svc_attach_date_string = eox_data["EOXRecord"][0]["EndOfSvcAttachDate"]["value"]
end_of_svc_attach_date = datetime.strptime(end_of_svc_attach_date_string, '%Y-%m-%d').date()
self.stdout.write(self.style.SUCCESS("%s - end_of_svc_attach_date: %s" % (pid, end_of_svc_attach_date)))

if dts.end_of_svc_attach_date != end_of_svc_attach_date:
dts.end_of_svc_attach_date = end_of_svc_attach_date
value_changed = True
except KeyError:
self.stdout.write(self.style.NOTICE("%s has no end_of_svc_attach_date" % pid))

if value_changed:
dts.save()

return

def get_device_types(self, manufacturer):
# trying to get the right manufacturer for this plugin
try:
m = Manufacturer.objects.get(name=manufacturer)
Expand All @@ -60,6 +159,14 @@ def get_product_ids(self, manufacturer):
except DeviceType.DoesNotExist:
raise CommandError('Manufacturer "%s" has no Device Types' % m)

return dt

def get_product_ids(self, manufacturer):
i = 0
product_ids = []

dt = self.get_device_types(manufacturer)

for device_type in dt:

# Skip if the device type has no valid part number. Part numbers must match the exact Cisco Base PID
Expand All @@ -71,21 +178,25 @@ def get_product_ids(self, manufacturer):
self.stdout.write(self.style.SUCCESS('Found device type "%s" with Part Number "%s"' % (device_type, device_type.part_number)))
product_ids.append(device_type.part_number)

# trying to get all devices and its serial numbers for this device type (for contract data)
try:
d = Device.objects.filter(device_type=device_type)
except Device.DoesNotExist:
raise CommandError('Device "%s" does not exist' % d)
return product_ids

def get_serial_numbers(self, dtype):
# trying to get all devices and its serial numbers for this device type (for contract data)
try:
d = Device.objects.filter(device_type=dtype)

for device in d:

# Skip if the device has no valid serial number.
if not device.serial:
self.stdout.write(self.style.WARNING('Found device "%s" WITHOUT Serial Number - SKIPPING' % (device)))
continue

self.stdout.write(self.style.SUCCESS('%s: Found device "%s" with Serial Number "%s"' % (i, device, device.serial)))
i += 1

return product_ids
# TODO - Add serial number to a list and do something with it rather than displaying.
self.stdout.write(self.style.SUCCESS('Found device "%s" with Serial Number "%s"' % (device, device.serial)))
except Device.DoesNotExist:
raise CommandError('Device with device type "%s" does not exist' % dtype)
return

def logon(self):
token_url = "https://cloudsso.cisco.com/as/token.oauth2"
Expand All @@ -104,16 +215,16 @@ def logon(self):
def handle(self, *args, **kwargs):
product_ids = self.get_product_ids(kwargs['manufacturer'])

# with open('/source/netbox_cisco_support/ACS.json') as json_file:
# data = json.load(json_file)

# print(json.dumps(data, sort_keys=True, indent=4))

# finished collecting data, making API calls
self.stdout.write(self.style.SUCCESS('Gathering data for these PIDs: ' + ', '.join(product_ids)))

api_call_headers = self.logon()

i = 1

for pid in product_ids:
# if i == 10:
# break

url = 'https://api.cisco.com/supporttools/eox/rest/5/EOXByProductID/1/%s?responseencoding=json' % pid
api_call_response = requests.get(url, headers=api_call_headers)
self.stdout.write(self.style.SUCCESS('Call ' + url))
Expand All @@ -126,3 +237,7 @@ def handle(self, *args, **kwargs):
outfile.write(api_call_response.text)

data = json.loads(api_call_response.text)

self.update_device_type_eox_data(pid, data)

i += 1
48 changes: 48 additions & 0 deletions netbox_cisco_support/migrations/0005_auto_20210914_1344.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Django 3.2.6 on 2021-09-14 13:44

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('netbox_cisco_support', '0004_ciscosupport_is_covered'),
]

operations = [
migrations.AlterField(
model_name='ciscodevicetypesupport',
name='end_of_routine_failure_analysis_date',
field=models.DateField(blank=True, null=True),
),
migrations.AlterField(
model_name='ciscodevicetypesupport',
name='end_of_sale_date',
field=models.DateField(blank=True, null=True),
),
migrations.AlterField(
model_name='ciscodevicetypesupport',
name='end_of_security_vul_support_date',
field=models.DateField(blank=True, null=True),
),
migrations.AlterField(
model_name='ciscodevicetypesupport',
name='end_of_service_contract_renewal',
field=models.DateField(blank=True, null=True),
),
migrations.AlterField(
model_name='ciscodevicetypesupport',
name='end_of_svc_attach_date',
field=models.DateField(blank=True, null=True),
),
migrations.AlterField(
model_name='ciscodevicetypesupport',
name='end_of_sw_maintenance_releases',
field=models.DateField(blank=True, null=True),
),
migrations.AlterField(
model_name='ciscodevicetypesupport',
name='last_date_of_support',
field=models.DateField(blank=True, null=True),
),
]
28 changes: 21 additions & 7 deletions netbox_cisco_support/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,53 @@ def __str__(self):

end_of_sale_date = models.DateField(
help_text='Last date to order the requested product through Cisco point-of-sale mechanisms. The product is no '
'longer for sale after this date.'
'longer for sale after this date.',
blank=True,
null=True
)

end_of_sw_maintenance_releases = models.DateField(
help_text='Last date that Cisco Engineering might release any software maintenance releases or bug fixes to '
'the software product. After this date, Cisco Engineering no longer develops, repairs, maintains, or '
'tests the product software.'
'tests the product software.',
blank=True,
null=True
)

end_of_security_vul_support_date = models.DateField(
help_text='Last date that Cisco Engineering may release a planned maintenance release or scheduled software '
'remedy for a security vulnerability issue.'
'remedy for a security vulnerability issue.',
blank=True,
null=True
)

end_of_routine_failure_analysis_date = models.DateField(
help_text='Last date Cisco might perform a routine failure analysis to determine the root cause of an '
'engineering-related or manufacturing-related issue.'
'engineering-related or manufacturing-related issue.',
blank=True,
null=True
)

end_of_service_contract_renewal = models.DateField(
help_text='Last date to extend or renew a service contract for the product. The extension or renewal period '
'cannot extend beyond the last date of support.'
'cannot extend beyond the last date of support.',
blank=True,
null=True
)

last_date_of_support = models.DateField(
help_text='Last date to receive service and support for the product. After this date, all support services for '
'the product are unavailable, and the product becomes obsolete.'
'the product are unavailable, and the product becomes obsolete.',
blank=True,
null=True
)

end_of_svc_attach_date = models.DateField(
help_text='Last date to order a new service-and-support contract or add the equipment and/or software to an '
'existing service-and-support contract for equipment and software that is not covered by a '
'service-and-support contract.'
'service-and-support contract.',
blank=True,
null=True
)


Expand Down
3 changes: 3 additions & 0 deletions netbox_cisco_support/templatetags/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ def expires_next_year(value):

@register.filter(is_safe=True)
def expiration_class(value):
if not value:
return

if is_expired(value):
return mark_safe('class="danger"')
elif expires_next_year(value):
Expand Down

0 comments on commit 2ab5485

Please sign in to comment.