diff --git a/netbox-diode-webinar/examples/go/diode-webinar.go b/netbox-diode-webinar/examples/go/diode-webinar.go new file mode 100644 index 0000000..4053f8e --- /dev/null +++ b/netbox-diode-webinar/examples/go/diode-webinar.go @@ -0,0 +1,60 @@ +package main + +import ( + "context" + "log" + + "github.com/netboxlabs/diode-sdk-go/diode" +) + +func main() { + client, err := diode.NewClient( + "grpc:///diode", + "example-app", + "0.1.0", + diode.WithClientID("YOUR_CLIENT_ID"), + diode.WithClientSecret("YOUR_CLIENT_SECRET"), + ) + if err != nil { + log.Fatal(err) + } + + // Create a device + deviceEntity := &diode.Device{ + Name: diode.String("PDU 05"), + DeviceType: &diode.DeviceType{ + Model: diode.String("AP7955"), + Manufacturer: &diode.Manufacturer{ + Name: diode.String("APC"), + }, + }, + Platform: &diode.Platform{ + Name: diode.String("APCOS"), + Manufacturer: &diode.Manufacturer{ + Name: diode.String("APC"), + }, + }, + Site: &diode.Site{ + Name: diode.String("Sunderland"), + }, + Role: &diode.DeviceRole{ + Name: diode.String("Rack PDU"), + }, + Status: diode.String("active"), + } + + entities := []diode.Entity{ + deviceEntity, + } + + resp, err := client.Ingest(context.Background(), entities) + if err != nil { + log.Fatal(err) + } + if resp != nil && resp.Errors != nil { + log.Printf("Errors: %v\n", resp.Errors) + } else { + log.Printf("Success\n") + } + +} diff --git a/netbox-diode-webinar/examples/go/go.mod b/netbox-diode-webinar/examples/go/go.mod new file mode 100644 index 0000000..5406ccb --- /dev/null +++ b/netbox-diode-webinar/examples/go/go.mod @@ -0,0 +1,17 @@ +module netboxlabs.com/diode_webinar + +go 1.23.4 + +toolchain go1.23.10 + +require ( + github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/netboxlabs/diode-sdk-go v1.1.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect +) diff --git a/netbox-diode-webinar/examples/go/go.sum b/netbox-diode-webinar/examples/go/go.sum new file mode 100644 index 0000000..753d80a --- /dev/null +++ b/netbox-diode-webinar/examples/go/go.sum @@ -0,0 +1,18 @@ +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/netboxlabs/diode-sdk-go v1.1.0 h1:+W2VwTFoofgG8CZcwQ1x0EcmDo9yFbyPwAH4gaKbswc= +github.com/netboxlabs/diode-sdk-go v1.1.0/go.mod h1:UNUmF3TJrvPVUutjKqMPpcRPm29myBjgc5c3CjHduis= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/netbox-diode-webinar/examples/python/1_direct.py b/netbox-diode-webinar/examples/python/1_direct.py new file mode 100644 index 0000000..7883b96 --- /dev/null +++ b/netbox-diode-webinar/examples/python/1_direct.py @@ -0,0 +1,59 @@ +''' + ensure DIODE_CLIENT_ID and DIODE_CLIENT_SECRET are set before running this +''' + +from netboxlabs.diode.sdk import DiodeClient +from netboxlabs.diode.sdk.ingester import ( + Device, + DeviceRole, + DeviceType, + Site, + Entity, + CustomFieldValue, +) + + +def generate_entities() -> list[Entity]: + ''' generate a list of entitles for return to Diode ''' + entities = [] + + device = Device( + name="PDU 1", + device_type=DeviceType( + manufacturer="APC", + model="7951" + ), + role=DeviceRole( + name="Rack PDU" + ), + site=Site( + name="Sunderland" + ), + custom_fields={ + 'software_version': CustomFieldValue( + text="1.1" + ) + } + ) + + entities.append(Entity(device=device)) + + return entities + + +if __name__ == "__main__": + ''' main function ''' + + entities = generate_entities() + + with DiodeClient( + target="grpc:///diode", + app_name="my-app", + app_version="1", + + ) as client: + try: + response = client.ingest(entities=entities) + print(response) + except Exception as exc: + print(f"\n!! An Exception Happened: {exc}") diff --git a/netbox-diode-webinar/examples/python/2_json.py b/netbox-diode-webinar/examples/python/2_json.py new file mode 100644 index 0000000..c36dab5 --- /dev/null +++ b/netbox-diode-webinar/examples/python/2_json.py @@ -0,0 +1,62 @@ +''' + ensure DIODE_CLIENT_ID and DIODE_CLIENT_SECRET are set before running this +''' + +from netboxlabs.diode.sdk import DiodeClient +from google.protobuf.json_format import ParseDict +from netboxlabs.diode.sdk.ingester import ( + Entity, +) + + +def generate_entities() -> list[Entity]: + ''' generate a list of entitles for return to Diode ''' + entities = [] + devices = [ + { + "device": { + "name": "Device PDU2", + "site": { + "name": "Sunderland" + }, + "role": { + "name": "Rack PDU" + }, + "device_type": { + "manufacturer": { + "name": "APC" + }, + "model": "AP7952" + }, + "custom_fields": { + "software_version": { + "text": "1.2" + } + } + } + } + ] + + for jsonEntity in devices: + entity = Entity() + entity = ParseDict(jsonEntity, entity) + entities.append(entity) + + return entities + + +if __name__ == "__main__": + ''' main function ''' + + entities = generate_entities() + + with DiodeClient( + target="grpc:///diode", + app_name="my-app", + app_version="1", + + ) as client: + try: + response = client.ingest(entities=entities) + except Exception as exc: + print(f"\n!! An Exception Happened: {exc}") diff --git a/netbox-diode-webinar/examples/python/3_csv.py b/netbox-diode-webinar/examples/python/3_csv.py new file mode 100644 index 0000000..32b65d6 --- /dev/null +++ b/netbox-diode-webinar/examples/python/3_csv.py @@ -0,0 +1,97 @@ +''' + ensure DIODE_CLIENT_ID and DIODE_CLIENT_SECRET are set before running this +''' + +from netboxlabs.diode.sdk import DiodeClient +from netboxlabs.diode.sdk.ingester import ( + Entity, + Device, + IPAddress, + Interface, + CustomFieldValue +) +import csv + + +def load_from_csv(filename: str) -> list: + ''' function to read a csv file and return it as a list ''' + pdu_list = [] + + ''' read the csv file and look through creating a dict for each row, + then append to our list ''' + with open(filename, newline='') as csvfile: + ''' use a dict reader so we can reference via column names ''' + reader = csv.DictReader(csvfile) + for row in reader: + pdu = { + 'name': row['name'], + 'serial': row['serial'], + 'model': row['model'], + 'manufacturer': row['manufacturer'], + 'management_ip': row['management_ip'], + 'software_version': row['software_version'] + } + pdu_list.append(pdu) + return pdu_list + + +def generate_entities() -> list[Entity]: + ''' generate a list of entitles for return to Diode ''' + + pdu_list = load_from_csv("ourdata.csv") + + entities = [] + for pdu in pdu_list: + ''' create Device Entity, an IP Address and an Interface ''' + device = Device( + name=pdu['name'], + device_type=pdu['model'], + manufacturer=pdu['manufacturer'], + site='Prague', + role='pdu', + serial=pdu['serial'], + status='active', + custom_fields={ + "software_version": CustomFieldValue( + text=pdu['software_version'] + ) + }, + primary_ip4=IPAddress( + address=pdu['management_ip'], + status='active', + description='loaded from csv', + assigned_object_interface=Interface( + name='eth0', + type='1000base-t', + device=Device( + name=pdu['name'], + device_type=pdu['model'], + manufacturer=pdu['manufacturer'], + site='Prague', + role='pdu', + serial=pdu['serial'], + status='active', + ) + ) + ) + ) + + entities.append(Entity(device=device)) + return entities + + +if __name__ == "__main__": + ''' main function ''' + + entities = generate_entities() + + with DiodeClient( + target="grpc:///diode", + app_name="my-app", + app_version="1", + + ) as client: + try: + response = client.ingest(entities=entities) + except Exception as exc: + print(f"\n!! An Exception Happened: {exc}") diff --git a/netbox-diode-webinar/examples/python/ourdata.csv b/netbox-diode-webinar/examples/python/ourdata.csv new file mode 100644 index 0000000..5056925 --- /dev/null +++ b/netbox-diode-webinar/examples/python/ourdata.csv @@ -0,0 +1,3 @@ +name,manufacturer,model,management_ip,serial,software_version +"PDU 03",APC,APC6732,10.163.3.3,345678,"1.3" +"PDU 04",APC,APC6734,10.163.3.4,456789,"1.4" \ No newline at end of file diff --git a/netbox-diode-webinar/examples/python/requirements.txt b/netbox-diode-webinar/examples/python/requirements.txt new file mode 100644 index 0000000..7e14965 --- /dev/null +++ b/netbox-diode-webinar/examples/python/requirements.txt @@ -0,0 +1,2 @@ +netboxlabs-diode-sdk +google diff --git a/netbox-diode-webinar/examples/python/sample-orb-worker/README.md b/netbox-diode-webinar/examples/python/sample-orb-worker/README.md new file mode 100644 index 0000000..01d3941 --- /dev/null +++ b/netbox-diode-webinar/examples/python/sample-orb-worker/README.md @@ -0,0 +1,2 @@ +# ac3-integration-tutorial +Related files for the AutoCon3 - NetBox Discovery and Assurance 101 (Discovery Integration) diff --git a/netbox-diode-webinar/examples/python/sample-orb-worker/agent.yaml b/netbox-diode-webinar/examples/python/sample-orb-worker/agent.yaml new file mode 100644 index 0000000..bc60ab3 --- /dev/null +++ b/netbox-diode-webinar/examples/python/sample-orb-worker/agent.yaml @@ -0,0 +1,21 @@ +orb: + backends: + worker: + host: 0.0.0.0 + common: + diode: + target: grpc://:8080/diode + client_id: ${DIODE_CLIENT_ID} + client_secret: ${DIODE_CLIENT_SECRET} + agent_name: agent01 + policies: + worker: + custom_policy: + config: + package: lab_integration + schedule: "* * * * *" + custom_config: custom + csv_filename: ${CSV_FILENAME} + method: ${METHOD} + scope: + custom: any \ No newline at end of file diff --git a/netbox-diode-webinar/examples/python/sample-orb-worker/lab-integration/lab_integration/__init__.py b/netbox-diode-webinar/examples/python/sample-orb-worker/lab-integration/lab_integration/__init__.py new file mode 100644 index 0000000..34d30ae --- /dev/null +++ b/netbox-diode-webinar/examples/python/sample-orb-worker/lab-integration/lab_integration/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# + +from lab_integration.runner import LabIntegration + +__all__ = ["LabIntegration"] diff --git a/netbox-diode-webinar/examples/python/sample-orb-worker/lab-integration/lab_integration/runner.py b/netbox-diode-webinar/examples/python/sample-orb-worker/lab-integration/lab_integration/runner.py new file mode 100644 index 0000000..bf39355 --- /dev/null +++ b/netbox-diode-webinar/examples/python/sample-orb-worker/lab-integration/lab_integration/runner.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +import requests +import json +import csv +import logging +from netboxlabs.diode.sdk.ingester import Device, Interface, IPAddress +from collections.abc import Iterable +from netboxlabs.diode.sdk.ingester import Entity +from worker.backend import Backend +from worker.models import Metadata, Policy + + +class LabIntegration(Backend): + + def setup(self) -> Metadata: + return Metadata(name="lab_integration", app_name="lab_integration_app", app_version="1.0.0") + + def load_from_controller(self, controller_url: str, controller_token: str) -> list: + ''' function to read from the controller and return a list ''' + pdu_list = [] + names = [] + headers = {'Authorization': f'Token {controller_token}' } + r = requests.get(controller_url, headers=headers) + ''' get the brief list ''' + if r.status_code == 200: + for item in r.json(): + names.append(item["name"]) + ''' loop through the brief list to get the individual pdus ''' + for name in names: + iurl = f'{controller_url}?name={name}' + r = requests.get(iurl, headers=headers) + if r.status_code == 200: + pdu_list.append(r.json()) + + return pdu_list + + + def load_from_csv(self, filename: str) -> list: + ''' function to read a csv file and return it as a list ''' + pdu_list = [] + + ''' read the csv file and look through creating a dict for each row, then append to our list ''' + with open(filename, newline='') as csvfile: + reader = csv.DictReader(csvfile) + for row in reader: + pdu = { + 'name': row['name'], + 'serial': row['serial'], + 'model': row['model'], + 'manufacturer': row['manufacturer'], + 'management_ip': row['management_ip'] + } + pdu_list.append(pdu) + return pdu_list + + def transform_to_diode(self, pdu_list: list) -> list: + entities = [] + for pdu in pdu_list: + device = Device( + name = pdu['name'], + device_type=pdu['model'], + manufacturer=pdu['manufacturer'], + site='Prague', + role='pdu', + serial=pdu['serial'], + status='active', + primary_ip4=IPAddress( + address=pdu['management_ip'], + status='active', + description='loaded from csv', + assigned_object_interface=Interface( + name='eth0', + type='1000base-t', + device=Device( + name = pdu['name'], + device_type=pdu['model'], + manufacturer=pdu['manufacturer'], + site='Prague', + role='pdu', + serial=pdu['serial'], + status='active', + ) + ) + ) + ) + + entities.append(Entity(device=device)) + return entities + + def run(self, policy_name: str, policy: Policy) -> Iterable[Entity]: + entities = [] + + p = json.loads(policy.model_dump_json()) + method = p.get('config', {}).get('method') + logging.info(f"Integration method: {method.upper()}") + + pdu_list = [] + ''' load method depends on our environment ''' + if method == 'csv': + filename = p.get('config', {}).get('csv_filename') + pdu_list = self.load_from_csv(filename) + elif method == "api": + controller_url = p.get('config', {}).get('controller_url') + controller_token = p.get('config', {}).get('controller_token') + pdu_list = self.load_from_controller(controller_url, controller_token) + + if pdu_list: + entities=self.transform_to_diode(pdu_list) + + logging.info(f"{len(entities)} entities to be ingested") + return entities diff --git a/netbox-diode-webinar/examples/python/sample-orb-worker/lab-integration/pyproject.toml b/netbox-diode-webinar/examples/python/sample-orb-worker/lab-integration/pyproject.toml new file mode 100644 index 0000000..4702ba3 --- /dev/null +++ b/netbox-diode-webinar/examples/python/sample-orb-worker/lab-integration/pyproject.toml @@ -0,0 +1,43 @@ +[project] +name = "lab-integration" +version = "1.0.0" # Overwritten during the build process +description = "Worker to import data from a custom integration" +readme = "README.md" +requires-python = ">=3.10" +license = { text = "Apache-2.0" } +authors = [ + {name = "YOUR NAME", email = "YOUR@EMAIL.COM" } +] +maintainers = [ + {name = "YOUR NAME", email = "YOUR@EMAIL.COM" } +] + +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Topic :: Software Development :: Build Tools", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', +] + +dependencies = [ + "netboxlabs-orb-worker~=1.0", +] + + +[project.urls] +"Homepage" = "" + +[tool.setuptools] +packages = [ + "lab_integration", +] +package-data = {"lab_integration" = ["**/*"]} + +[build-system] +requires = ["setuptools>=43.0.0", "wheel"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/netbox-diode-webinar/examples/python/sample-orb-worker/ourdata.csv b/netbox-diode-webinar/examples/python/sample-orb-worker/ourdata.csv new file mode 100644 index 0000000..6c0283d --- /dev/null +++ b/netbox-diode-webinar/examples/python/sample-orb-worker/ourdata.csv @@ -0,0 +1,3 @@ +name,manufacturer,model,management_ip,serial +pdu03,APC,APC6732,10.163.3.3,345678 +pdu04,APC,APC6734,10.163.3.4,456789 \ No newline at end of file diff --git a/netbox-diode-webinar/examples/python/sample-orb-worker/workers.txt b/netbox-diode-webinar/examples/python/sample-orb-worker/workers.txt new file mode 100644 index 0000000..ce1df13 --- /dev/null +++ b/netbox-diode-webinar/examples/python/sample-orb-worker/workers.txt @@ -0,0 +1 @@ +./lab-integration \ No newline at end of file diff --git a/netbox-diode-webinar/examples/python/skeleton.py b/netbox-diode-webinar/examples/python/skeleton.py new file mode 100644 index 0000000..5959428 --- /dev/null +++ b/netbox-diode-webinar/examples/python/skeleton.py @@ -0,0 +1,32 @@ +''' + ensure DIODE_CLIENT_ID and DIODE_CLIENT_SECRET are set before running this +''' + +from netboxlabs.diode.sdk import DiodeClient +from netboxlabs.diode.sdk.ingester import ( + Entity, +) + + +def generate_entities() -> list[Entity]: + ''' generate a list of entitles for return to Diode ''' + entities = [] + return entities + + +if __name__ == "__main__": + ''' main function ''' + + entities = generate_entities() + + with DiodeClient( + target="grpc:///diode", + app_name="my-app", + app_version="1", + + ) as client: + try: + response = client.ingest(entities=entities) + print(response) + except Exception as exc: + print(f"\n!! An Exception Happened: {exc}")