From 9198d8e2cf1e59760df56d110b522c9939a7383d Mon Sep 17 00:00:00 2001 From: GVE Devnet Admin Date: Thu, 7 Mar 2024 18:04:33 +0000 Subject: [PATCH] Baselined from internal Repository last_commit:11ca9f88bb86b97fc3d10ddc7eeb1f51b14ba3cc --- .env | 5 + .github/workflows/docker-publish.yml | 53 ++++++++ .gitignore | 5 + CODE_OF_CONDUCT.md | 45 +++++++ CONTRIBUTING.md | 11 ++ Dockerfile | 7 + IMAGES/0image.png | Bin 0 -> 49922 bytes LICENSE.md | 81 ++++++++++++ README.md | 163 ++++++++++++++++++++++++ docker-compose.yml | 15 +++ flaskapp.py | 99 ++++++++++++++ requirements.txt | 27 ++++ work_files/C9k_onboarding_template.json | 80 ++++++++++++ work_files/mapping.csv | 3 + 14 files changed, 594 insertions(+) create mode 100644 .env create mode 100644 .github/workflows/docker-publish.yml create mode 100644 .gitignore create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 Dockerfile create mode 100644 IMAGES/0image.png create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 flaskapp.py create mode 100644 requirements.txt create mode 100644 work_files/C9k_onboarding_template.json create mode 100644 work_files/mapping.csv diff --git a/.env b/.env new file mode 100644 index 0000000..093a9a3 --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +DNAC_HOST= +USERNAME= +PASSWORD= +DAY0_TEMPLATE= +AUTOCLAIM_DEVICE_PID= \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..5c0c8d4 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,53 @@ +name: Docker Build & Publish + +on: + push: + branches: + - main + - master + tags: + - '*' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + + +jobs: + push: + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf + + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7bec9e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +venv +.DS_STORE +templates +__pycache__ +*.pyc diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..b86d304 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,45 @@ +# Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as maintainers of this Cisco Sample Code pledge to making participation with our project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Showing empathy towards other people + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other interactions with this project that are not aligned to this Code of Conduct, or to ban temporarily or permanently any person for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project. Examples of representing a project include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Cisco SE GitHub team at ciscose-github@cisco.com. The team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project or Cisco SE Leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8370d5e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,11 @@ +# Cisco Sample Code + +This project, and the code contained herein, is provided for example and/or demonstration purposes by Cisco for use by our partners and customers in working with Cisco's products and services. While Cisco's customers and partners are free to use this code pursuant to the terms set forth in the [LICENSE][LICENSE], this is not an Open Source project as we are not seeking to build a community around this project and its capabilities. + + +We do desire to provide functional and high-quality examples and demonstrations. If you should discover some bug, issue, or opportunity for enhancement with the code contained in this project, please do notify us by: + +1. **Reviewing Open Issues** to verify that the issue hasn't already been reported. +2. **Opening a New Issue** to report the bug, issue, or enhancement opportunity. + +[LICENSE]: ../LICENSE diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bb63827 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.9-slim-buster +WORKDIR /app +COPY ./requirements.txt /app +RUN pip install -r requirements.txt +COPY . . +EXPOSE 9002 +CMD ["python", "./flaskapp.py"] diff --git a/IMAGES/0image.png b/IMAGES/0image.png new file mode 100644 index 0000000000000000000000000000000000000000..1244d962ed8591e9ec1d8cc5a6cde0b19ab9808c GIT binary patch literal 49922 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%0DgIwFEgG%DKu%%R zHCI#FX6{X|Ev=0HUP|;8nKd2|EiKR?r8#Rts&&l>$_BI221Tap6i41@q9td!w|h-J zm?*Ly!4^frq%39CcMw!|G|-^u*@(|vWTMQ+m<;HQ zWB5jv>er-SkbTZ5iQEoly~n5z;xXA~N+@xgWS@(H)daKB(q-iMPFR>Rf=BL=1dpR8 zPn{p09OvIY*;D3saRO5qG4PNfB0ivNfC8!@ftd`8iL?aD_Dlkd1?z{Va>Q~^wL#sX&e4F$6awN(MG?7ayWZ}cQt zqmu9+5=JotX5u9Kbyd%t-^E1E%cPVAE6|bc{UP-jpMDi*H02_2I7M?QyBTL@mxkaR zPou+lZJjCC?R#btCaM{?URJQx3{oQKYlGGxAbApwvtk3Uv2T~v-OGkLzc z)ec1*u7OlyEEuGK!@w=(?gX(r`7l4vK6iSW;+kXq*zl=j5E!)PGBZ}=Q*30v0Dc40 zn1TwJQ+IM{%4y+zhxR4=^SJ=*@jZt^ZoGmpf~ty?1A;T9;Z&?uAVVy((xBmO8JEdQ zhoceyg{`;ZNw$bN$zjSNnUtm~8vnkV1_;iL6zAYp1A-5aj>|t~A)inqb^;$d$19yP!Q{tSiQ~((?%VBP>QR zA{1tR-Z5dNA-Fy?`bzpF|Hb)qSmAaPeU@lzFrr5En1aHbSAHt(H)zkR>pH0a*T9gG zdnGLM1tYXlx|_zZym$9VUWN6EsfC`39O`n%xxeG#xcjf?MYg9D#n4@qQbISbO!QnS zmvm{%6h48fNBJl2qjMHY<0ZW5cB=V>GeFkyzvuf?$e|b>Wad)meqQiwtX+e}L22x(>47>_LerSrieYiN{l6Zn3oCSi=1ySDWwbw5fL7nI Note: This repository is an adapted version of the [PnPAutoClaim](https://github.com/pamosima/PnPAutoClaim) repository. + +The sample code was tested with Catalyst 9200/9300 and Catalyst Center version 2.3.7.4. + + +## Contacts +* Ramona Renner + +## Solution Components +* Python +* DNA Center +* DNA Center REST API's +* Environment set up for PnP process + + +## Prerequisites + +### Webhooks + +The app needs to be reachable on port 9002 from the public internet for Catalyst Center Webhooks to be received and processed. Any option is valid, like AWS Lambda, Heroku, GCP, etc. Ngrok is used here to expose the local app for simplicity. + +#### Ngrok + +The Flask app runs on http://localhost:9002 by default, so it requires a Ngrok forwarding address to port 9002 to receive the webhooks. + +Follow these instructions to set up ngrok: + +1. Create a free account or login to [Ngrok](https://ngrok.com/). +2. Retrieve your auth token by navigating to `Getting Started > Your Authtoken` on the menu on the left-hand side. Copy the token on this page. +3. Then install the client library, depending on your OS [here](https://ngrok.com/download). +4. Once you have ngrok installed, update the ngrok configuration file with your auth token by running the following command on the terminal/command prompt: + + ```ngrok authtoken ``` + +replacing with the authtoken you copied in Step 2. + +5. Start the ngrok tunnel for port 9002 with the command: + + ```ngrok http https://localhost:9002``` + +6. Note the link under `Forwarding` with the format `http://xxxxx.ngrok-free.app` for a later step. + + +### Cisco DNA Center + +#### Configure the Webhook Event and Notification + +1. Choose **Platform > Developer Toolkit > Event Notifications > Notifications** + +2. Click **+** to create a new notification + +3. For **Step 1 - Select Site and Events**, search for ```Device waiting to be claimed```, select the notification and click **Next** + +4. In **Step 2 - Select Channels**, select **Rest** as a notification channels and click **Next** + +5. In **Step 3 - REST Settings**, click **here** to create a new setting instance. + +6. On the **Destinations** page, click **Add** and provide the following information, and click **Save** + * Name: Name of the Webhook destination, e.g. PnP Sample Script + * (Optional) Description + * URL: public URL of the sample application, format: `/claim` + * Authentication, e.g. `No Auth` + * Proxy, e.g. checked + + +7. Return to the **Step 3 - REST Settings** page, refresh the instance select, and select the created instance. + +8. Click on **Next** + +9. In **Step 4 - Name and Description**, provide a name and short description for your notification, and click on **Next** + +10. On the Summary page, click on **Finish** + +Done! Your new notification is complete. + + +#### Import the Onboarding Template + +1. Choose **Design** > **CLI Templates** or alternative in older Catalyst Versions **Tools** > **Template Hub** + +2. Click **Import** > **Import Template** + +3. Select the project **Onboarding Configuration** and upload the template provided in the **work_files** folder of this repository. Click **Import**. + + +### Set up the Sample App + +8. Make sure you have [Python 3.8.10](https://www.python.org/downloads/) and [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) installed. + +9. (Optional) Create and activate a virtual environment for the project ([Instructions](https://docs.python.org/3/tutorial/venv.html)) + +10. Access the created virtual environment folder + ``` + cd [add name of virtual environment here] + ``` + +11. Clone this GitHub repository into the local folder: + ``` + git clone [add GitHub link here] + ``` + * For GitHub link: + In GitHub, click on the **Code** button in the upper part of the page > click the **copy icon** + + * Or simply download the repository as zip file using the 'Download ZIP' button and extract it + +12. Access the downloaded folder: + ``` + cd gve_devnet_catalyst_center_pnp_automatic_claim + ``` + +13. Install all dependencies: + ``` + pip3 install -r requirements.txt + ``` + +14. Fill in your variables in the .env file. Feel free to leave the variable without note as is: + + ```python + DNAC_HOST= + USERNAME= + PASSWORD= + DAY0_TEMPLATE= + AUTOCLAIM_DEVICE_PID= + ``` + +> Note: Mac OS hides the .env file in the finder by default. View the demo folder for example with your preferred IDE to make the file visible. + +15. Fill in one row for each network, site and configuration values in the **work_files/mapping.csv** file. As soon as a device with a defined type contacts Catalyst Center its IP address is compared to each entry in the **mapping.csv** file. If the IP address is part of one of the listed networks, the device will be claimed using the provided information for the mapping network. + +IP,CIDR,site,hostname +,,/, + +## Usage + +16. Start the flask application: + +```python3 flaskapp.py``` + +The sample script will react as soon as a new device of the defined type is waiting to be claimed. + + +### LICENSE + +Provided under Cisco Sample Code License, for details see [LICENSE](LICENSE.md) + +### CODE_OF_CONDUCT + +Our code of conduct is available [here](CODE_OF_CONDUCT.md) + +### CONTRIBUTING + +See our contributing guidelines [here](CONTRIBUTING.md) + +#### DISCLAIMER: +Please note: This script is meant for demo purposes only. All tools/ scripts in this repo are released for use "AS IS" without any warranties of any kind, including, but not limited to their installation, use, or performance. Any use of these scripts and tools is at your own risk. There is no guarantee that they have been through thorough testing in a comparable environment and we are not responsible for any damage or data loss incurred with their use. +You are responsible for reviewing and testing any scripts you run thoroughly before use in any non-testing environment. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c7a605d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3.5" + +services: + catalyst_center_pnp_automatic_claim: + image: ghcr.io/gve-sw/gve_devnet_catalyst_center_pnp_automatic_claim:latest + container_name: catalyst_center_pnp_automatic_claim + environment: + - DNAC_HOST=${DNAC_HOST} + - USERNAME=${USERNAME} + - PASSWORD=${PASSWORD} + - DAY0_TEMPLATE=${DAY0_TEMPLATE} + - AUTOCLAIM_DEVICE_PID=${AUTOCLAIM_DEVICE_PID} + volumes: + - config.yaml:/app/config.yaml + restart: "always" diff --git a/flaskapp.py b/flaskapp.py new file mode 100644 index 0000000..b132c27 --- /dev/null +++ b/flaskapp.py @@ -0,0 +1,99 @@ +""" Copyright (c) 2024 Cisco and/or its affiliates. +This software is licensed to you under the terms of the Cisco Sample +Code License, Version 1.1 (the "License"). You may obtain a copy of the +License at + https://developer.cisco.com/docs/licenses +All use of the material herein must be in accordance with the terms of +the License. All rights not expressly granted by the License are +reserved. Unless required by applicable law or agreed to separately in +writing, software distributed under the License is distributed on an "AS +IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied. +""" + +from flask import Flask, request +from dnacentersdk import DNACenterAPI +import pandas as pd +import ipaddress +from dotenv import load_dotenv +import os +import json + +load_dotenv() + +app = Flask(__name__) + +file = pd.read_csv("work_files/mapping.csv", sep=",") + +@app.route('/claim', methods=['GET', 'POST']) +def dnac_alert_received(): + + if request.method == 'POST': + + dnac_notification = request.get_json() + print(f'-- Received webhook for device to be claimed:') + print(json.dumps(dnac_notification, indent=2)) + + ip = dnac_notification['details']['ipAddress'] + serialNo = dnac_notification['details']['deviceName'] + dnacIP = dnac_notification['dnacIP'] + + api = DNACenterAPI(base_url="https://"+dnacIP+":443", + username=os.environ['USERNAME'], + password=os.environ['PASSWORD'], + version='2.3.3.0', + verify=False) + + device_info = api.device_onboarding_pnp.get_device_list( + serial_number=serialNo) + + print("-- Device Info based on serial") + print(json.dumps(device_info, indent=2)) + + deviceId = device_info[0]["id"] + pid = device_info[0]["deviceInfo"]["pid"] + + if os.environ['AUTOCLAIM_DEVICE_PID'] == pid: + + print(f"Device has correct pid type: {os.environ['AUTOCLAIM_DEVICE_PID']}") + + for inx, row in file.iterrows(): + + subnet = f"{row['IP']}/{row['CIDR']}" + + if ipaddress.ip_address(ip) in ipaddress.ip_network(subnet): + + print(f"The device IP {ip} is part of subnet: {subnet}") + + print(f"It will be assigned to site: {row['site']}") + siteId = api.sites.get_site(name=str(row["site"]))[ + "response"][0]["id"] + print(f"Associated site id: {siteId}") + + print(f'Update name of device to {row["hostname"]}') + device_info = {'hostname': row["hostname"], + 'name': row["hostname"]} + api.device_onboarding_pnp.update_device(deviceId, deviceInfo=device_info) + + templateId = api.configuration_templates.get_templates_details(name=os.environ['DAY0_TEMPLATE'])["response"][0]["id"] + print(f"Prepare template ID and config parameters for template with ID: {templateId}") + configInfo = {'configId': templateId, + 'configParameters': [{'key': 'hostname', 'value': row["hostname"]}] + } + print(f"Template config parameters: {configInfo}") + + print(f"Claiming device to site. Result:") + response = api.device_onboarding_pnp.claim_a_device_to_a_site(configInfo=configInfo, + deviceId=deviceId, + siteId=siteId, + type="Default") + print(response) + + break + + return("Webhook Recieved") + + +if __name__ == '__main__': + + app.run(host="0.0.0.0", port="9002", ssl_context="adhoc") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b6dd0e8 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,27 @@ +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +cryptography==40.0.2 +dnacentersdk==2.6.11 +fastjsonschema==2.19.1 +Flask==2.2.3 +future==0.18.3 +idna==3.6 +importlib-metadata==7.0.1 +itsdangerous==2.1.2 +Jinja2==3.1.3 +MarkupSafe==2.1.5 +numpy==1.24.4 +pandas==1.5.3 +pycparser==2.21 +pyOpenSSL==23.1.1 +python-dateutil==2.8.2 +python-dotenv==1.0.1 +pytz==2024.1 +requests==2.31.0 +requests-toolbelt==1.0.0 +six==1.16.0 +urllib3==1.26.15 +Werkzeug==2.2.2 +zipp==3.17.0 \ No newline at end of file diff --git a/work_files/C9k_onboarding_template.json b/work_files/C9k_onboarding_template.json new file mode 100644 index 0000000..e651544 --- /dev/null +++ b/work_files/C9k_onboarding_template.json @@ -0,0 +1,80 @@ +[ + { + "name": "PnP-Onboarding", + "description": "", + "tags": [], + "author": "apiuser", + "deviceTypes": [ + { + "productFamily": "Switches and Hubs", + "productSeries": "Cisco Catalyst 9200 Series Switches" + } + ], + "softwareType": "IOS", + "softwareVariant": "XE", + "templateContent": "hostname $hostname\n", + "templateParams": [ + { + "parameterName": "hostname", + "dataType": "STRING", + "defaultValue": null, + "description": null, + "required": true, + "notParam": false, + "paramArray": false, + "instructionText": null, + "group": null, + "order": 1, + "customOrder": 0, + "selection": null, + "range": [], + "key": null, + "provider": null, + "binding": "", + "sensitiveField": false, + "displayName": null + } + ], + "rollbackTemplateParams": [], + "composite": false, + "containingTemplates": [], + "language": "VELOCITY", + "promotedTemplateContent": "hostname $hostname\n", + "promotedTemplateParams": [ + { + "parameterName": "hostname", + "dataType": "STRING", + "defaultValue": null, + "description": null, + "required": true, + "notParam": false, + "paramArray": false, + "instructionText": null, + "group": null, + "order": 1, + "customOrder": 0, + "selection": null, + "range": [], + "key": null, + "provider": null, + "binding": "", + "sensitiveField": false, + "id": null, + "displayName": null + } + ], + "customParamsOrder": false, + "createTime": 1709652028512, + "lastUpdateTime": 1709806483362, + "latestVersionTime": 1709806505226, + "validationErrors": { + "templateErrors": [], + "rollbackTemplateErrors": [], + "templateId": "3765d338-1af6-49da-8893-9aabba5c0109", + "templateVersion": null + }, + "noOfConflicts": 0, + "documentDatabase": false, + "projectAssociated": true + } +] \ No newline at end of file diff --git a/work_files/mapping.csv b/work_files/mapping.csv new file mode 100644 index 0000000..b00dc80 --- /dev/null +++ b/work_files/mapping.csv @@ -0,0 +1,3 @@ +IP,CIDR,site,hostname + +