diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..3233c23 --- /dev/null +++ b/404.html @@ -0,0 +1,443 @@ + + + + + + + + + + + + + + + + + + NetBox Config Diff Plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/assets/_mkdocstrings.css b/assets/_mkdocstrings.css new file mode 100644 index 0000000..e69de29 diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000..1cf13b9 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.220ee61c.min.js b/assets/javascripts/bundle.220ee61c.min.js new file mode 100644 index 0000000..116072a --- /dev/null +++ b/assets/javascripts/bundle.220ee61c.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Ci=Object.create;var gr=Object.defineProperty;var Ri=Object.getOwnPropertyDescriptor;var ki=Object.getOwnPropertyNames,Ht=Object.getOwnPropertySymbols,Hi=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,nn=Object.prototype.propertyIsEnumerable;var rn=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&rn(e,r,t[r]);if(Ht)for(var r of Ht(t))nn.call(t,r)&&rn(e,r,t[r]);return e};var on=(e,t)=>{var r={};for(var n in e)yr.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&Ht)for(var n of Ht(e))t.indexOf(n)<0&&nn.call(e,n)&&(r[n]=e[n]);return r};var Pt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Pi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ki(t))!yr.call(e,o)&&o!==r&&gr(e,o,{get:()=>t[o],enumerable:!(n=Ri(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Ci(Hi(e)):{},Pi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var sn=Pt((xr,an)=>{(function(e,t){typeof xr=="object"&&typeof an!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(O){return!!(O&&O!==document&&O.nodeName!=="HTML"&&O.nodeName!=="BODY"&&"classList"in O&&"contains"in O.classList)}function f(O){var Qe=O.type,De=O.tagName;return!!(De==="INPUT"&&s[Qe]&&!O.readOnly||De==="TEXTAREA"&&!O.readOnly||O.isContentEditable)}function c(O){O.classList.contains("focus-visible")||(O.classList.add("focus-visible"),O.setAttribute("data-focus-visible-added",""))}function u(O){O.hasAttribute("data-focus-visible-added")&&(O.classList.remove("focus-visible"),O.removeAttribute("data-focus-visible-added"))}function p(O){O.metaKey||O.altKey||O.ctrlKey||(a(r.activeElement)&&c(r.activeElement),n=!0)}function m(O){n=!1}function d(O){a(O.target)&&(n||f(O.target))&&c(O.target)}function h(O){a(O.target)&&(O.target.classList.contains("focus-visible")||O.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(O.target))}function v(O){document.visibilityState==="hidden"&&(o&&(n=!0),Y())}function Y(){document.addEventListener("mousemove",N),document.addEventListener("mousedown",N),document.addEventListener("mouseup",N),document.addEventListener("pointermove",N),document.addEventListener("pointerdown",N),document.addEventListener("pointerup",N),document.addEventListener("touchmove",N),document.addEventListener("touchstart",N),document.addEventListener("touchend",N)}function B(){document.removeEventListener("mousemove",N),document.removeEventListener("mousedown",N),document.removeEventListener("mouseup",N),document.removeEventListener("pointermove",N),document.removeEventListener("pointerdown",N),document.removeEventListener("pointerup",N),document.removeEventListener("touchmove",N),document.removeEventListener("touchstart",N),document.removeEventListener("touchend",N)}function N(O){O.target.nodeName&&O.target.nodeName.toLowerCase()==="html"||(n=!1,B())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),Y(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var cn=Pt(Er=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},s=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(B,N){d.append(N,B)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(O){throw new Error("URL unable to set base "+c+" due to "+O)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,Y=!0,B=this;["append","delete","set"].forEach(function(O){var Qe=h[O];h[O]=function(){Qe.apply(h,arguments),v&&(Y=!1,B.search=h.toString(),Y=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var N=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==N&&(N=this.search,Y&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},s=i.prototype,a=function(f){Object.defineProperty(s,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){a(f)}),Object.defineProperty(s,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(s,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er)});var qr=Pt((Mt,Nr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Mt=="object"&&typeof Nr=="object"?Nr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Mt=="object"?Mt.ClipboardJS=r():t.ClipboardJS=r()})(Mt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return Ai}});var s=i(279),a=i.n(s),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(T){return!1}}var d=function(T){var E=p()(T);return m("cut"),E},h=d;function v(j){var T=document.documentElement.getAttribute("dir")==="rtl",E=document.createElement("textarea");E.style.fontSize="12pt",E.style.border="0",E.style.padding="0",E.style.margin="0",E.style.position="absolute",E.style[T?"right":"left"]="-9999px";var H=window.pageYOffset||document.documentElement.scrollTop;return E.style.top="".concat(H,"px"),E.setAttribute("readonly",""),E.value=j,E}var Y=function(T,E){var H=v(T);E.container.appendChild(H);var I=p()(H);return m("copy"),H.remove(),I},B=function(T){var E=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},H="";return typeof T=="string"?H=Y(T,E):T instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(T==null?void 0:T.type)?H=Y(T.value,E):(H=p()(T),m("copy")),H},N=B;function O(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?O=function(E){return typeof E}:O=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},O(j)}var Qe=function(){var T=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},E=T.action,H=E===void 0?"copy":E,I=T.container,q=T.target,Me=T.text;if(H!=="copy"&&H!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&O(q)==="object"&&q.nodeType===1){if(H==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(H==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Me)return N(Me,{container:I});if(q)return H==="cut"?h(q):N(q,{container:I})},De=Qe;function $e(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?$e=function(E){return typeof E}:$e=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},$e(j)}function Ei(j,T){if(!(j instanceof T))throw new TypeError("Cannot call a class as a function")}function tn(j,T){for(var E=0;E0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof I.action=="function"?I.action:this.defaultAction,this.target=typeof I.target=="function"?I.target:this.defaultTarget,this.text=typeof I.text=="function"?I.text:this.defaultText,this.container=$e(I.container)==="object"?I.container:document.body}},{key:"listenClick",value:function(I){var q=this;this.listener=c()(I,"click",function(Me){return q.onClick(Me)})}},{key:"onClick",value:function(I){var q=I.delegateTarget||I.currentTarget,Me=this.action(q)||"copy",kt=De({action:Me,container:this.container,target:this.target(q),text:this.text(q)});this.emit(kt?"success":"error",{action:Me,text:kt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(I){return vr("action",I)}},{key:"defaultTarget",value:function(I){var q=vr("target",I);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(I){return vr("text",I)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(I){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return N(I,q)}},{key:"cut",value:function(I){return h(I)}},{key:"isSupported",value:function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof I=="string"?[I]:I,Me=!!document.queryCommandSupported;return q.forEach(function(kt){Me=Me&&!!document.queryCommandSupported(kt)}),Me}}]),E}(a()),Ai=Li},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,f){for(;a&&a.nodeType!==o;){if(typeof a.matches=="function"&&a.matches(f))return a;a=a.parentNode}}n.exports=s},438:function(n,o,i){var s=i(828);function a(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof m=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return a(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=s(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(n,o,i){var s=i(879),a=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(h))throw new TypeError("Third argument must be a Function");if(s.node(m))return c(m,d,h);if(s.nodeList(m))return u(m,d,h);if(s.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return a(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),s=f.toString()}return s}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,s,a){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var f=this;function c(){f.off(i,c),s.apply(a,arguments)}return c._=s,this.on(i,c,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=a.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var rs=/["'&<>]/;Yo.exports=ns;function ns(e){var t=""+e,r=rs.exec(t);if(!r)return t;var n,o="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||a(m,d)})})}function a(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof et?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){a("next",m)}function u(m){a("throw",m)}function p(m,d){m(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function pn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(s){return new Promise(function(a,f){s=e[i](s),o(a,f,s.done,s.value)})}}function o(i,s,a,f){Promise.resolve(f).then(function(c){i({value:c,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),f=a.next();!f.done;f=a.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var u=this.initialTeardown;if(C(u))try{u()}catch(v){i=v instanceof It?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=Ee(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{ln(h)}catch(v){i=i!=null?i:[],v instanceof It?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ln(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Sr=Ie.EMPTY;function jt(e){return e instanceof Ie||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function ln(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,s=o.isStopped,a=o.observers;return i||s?Sr:(this.currentObservers=null,a.push(r),new Ie(function(){n.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,s=n.isStopped;o?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,n){return new xn(r,n)},t}(F);var xn=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Sr},t}(x);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,s=n._infiniteTimeWindow,a=n._timestampProvider,f=n._windowTime;o||(i.push(r),!s&&i.push(a.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,s=o._buffer,a=s.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var s=r.actions;n!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Wt);var Sn=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Dt);var Oe=new Sn(wn);var M=new F(function(e){return e.complete()});function Vt(e){return e&&C(e.schedule)}function Cr(e){return e[e.length-1]}function Ye(e){return C(Cr(e))?e.pop():void 0}function Te(e){return Vt(Cr(e))?e.pop():void 0}function zt(e,t){return typeof Cr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Nt(e){return C(e==null?void 0:e.then)}function qt(e){return C(e[ft])}function Kt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function zi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Yt=zi();function Gt(e){return C(e==null?void 0:e[Yt])}function Bt(e){return un(this,arguments,function(){var r,n,o,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,et(r.read())];case 3:return n=s.sent(),o=n.value,i=n.done,i?[4,et(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,et(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Jt(e){return C(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(qt(e))return Ni(e);if(pt(e))return qi(e);if(Nt(e))return Ki(e);if(Kt(e))return On(e);if(Gt(e))return Qi(e);if(Jt(e))return Yi(e)}throw Qt(e)}function Ni(e){return new F(function(t){var r=e[ft]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function qi(e){return new F(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?A(function(o,i){return e(o,i,n)}):de,ge(1),r?He(t):Dn(function(){return new Zt}))}}function Vn(){for(var e=[],t=0;t=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,f=a===void 0?!0:a;return function(c){var u,p,m,d=0,h=!1,v=!1,Y=function(){p==null||p.unsubscribe(),p=void 0},B=function(){Y(),u=m=void 0,h=v=!1},N=function(){var O=u;B(),O==null||O.unsubscribe()};return y(function(O,Qe){d++,!v&&!h&&Y();var De=m=m!=null?m:r();Qe.add(function(){d--,d===0&&!v&&!h&&(p=$r(N,f))}),De.subscribe(Qe),!u&&d>0&&(u=new rt({next:function($e){return De.next($e)},error:function($e){v=!0,Y(),p=$r(B,o,$e),De.error($e)},complete:function(){h=!0,Y(),p=$r(B,s),De.complete()}}),U(O).subscribe(u))})(c)}}function $r(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function z(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),V(e===_e()),J())}function Xe(e){return{x:e.offsetLeft,y:e.offsetTop}}function Kn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>Xe(e)),V(Xe(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>rr(e)),V(rr(e)))}var Yn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!Wr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),va?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!Wr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ba.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Gn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Jn=typeof WeakMap!="undefined"?new WeakMap:new Yn,Xn=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=ga.getInstance(),n=new La(t,r,this);Jn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){Xn.prototype[e]=function(){var t;return(t=Jn.get(this))[e].apply(t,arguments)}});var Aa=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:Xn}(),Zn=Aa;var eo=new x,Ca=$(()=>k(new Zn(e=>{for(let t of e)eo.next(t)}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return Ca.pipe(S(t=>t.observe(e)),g(t=>eo.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var to=new x,Ra=$(()=>k(new IntersectionObserver(e=>{for(let t of e)to.next(t)},{threshold:0}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function sr(e){return Ra.pipe(S(t=>t.observe(e)),g(t=>to.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function ro(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),J())}var cr={drawer:z("[data-md-toggle=drawer]"),search:z("[data-md-toggle=search]")};function no(e){return cr[e].checked}function Ke(e,t){cr[e].checked!==t&&cr[e].click()}function Ue(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),V(t.checked))}function ka(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ha(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function oo(){let e=b(window,"keydown").pipe(A(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:no("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),A(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!ka(n,r)}return!0}),pe());return Ha().pipe(g(t=>t?M:e))}function le(){return new URL(location.href)}function ot(e){location.href=e.href}function io(){return new x}function ao(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)ao(e,r)}function _(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)ao(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function so(){return location.hash.substring(1)}function Dr(e){let t=_("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Pa(e){return L(b(window,"hashchange"),e).pipe(l(so),V(so()),A(t=>t.length>0),X(1))}function co(e){return Pa(e).pipe(l(t=>ce(`[id="${t}"]`)),A(t=>typeof t!="undefined"))}function Vr(e){let t=matchMedia(e);return er(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function fo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function zr(e,t){return e.pipe(g(r=>r?t():M))}function ur(e,t={credentials:"same-origin"}){return ue(fetch(`${e}`,t)).pipe(fe(()=>M),g(r=>r.status!==200?Ot(()=>new Error(r.statusText)):k(r)))}function We(e,t){return ur(e,t).pipe(g(r=>r.json()),X(1))}function uo(e,t){let r=new DOMParser;return ur(e,t).pipe(g(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),X(1))}function pr(e){let t=_("script",{src:e});return $(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(g(()=>Ot(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),R(()=>document.head.removeChild(t)),ge(1))))}function po(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function lo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(po),V(po()))}function mo(){return{width:innerWidth,height:innerHeight}}function ho(){return b(window,"resize",{passive:!0}).pipe(l(mo),V(mo()))}function bo(){return G([lo(),ho()]).pipe(l(([e,t])=>({offset:e,size:t})),X(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(ee("size")),o=G([n,r]).pipe(l(()=>Xe(e)));return G([r,t,o]).pipe(l(([{height:i},{offset:s,size:a},{x:f,y:c}])=>({offset:{x:s.x-f,y:s.y-c+i},size:a})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(s=>{let a=document.createElement("script");a.src=i,a.onload=s,document.body.appendChild(a)})),Promise.resolve())}var r=class extends EventTarget{constructor(n){super(),this.url=n,this.m=i=>{i.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:i.data})),this.onmessage&&this.onmessage(i))},this.e=(i,s,a,f,c)=>{if(s===`${this.url}`){let u=new ErrorEvent("error",{message:i,filename:s,lineno:a,colno:f,error:c});this.dispatchEvent(u),this.onerror&&this.onerror(u)}};let o=document.createElement("iframe");o.hidden=!0,document.body.appendChild(this.iframe=o),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Changelog

+

2.2.0 (2024-02-06)

+
    +
  • #47 Move plugin to separete menu item in navbar and add tab for devices with compliance result
  • +
  • #50 Add template field for device name in DataSource to ConfigDiffScript
  • +
  • #53 Add netbox-rq to installation process docs
  • +
+

2.1.0 (2023-10-26)

+
    +
  • #35 Add ability to define password for accessing priviliged exec mode
  • +
  • #37 Add DeviceRole field to CollectDiffScript
  • +
  • #38 Remove config template filter for devices filed in forms
  • +
  • #39 Add Status field to CollectDiffScript
  • +
  • #43 ConfigDiffScript does not create empty changelog entries
  • +
+

2.0.1 (2023-10-22)

+
    +
  • #33 Fix failing migrations on fresh database install
  • +
+

2.0.0 (2023-10-18)

+
    +
  • #25 Add configuration management
  • +
+

1.2.2 (2023-09-29)

+
    +
  • #28 Add legacy ssh algorithms to support old OS versions
  • +
+

1.2.1 (2023-09-07)

+
    +
  • #26 Add dark theme for diff
  • +
+

1.2.0 (2023-08-23)

+ +

1.1.1 (2023-08-13)

+
    +
  • #1 Add tests
  • +
+

1.1.0 (2023-08-01)

+
    +
  • #16 Add missing and extra config lines
  • +
+

1.0.0 (2023-07-23)

+
    +
  • Publish on PyPI.
  • +
+

0.1.1 (2023-07-23)

+
    +
  • Add DataSoures as sources for device configurations.
  • +
  • Add docs.
  • +
+

0.1.0 (2023-07-09)

+
    +
  • First release.
  • +
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/colliecting-diffs/index.html b/colliecting-diffs/index.html new file mode 100644 index 0000000..74714c7 --- /dev/null +++ b/colliecting-diffs/index.html @@ -0,0 +1,619 @@ + + + + + + + + + + + + + + + + + + + + + + + + Collecting diffs - NetBox Config Diff Plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Usage

+

In navbar serach for Config Diff Plugin menu

+

Screenshot of navbar

+

PlatformSetting

+

Add PlatformSetting objects for your platforms in NetBox. This model contains info about how to connect and what to collect from device.

+

Define:

+
    +
  • Driver for Scrapli, you can find all drivers in Scrapli and Scrapli community documentation.
  • +
  • Command to collect configuration
  • +
  • Optional regex patterns to exclude from actual config, specify each pattern on a new line
  • +
+

With regexps you can exclude big parts of the configuration and compare tiny configuration pieces (only ntp configuration).

+

You can test regexp on the site regex101.com.

+

Screenshot of PlatformSetting

+

Script

+

Plugin adds a custom script ConfigDiffScript that runs all logic about diff calculations and connections to devices. +You can find scripts list in navbar Customization -> Scripts.

+

Screenshot of the scripts list

+

In the script, you can define a site or role, on which devices run compliance, or devices. + If you define all fields, script will run only on devices from Devices field

+
+

Warning

+

Script runs only on devices with assigned Primary IP, Platform and PlatformSetting

+
+

If you have configs in NetBox DataSource, you can define it, the script instead of connecting to devices will find configs in DataSource by device's names.

+
+

Warning

+

Be sure that DataSource is synced and has the latest data

+
+
+

Note

+

Only synced DataSources are acceptable

+
+

If in your DataSource config names are different from the hostnames of the devices, you can specify config name with Jinja2 template in Name template field. + Reference device with {{ object }} variable.

+

For example, config name is virtual chassis name plus config (switchname-config) and your devices names are switchname1, switchname2 and etc.

+

You can define Jinja2 template with logic to use virtual chassis name if device is in chassis, else use device name:

+
{% if object.virtual_chassis %}{{ object.virtual_chassis.name }}-config{% else %}{{ object.name }}{% endif %}
+
+

Screenshot of the script

+

Results

+

After script is done you can find results in Config Compliances menu. Each device has its own result.

+

Screenshot of the compliance list

+

Also result is storing rendered and actual configurations from devices.

+

With the help of netutils library plugin stores missing and extra config lines.

+

Screenshot of the missing/extra lines

+

Supported platforms for missing/extra lines:

+
    +
  • Arista EOS (arista_eos)
  • +
  • Aruba AOSCX (aruba_aoscx)
  • +
  • Cisco AireOS (cisco_aireos)
  • +
  • Cisco ASA (cisco_asa)
  • +
  • Cisco IOS-XE (cisco_iosxe)
  • +
  • Cisco IOS-XR (cisco_iosxr)
  • +
  • Cisco NX-OS (cisco_nxos)
  • +
  • Juniper JunOS (juniper_junos)
  • +
  • Mikrotik RouterOS (mikrotik_routeros)
  • +
  • Nokia SROS (nokia_sros)
  • +
  • PaloAlto PanOS (paloalto_panos)
  • +
  • Ruckus FastIron (ruckus_fastiron)
  • +
+

Compliance finished with error

+

Screenshot of the compliance error

+

Render diff between configurations

+

Screenshot of diff

+

No diff

+

Screenshot of the compliance ok

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/configuratiom-management/index.html b/configuratiom-management/index.html new file mode 100644 index 0000000..ba5cb74 --- /dev/null +++ b/configuratiom-management/index.html @@ -0,0 +1,636 @@ + + + + + + + + + + + + + + + + + + + + + + + + Configuration management - NetBox Config Diff Plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Usage

+

With plugin you can push rendered configuration from NetBox to devices.

+

Supported platforms:

+
    +
  • arista_eos
  • +
  • cisco_iosxe
  • +
  • cisco_iosxr
  • +
  • cisco_nxos
  • +
  • juniper_junos
  • +
+

Plugin using scrapli-cfg for this feature.

+
+

Warning

+

If you use Juniper and render config in set commands, please read next info. +Plugin uses load override command to load config to a device, set commands load with load set. +With load set commnad you can't replace all config, because this command uses merge action. +So, please, be careful when using set commands in rendering config and pushig it with plugin, it can have unexpected side effects.

+
+

Substitutes

+

If you render not full configuration, it is acceptable to pull missing config sections from the actual configuration to render full configuration.

+
+

Note

+

If you render full configuration in NetBox, you can proceed to Configuration Request part

+
+

To do that you should create substitute.

+

Substitutes is a "tag" that needs to be replaced with output from the real device, and a regex pattern that "pulls" this section from the actual device itself.

+

Screenshot of the substitute

+

In screenshot below we add substitute for Arista PlatformSetting

+
    +
  • Name is a "tag", you should put this as jinja2 variable in your config template in NetBox
  • +
  • Regexp is a regex, that "pulls" what matched from device and replace Name jinja2 variable in config template
  • +
+

In example substitute ethernet_interfaces section will be replaced with whatever the provided pattern finds from the real device.

+

This pattern matches all ethernet interfaces on a Arista device.

+

To correctly render substitute in config template you have two options:

+
{{ "{{ ethernet_interfaces }}" }}
+
+

or

+
{% raw %}{{ ethernet_interfaces }}{% endraw %}
+
+

Config template will look like:

+

Screenshot of the config template with substitute

+

And rendered config template with substitute

+

Screenshot of the rendered template with substitute

+

Configuration Request

+

Now you let's create Configuration Request with devices you want to configure.

+
+

Warning

+

For request only accepts devices with Active status and assigned Platform, Primary IP, and PlatformSetting

+
+

Find Configuration Requests in navbar.

+

Now collect diffs for devices pressing Collecting diffs button.

+

Screenshot of the Collecting diffs button

+

On tab Diffs you can review diffs for devices.

+

Screenshot of the Diffs tab

+

To continue approve request by pressing Approve button.

+

Screenshot of the Approve button

+

Also you can cancel approve after that.

+

Screenshot of the Unapprove button

+

After approval you can see by whom configuration request is approved.

+

Screenshot of the Approved request

+

At this moment you can schedule job that will push rendered configuration to devices in configuration request by pressing schedule button.

+

Screenshot of the Schedule button

+

After that you can see by whom configuration request is scheduled and time.

+

Screenshot of the Scheduled request

+

Also you can cancel scheduled job by pressing Unschedule button.

+

Screenshot of the Unschedule button

+
+

Warning

+

Approve and Schedule buttons is accessable only to user with netbox_config_diff.approve_configurationrequest +permission

+
+
+

Warning

+

If you unapprove scheduled configuration request, scheduled job will be canceled

+
+

After scheduled job is completed you can job logs on configuration request page.

+

Screenshot of the Unschedule button

+
+

Note

+

Completed configuration requests can't be edited.

+
+

Rollback

+

If an error occurs while executing a job that pushes configurations to devices then all configured devices will be rollbacked to the previous version of the configuration.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/contributing/index.html b/contributing/index.html new file mode 100644 index 0000000..1294fa9 --- /dev/null +++ b/contributing/index.html @@ -0,0 +1,730 @@ + + + + + + + + + + + + + + + + + + + + + + + + Contributing - NetBox Config Diff Plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Contributing

+

Contributions are welcome, and they are greatly appreciated! Every little bit +helps, and credit will always be given.

+

You can contribute in many ways:

+

Types of Contributions

+

Report Bugs

+

Report bugs at https://github.com/miaow2/netbox-config-diff/issues.

+

If you are reporting a bug, please include:

+
    +
  • Your operating system name and version.
  • +
  • Any details about your local setup that might be helpful in troubleshooting.
  • +
  • Detailed steps to reproduce the bug.
  • +
+

Fix Bugs

+

Look through the GitHub issues for bugs. Anything tagged with "bug" and "help +wanted" is open to whoever wants to implement it.

+

Implement Features

+

Look through the GitHub issues for features. Anything tagged with "enhancement" +and "help wanted" is open to whoever wants to implement it.

+

Write Documentation

+

NetBox Config Diff Plugin could always use more documentation, whether as part of the +official NetBox Config Diff Plugin docs, in docstrings, or even on the web in blog posts, +articles, and such.

+

Submit Feedback

+

The best way to send feedback is to file an issue at https://github.com/miaow2/netbox-config-diff/issues.

+

If you are proposing a feature:

+
    +
  • Explain in detail how it would work.
  • +
  • Keep the scope as narrow as possible, to make it easier to implement.
  • +
  • Remember that this is a volunteer-driven project, and that contributions + are welcome :)
  • +
+

Get Started!

+

Ready to contribute? Here's how to set up netbox-config-diff for local development.

+
    +
  1. Fork the netbox-config-diff repo on GitHub.
  2. +
  3. +

    Clone your fork locally

    +

    $ git clone git@github.com:your_name_here/netbox-config-diff.git

    +
  4. +
  5. +

    Install dependencies and start your virtualenv:

    +

    $ poetry install -E test -E doc -E dev

    +
  6. +
  7. +

    Create a branch for local development:

    +

    $ git checkout -b name-of-your-bugfix-or-feature

    +

    Now you can make your changes locally.

    +
  8. +
  9. +

    When you're done making changes, check that your changes pass the + tests, including testing other Python versions, with tox:

    +

    $ poetry run tox

    +
  10. +
  11. +

    Commit your changes and push your branch to GitHub:

    +

    $ git add . +$ git commit -m "Your detailed description of your changes." +$ git push origin name-of-your-bugfix-or-feature

    +
  12. +
  13. +

    Submit a pull request through the GitHub website.

    +
  14. +
+

Pull Request Guidelines

+

Before you submit a pull request, check that it meets these guidelines:

+
    +
  1. The pull request should include tests.
  2. +
  3. If the pull request adds functionality, the docs should be updated. Put + your new functionality into a function with a docstring, and add the + feature to the list in README.md.
  4. +
  5. The pull request should work for Python 3.8, 3.9 and 3.10. Check + https://github.com/miaow2/netbox-config-diff/actions + and make sure that the tests pass for all supported Python versions.
  6. +
+

Deploying

+

A reminder for the maintainers on how to deploy. +Make sure all your changes are committed (including an entry in CHANGELOG.md). +Then run:

+
$ poetry run bump2version patch # possible: major / minor / patch
+$ git push
+$ git push --tags
+
+

GitHub Actions will then deploy to PyPI if tests pass.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..26e761d --- /dev/null +++ b/index.html @@ -0,0 +1,666 @@ + + + + + + + + + + + + + + + + + + + + + + NetBox Config Diff Plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Home

+ + + +

About

+

Collecting diffs

+

With this plugin you can find diff between the rendered configuration for a device to its actual configuration, retrieved from the device itself, or stored in DataSource. +Read about DataSources for further details.

+

Device configuration renders natively in NetBox. This feature was introduced in 3.5 version. + NetBox Labs blog post about it.

+

Plugin supports a wide list of vendors (Cisco, Juniper, Huawei, MicroTik etc.) with the help of Scrapli. Read Scrapli and scrapli-community documentations to find full list of vendors.

+

Pushing configuration

+

Also you can push rendered configuration from NetBox to device and apply it.

+

Supported platforms:

+
    +
  • arista_eos
  • +
  • cisco_iosxe
  • +
  • cisco_iosxr
  • +
  • cisco_nxos
  • +
  • juniper_junos
  • +
+

This is possible thanks to the scrapli_cfg. Read Scrapli documentation for more info.

+ + + + +

Installing

+

For adding to a NetBox Docker setup see +the general instructions for using netbox-docker with plugins.

+

Install with pip:

+
pip install netbox-config-diff
+
+

Add to local_requirements.txt:

+
echo netbox-config-diff >> local_requirements.txt
+
+

Enable the plugin in /opt/netbox/netbox/netbox/configuration.py, + or if you use netbox-docker, your /configuration/plugins.py file, + and define credentials for devices connection:

+
PLUGINS = [
+    "netbox_config_diff",
+]
+
+PLUGINS_CONFIG = {
+    "netbox_config_diff": {
+        "USERNAME": "foo",
+        "PASSWORD": "bar",
+        "AUTH_SECONDARY": "foobar",  # define here password for accessing Privileged EXEC mode, this variable is optional
+    },
+}
+
+

Run database migrations:

+
python manage.py migrate
+
+
+

Collect static from the plugin:

+
python manage.py collectstatic --noinput
+
+

Restart NetBox service:

+
systemctl restart netbox netbox-rq
+
+ + + + +

Usage

+

Read this doc about collecting diffs, for configuration management read this

+

Video

+

My presention about plugin at October NetBox community call (19.10.2023).

+

October NetBox community call

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/media/screenshots/compliance-diff.png b/media/screenshots/compliance-diff.png new file mode 100644 index 0000000..855e176 Binary files /dev/null and b/media/screenshots/compliance-diff.png differ diff --git a/media/screenshots/compliance-error.png b/media/screenshots/compliance-error.png new file mode 100644 index 0000000..50f8393 Binary files /dev/null and b/media/screenshots/compliance-error.png differ diff --git a/media/screenshots/compliance-list.png b/media/screenshots/compliance-list.png new file mode 100644 index 0000000..08d2265 Binary files /dev/null and b/media/screenshots/compliance-list.png differ diff --git a/media/screenshots/compliance-missing-extra.png b/media/screenshots/compliance-missing-extra.png new file mode 100644 index 0000000..456ffb3 Binary files /dev/null and b/media/screenshots/compliance-missing-extra.png differ diff --git a/media/screenshots/compliance-ok.png b/media/screenshots/compliance-ok.png new file mode 100644 index 0000000..4b12066 Binary files /dev/null and b/media/screenshots/compliance-ok.png differ diff --git a/media/screenshots/config-temp-substitute.png b/media/screenshots/config-temp-substitute.png new file mode 100644 index 0000000..700aba6 Binary files /dev/null and b/media/screenshots/config-temp-substitute.png differ diff --git a/media/screenshots/cr-approve-button.png b/media/screenshots/cr-approve-button.png new file mode 100644 index 0000000..99fd64d Binary files /dev/null and b/media/screenshots/cr-approve-button.png differ diff --git a/media/screenshots/cr-approved.png b/media/screenshots/cr-approved.png new file mode 100644 index 0000000..d545f7a Binary files /dev/null and b/media/screenshots/cr-approved.png differ diff --git a/media/screenshots/cr-collecting-diff-button.png b/media/screenshots/cr-collecting-diff-button.png new file mode 100644 index 0000000..1fdf224 Binary files /dev/null and b/media/screenshots/cr-collecting-diff-button.png differ diff --git a/media/screenshots/cr-completed.png b/media/screenshots/cr-completed.png new file mode 100644 index 0000000..368a75c Binary files /dev/null and b/media/screenshots/cr-completed.png differ diff --git a/media/screenshots/cr-created.png b/media/screenshots/cr-created.png new file mode 100644 index 0000000..5733189 Binary files /dev/null and b/media/screenshots/cr-created.png differ diff --git a/media/screenshots/cr-diffs-tab.png b/media/screenshots/cr-diffs-tab.png new file mode 100644 index 0000000..4134a86 Binary files /dev/null and b/media/screenshots/cr-diffs-tab.png differ diff --git a/media/screenshots/cr-job-log.png b/media/screenshots/cr-job-log.png new file mode 100644 index 0000000..6df5508 Binary files /dev/null and b/media/screenshots/cr-job-log.png differ diff --git a/media/screenshots/cr-schedule-button.png b/media/screenshots/cr-schedule-button.png new file mode 100644 index 0000000..7baf7cc Binary files /dev/null and b/media/screenshots/cr-schedule-button.png differ diff --git a/media/screenshots/cr-scheduled.png b/media/screenshots/cr-scheduled.png new file mode 100644 index 0000000..a61304b Binary files /dev/null and b/media/screenshots/cr-scheduled.png differ diff --git a/media/screenshots/cr-unapprove-button.png b/media/screenshots/cr-unapprove-button.png new file mode 100644 index 0000000..7a0ef97 Binary files /dev/null and b/media/screenshots/cr-unapprove-button.png differ diff --git a/media/screenshots/cr-unschedule-button.png b/media/screenshots/cr-unschedule-button.png new file mode 100644 index 0000000..aaf0c90 Binary files /dev/null and b/media/screenshots/cr-unschedule-button.png differ diff --git a/media/screenshots/navbar.png b/media/screenshots/navbar.png new file mode 100644 index 0000000..7b0dba2 Binary files /dev/null and b/media/screenshots/navbar.png differ diff --git a/media/screenshots/platformsetting.png b/media/screenshots/platformsetting.png new file mode 100644 index 0000000..f18b355 Binary files /dev/null and b/media/screenshots/platformsetting.png differ diff --git a/media/screenshots/render-temp-substitute.png b/media/screenshots/render-temp-substitute.png new file mode 100644 index 0000000..d3ed33f Binary files /dev/null and b/media/screenshots/render-temp-substitute.png differ diff --git a/media/screenshots/script-list.png b/media/screenshots/script-list.png new file mode 100644 index 0000000..3d05dd6 Binary files /dev/null and b/media/screenshots/script-list.png differ diff --git a/media/screenshots/script.png b/media/screenshots/script.png new file mode 100644 index 0000000..498aeb6 Binary files /dev/null and b/media/screenshots/script.png differ diff --git a/media/screenshots/substitute.png b/media/screenshots/substitute.png new file mode 100644 index 0000000..0a9a228 Binary files /dev/null and b/media/screenshots/substitute.png differ diff --git a/screenshots/index.html b/screenshots/index.html new file mode 100644 index 0000000..975ce6a --- /dev/null +++ b/screenshots/index.html @@ -0,0 +1,485 @@ + + + + + + + + + + + + + + + + + + + + + + + + Screenshots - NetBox Config Diff Plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Screenshots

+

Screenshot of the compliance list

+

Also result is storing rendered and actual configurations from device.

+

Compliance finished with error

+

Screenshot of the compliance error

+

Render diff between configurations

+

Screenshot of diff

+

No diff

+

Screenshot of the compliance ok

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 0000000..c802f20 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#about","title":"About","text":""},{"location":"#collecting-diffs","title":"Collecting diffs","text":"

With this plugin you can find diff between the rendered configuration for a device to its actual configuration, retrieved from the device itself, or stored in DataSource. Read about DataSources for further details.

Device configuration renders natively in NetBox. This feature was introduced in 3.5 version. NetBox Labs blog post about it.

Plugin supports a wide list of vendors (Cisco, Juniper, Huawei, MicroTik etc.) with the help of Scrapli. Read Scrapli and scrapli-community documentations to find full list of vendors.

"},{"location":"#pushing-configuration","title":"Pushing configuration","text":"

Also you can push rendered configuration from NetBox to device and apply it.

Supported platforms:

  • arista_eos
  • cisco_iosxe
  • cisco_iosxr
  • cisco_nxos
  • juniper_junos

This is possible thanks to the scrapli_cfg. Read Scrapli documentation for more info.

"},{"location":"#installing","title":"Installing","text":"

For adding to a NetBox Docker setup see the general instructions for using netbox-docker with plugins.

Install with pip:

pip install netbox-config-diff\n

Add to local_requirements.txt:

echo netbox-config-diff >> local_requirements.txt\n

Enable the plugin in /opt/netbox/netbox/netbox/configuration.py, or if you use netbox-docker, your /configuration/plugins.py file, and define credentials for devices connection:

PLUGINS = [\n    \"netbox_config_diff\",\n]\n\nPLUGINS_CONFIG = {\n    \"netbox_config_diff\": {\n        \"USERNAME\": \"foo\",\n        \"PASSWORD\": \"bar\",\n        \"AUTH_SECONDARY\": \"foobar\",  # define here password for accessing Privileged EXEC mode, this variable is optional\n    },\n}\n

Run database migrations:

python manage.py migrate\n\n

Collect static from the plugin:

python manage.py collectstatic --noinput\n

Restart NetBox service:

systemctl restart netbox netbox-rq\n
"},{"location":"#usage","title":"Usage","text":"

Read this doc about collecting diffs, for configuration management read this

"},{"location":"#video","title":"Video","text":"

My presention about plugin at October NetBox community call (19.10.2023).

"},{"location":"changelog/","title":"Changelog","text":""},{"location":"changelog/#220-2024-02-06","title":"2.2.0 (2024-02-06)","text":"
  • #47 Move plugin to separete menu item in navbar and add tab for devices with compliance result
  • #50 Add template field for device name in DataSource to ConfigDiffScript
  • #53 Add netbox-rq to installation process docs
"},{"location":"changelog/#210-2023-10-26","title":"2.1.0 (2023-10-26)","text":"
  • #35 Add ability to define password for accessing priviliged exec mode
  • #37 Add DeviceRole field to CollectDiffScript
  • #38 Remove config template filter for devices filed in forms
  • #39 Add Status field to CollectDiffScript
  • #43 ConfigDiffScript does not create empty changelog entries
"},{"location":"changelog/#201-2023-10-22","title":"2.0.1 (2023-10-22)","text":"
  • #33 Fix failing migrations on fresh database install
"},{"location":"changelog/#200-2023-10-18","title":"2.0.0 (2023-10-18)","text":"
  • #25 Add configuration management
"},{"location":"changelog/#122-2023-09-29","title":"1.2.2 (2023-09-29)","text":"
  • #28 Add legacy ssh algorithms to support old OS versions
"},{"location":"changelog/#121-2023-09-07","title":"1.2.1 (2023-09-07)","text":"
  • #26 Add dark theme for diff
"},{"location":"changelog/#120-2023-08-23","title":"1.2.0 (2023-08-23)","text":"
  • #20 Add integration with netbox-secrets plugin
"},{"location":"changelog/#111-2023-08-13","title":"1.1.1 (2023-08-13)","text":"
  • #1 Add tests
"},{"location":"changelog/#110-2023-08-01","title":"1.1.0 (2023-08-01)","text":"
  • #16 Add missing and extra config lines
"},{"location":"changelog/#100-2023-07-23","title":"1.0.0 (2023-07-23)","text":"
  • Publish on PyPI.
"},{"location":"changelog/#011-2023-07-23","title":"0.1.1 (2023-07-23)","text":"
  • Add DataSoures as sources for device configurations.
  • Add docs.
"},{"location":"changelog/#010-2023-07-09","title":"0.1.0 (2023-07-09)","text":"
  • First release.
"},{"location":"colliecting-diffs/","title":"Usage","text":"

In navbar serach for Config Diff Plugin menu

"},{"location":"colliecting-diffs/#platformsetting","title":"PlatformSetting","text":"

Add PlatformSetting objects for your platforms in NetBox. This model contains info about how to connect and what to collect from device.

Define:

  • Driver for Scrapli, you can find all drivers in Scrapli and Scrapli community documentation.
  • Command to collect configuration
  • Optional regex patterns to exclude from actual config, specify each pattern on a new line

With regexps you can exclude big parts of the configuration and compare tiny configuration pieces (only ntp configuration).

You can test regexp on the site regex101.com.

"},{"location":"colliecting-diffs/#script","title":"Script","text":"

Plugin adds a custom script ConfigDiffScript that runs all logic about diff calculations and connections to devices. You can find scripts list in navbar Customization -> Scripts.

In the script, you can define a site or role, on which devices run compliance, or devices. If you define all fields, script will run only on devices from Devices field

Warning

Script runs only on devices with assigned Primary IP, Platform and PlatformSetting

If you have configs in NetBox DataSource, you can define it, the script instead of connecting to devices will find configs in DataSource by device's names.

Warning

Be sure that DataSource is synced and has the latest data

Note

Only synced DataSources are acceptable

If in your DataSource config names are different from the hostnames of the devices, you can specify config name with Jinja2 template in Name template field. Reference device with {{ object }} variable.

For example, config name is virtual chassis name plus config (switchname-config) and your devices names are switchname1, switchname2 and etc.

You can define Jinja2 template with logic to use virtual chassis name if device is in chassis, else use device name:

{% if object.virtual_chassis %}{{ object.virtual_chassis.name }}-config{% else %}{{ object.name }}{% endif %}\n

"},{"location":"colliecting-diffs/#results","title":"Results","text":"

After script is done you can find results in Config Compliances menu. Each device has its own result.

Also result is storing rendered and actual configurations from devices.

With the help of netutils library plugin stores missing and extra config lines.

Supported platforms for missing/extra lines:

  • Arista EOS (arista_eos)
  • Aruba AOSCX (aruba_aoscx)
  • Cisco AireOS (cisco_aireos)
  • Cisco ASA (cisco_asa)
  • Cisco IOS-XE (cisco_iosxe)
  • Cisco IOS-XR (cisco_iosxr)
  • Cisco NX-OS (cisco_nxos)
  • Juniper JunOS (juniper_junos)
  • Mikrotik RouterOS (mikrotik_routeros)
  • Nokia SROS (nokia_sros)
  • PaloAlto PanOS (paloalto_panos)
  • Ruckus FastIron (ruckus_fastiron)

Compliance finished with error

Render diff between configurations

No diff

"},{"location":"configuratiom-management/","title":"Usage","text":"

With plugin you can push rendered configuration from NetBox to devices.

Supported platforms:

  • arista_eos
  • cisco_iosxe
  • cisco_iosxr
  • cisco_nxos
  • juniper_junos

Plugin using scrapli-cfg for this feature.

Warning

If you use Juniper and render config in set commands, please read next info. Plugin uses load override command to load config to a device, set commands load with load set. With load set commnad you can't replace all config, because this command uses merge action. So, please, be careful when using set commands in rendering config and pushig it with plugin, it can have unexpected side effects.

"},{"location":"configuratiom-management/#substitutes","title":"Substitutes","text":"

If you render not full configuration, it is acceptable to pull missing config sections from the actual configuration to render full configuration.

Note

If you render full configuration in NetBox, you can proceed to Configuration Request part

To do that you should create substitute.

Substitutes is a \"tag\" that needs to be replaced with output from the real device, and a regex pattern that \"pulls\" this section from the actual device itself.

In screenshot below we add substitute for Arista PlatformSetting

  • Name is a \"tag\", you should put this as jinja2 variable in your config template in NetBox
  • Regexp is a regex, that \"pulls\" what matched from device and replace Name jinja2 variable in config template

In example substitute ethernet_interfaces section will be replaced with whatever the provided pattern finds from the real device.

This pattern matches all ethernet interfaces on a Arista device.

To correctly render substitute in config template you have two options:

{{ \"{{ ethernet_interfaces }}\" }}\n

or

{% raw %}{{ ethernet_interfaces }}{% endraw %}\n

Config template will look like:

And rendered config template with substitute

"},{"location":"configuratiom-management/#configuration-request","title":"Configuration Request","text":"

Now you let's create Configuration Request with devices you want to configure.

Warning

For request only accepts devices with Active status and assigned Platform, Primary IP, and PlatformSetting

Find Configuration Requests in navbar.

Now collect diffs for devices pressing Collecting diffs button.

On tab Diffs you can review diffs for devices.

To continue approve request by pressing Approve button.

Also you can cancel approve after that.

After approval you can see by whom configuration request is approved.

At this moment you can schedule job that will push rendered configuration to devices in configuration request by pressing schedule button.

After that you can see by whom configuration request is scheduled and time.

Also you can cancel scheduled job by pressing Unschedule button.

Warning

Approve and Schedule buttons is accessable only to user with netbox_config_diff.approve_configurationrequest permission

Warning

If you unapprove scheduled configuration request, scheduled job will be canceled

After scheduled job is completed you can job logs on configuration request page.

Note

Completed configuration requests can't be edited.

"},{"location":"configuratiom-management/#rollback","title":"Rollback","text":"

If an error occurs while executing a job that pushes configurations to devices then all configured devices will be rollbacked to the previous version of the configuration.

"},{"location":"contributing/","title":"Contributing","text":"

Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

You can contribute in many ways:

"},{"location":"contributing/#types-of-contributions","title":"Types of Contributions","text":""},{"location":"contributing/#report-bugs","title":"Report Bugs","text":"

Report bugs at https://github.com/miaow2/netbox-config-diff/issues.

If you are reporting a bug, please include:

  • Your operating system name and version.
  • Any details about your local setup that might be helpful in troubleshooting.
  • Detailed steps to reproduce the bug.
"},{"location":"contributing/#fix-bugs","title":"Fix Bugs","text":"

Look through the GitHub issues for bugs. Anything tagged with \"bug\" and \"help wanted\" is open to whoever wants to implement it.

"},{"location":"contributing/#implement-features","title":"Implement Features","text":"

Look through the GitHub issues for features. Anything tagged with \"enhancement\" and \"help wanted\" is open to whoever wants to implement it.

"},{"location":"contributing/#write-documentation","title":"Write Documentation","text":"

NetBox Config Diff Plugin could always use more documentation, whether as part of the official NetBox Config Diff Plugin docs, in docstrings, or even on the web in blog posts, articles, and such.

"},{"location":"contributing/#submit-feedback","title":"Submit Feedback","text":"

The best way to send feedback is to file an issue at https://github.com/miaow2/netbox-config-diff/issues.

If you are proposing a feature:

  • Explain in detail how it would work.
  • Keep the scope as narrow as possible, to make it easier to implement.
  • Remember that this is a volunteer-driven project, and that contributions are welcome :)
"},{"location":"contributing/#get-started","title":"Get Started!","text":"

Ready to contribute? Here's how to set up netbox-config-diff for local development.

  1. Fork the netbox-config-diff repo on GitHub.
  2. Clone your fork locally

    $ git clone git@github.com:your_name_here/netbox-config-diff.git

  3. Install dependencies and start your virtualenv:

    $ poetry install -E test -E doc -E dev

  4. Create a branch for local development:

    $ git checkout -b name-of-your-bugfix-or-feature

    Now you can make your changes locally.

  5. When you're done making changes, check that your changes pass the tests, including testing other Python versions, with tox:

    $ poetry run tox

  6. Commit your changes and push your branch to GitHub:

    $ git add . $ git commit -m \"Your detailed description of your changes.\" $ git push origin name-of-your-bugfix-or-feature

  7. Submit a pull request through the GitHub website.

"},{"location":"contributing/#pull-request-guidelines","title":"Pull Request Guidelines","text":"

Before you submit a pull request, check that it meets these guidelines:

  1. The pull request should include tests.
  2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.md.
  3. The pull request should work for Python 3.8, 3.9 and 3.10. Check https://github.com/miaow2/netbox-config-diff/actions and make sure that the tests pass for all supported Python versions.
"},{"location":"contributing/#deploying","title":"Deploying","text":"

A reminder for the maintainers on how to deploy. Make sure all your changes are committed (including an entry in CHANGELOG.md). Then run:

$ poetry run bump2version patch # possible: major / minor / patch\n$ git push\n$ git push --tags\n

GitHub Actions will then deploy to PyPI if tests pass.

"},{"location":"screenshots/","title":"Screenshots","text":"

Also result is storing rendered and actual configurations from device.

Compliance finished with error

Render diff between configurations

No diff

"},{"location":"secrets/","title":"Integration with NetBox secrets plugin","text":"

You can store credentials for devices authentification in NetBox secrets plugin.

Read NetBox secrets docs for more info.

In plugin variables define secrets roles for username (USER_SECRET_ROLE), password (PASSWORD_SECRET_ROLE) and password (SECOND_AUTH_SECRET_ROLE) for Privileged EXEC mode.

Default values for this variables are:

PLUGINS_CONFIG = {\n    \"netbox_config_diff\": {\n        \"USER_SECRET_ROLE\": \"Username\",\n        \"PASSWORD_SECRET_ROLE\": \"Password\",\n        \"SECOND_AUTH_SECRET_ROLE\": \"Second Auth\",\n    },\n}\n

Script will find secrets with these roles attached to the device and use them as credentials.

If something goes wrong, then credentials from PLUGINS_CONFIG will be used.

"}]} \ No newline at end of file diff --git a/secrets/index.html b/secrets/index.html new file mode 100644 index 0000000..9fafaaa --- /dev/null +++ b/secrets/index.html @@ -0,0 +1,494 @@ + + + + + + + + + + + + + + + + + + + + + + + + Integration with secrets - NetBox Config Diff Plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Integration with NetBox secrets plugin

+

You can store credentials for devices authentification in NetBox secrets plugin.

+

Read NetBox secrets docs for more info.

+

In plugin variables define secrets roles for username (USER_SECRET_ROLE), password (PASSWORD_SECRET_ROLE) and + password (SECOND_AUTH_SECRET_ROLE) for Privileged EXEC mode.

+

Default values for this variables are:

+
PLUGINS_CONFIG = {
+    "netbox_config_diff": {
+        "USER_SECRET_ROLE": "Username",
+        "PASSWORD_SECRET_ROLE": "Password",
+        "SECOND_AUTH_SECRET_ROLE": "Second Auth",
+    },
+}
+
+

Script will find secrets with these roles attached to the device and use them as credentials.

+

If something goes wrong, then credentials from PLUGINS_CONFIG will be used.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..a6f1fcd --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,38 @@ + + + + https://miaow2.github.io/netbox-config-diff/ + 2024-04-11 + daily + + + https://miaow2.github.io/netbox-config-diff/changelog/ + 2024-04-11 + daily + + + https://miaow2.github.io/netbox-config-diff/colliecting-diffs/ + 2024-04-11 + daily + + + https://miaow2.github.io/netbox-config-diff/configuratiom-management/ + 2024-04-11 + daily + + + https://miaow2.github.io/netbox-config-diff/contributing/ + 2024-04-11 + daily + + + https://miaow2.github.io/netbox-config-diff/screenshots/ + 2024-04-11 + daily + + + https://miaow2.github.io/netbox-config-diff/secrets/ + 2024-04-11 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000..cc3769c Binary files /dev/null and b/sitemap.xml.gz differ