mirror of https://gerrit.hackerspace.pl/hscloud
Merge branch 'master' of /home/q3k/Projects/hscloud/go/src/code.hackerspace.pl/q3k/topo
commit
ff9995ce61
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
Viz.js 2.0.0 (Graphviz 2.40.1, Expat 2.2.5, Emscripten 1.37.36)
|
||||
Copyright (c) 2014-2018 Michael Daines
|
||||
Licensed under MIT license
|
||||
|
||||
This distribution contains other software in object code form:
|
||||
|
||||
Graphviz
|
||||
Licensed under Eclipse Public License - v 1.0
|
||||
http://www.graphviz.org
|
||||
|
||||
Expat
|
||||
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd and Clark Cooper
|
||||
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers.
|
||||
Licensed under MIT license
|
||||
http://www.libexpat.org
|
||||
|
||||
zlib
|
||||
Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
||||
http://www.zlib.net/zlib_license.html
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global.Viz = factory());
|
||||
}(this, (function () { 'use strict';
|
||||
|
||||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
|
||||
return typeof obj;
|
||||
} : function (obj) {
|
||||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
||||
};
|
||||
|
||||
var classCallCheck = function (instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
};
|
||||
|
||||
var createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
var _extends = Object.assign || function (target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i];
|
||||
|
||||
for (var key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
var WorkerWrapper = function () {
|
||||
function WorkerWrapper(worker) {
|
||||
var _this = this;
|
||||
|
||||
classCallCheck(this, WorkerWrapper);
|
||||
|
||||
this.worker = worker;
|
||||
this.listeners = [];
|
||||
this.nextId = 0;
|
||||
|
||||
this.worker.addEventListener('message', function (event) {
|
||||
var id = event.data.id;
|
||||
var error = event.data.error;
|
||||
var result = event.data.result;
|
||||
|
||||
_this.listeners[id](error, result);
|
||||
delete _this.listeners[id];
|
||||
});
|
||||
}
|
||||
|
||||
createClass(WorkerWrapper, [{
|
||||
key: 'render',
|
||||
value: function render(src, options) {
|
||||
var _this2 = this;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var id = _this2.nextId++;
|
||||
|
||||
_this2.listeners[id] = function (error, result) {
|
||||
if (error) {
|
||||
reject(new Error(error.message, error.fileName, error.lineNumber));
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
};
|
||||
|
||||
_this2.worker.postMessage({ id: id, src: src, options: options });
|
||||
});
|
||||
}
|
||||
}]);
|
||||
return WorkerWrapper;
|
||||
}();
|
||||
|
||||
var ModuleWrapper = function ModuleWrapper(module, render) {
|
||||
classCallCheck(this, ModuleWrapper);
|
||||
|
||||
var instance = module();
|
||||
this.render = function (src, options) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
try {
|
||||
resolve(render(instance, src, options));
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
|
||||
|
||||
|
||||
function b64EncodeUnicode(str) {
|
||||
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
|
||||
return String.fromCharCode('0x' + p1);
|
||||
}));
|
||||
}
|
||||
|
||||
function defaultScale() {
|
||||
if ('devicePixelRatio' in window && window.devicePixelRatio > 1) {
|
||||
return window.devicePixelRatio;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
function svgXmlToImageElement(svgXml) {
|
||||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
||||
_ref$scale = _ref.scale,
|
||||
scale = _ref$scale === undefined ? defaultScale() : _ref$scale,
|
||||
_ref$mimeType = _ref.mimeType,
|
||||
mimeType = _ref$mimeType === undefined ? "image/png" : _ref$mimeType,
|
||||
_ref$quality = _ref.quality,
|
||||
quality = _ref$quality === undefined ? 1 : _ref$quality;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var svgImage = new Image();
|
||||
|
||||
svgImage.onload = function () {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = svgImage.width * scale;
|
||||
canvas.height = svgImage.height * scale;
|
||||
|
||||
var context = canvas.getContext("2d");
|
||||
context.drawImage(svgImage, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
canvas.toBlob(function (blob) {
|
||||
var image = new Image();
|
||||
image.src = URL.createObjectURL(blob);
|
||||
image.width = svgImage.width;
|
||||
image.height = svgImage.height;
|
||||
|
||||
resolve(image);
|
||||
}, mimeType, quality);
|
||||
};
|
||||
|
||||
svgImage.onerror = function (e) {
|
||||
var error;
|
||||
|
||||
if ('error' in e) {
|
||||
error = e.error;
|
||||
} else {
|
||||
error = new Error('Error loading SVG');
|
||||
}
|
||||
|
||||
reject(error);
|
||||
};
|
||||
|
||||
svgImage.src = 'data:image/svg+xml;base64,' + b64EncodeUnicode(svgXml);
|
||||
});
|
||||
}
|
||||
|
||||
function svgXmlToImageElementFabric(svgXml) {
|
||||
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
||||
_ref2$scale = _ref2.scale,
|
||||
scale = _ref2$scale === undefined ? defaultScale() : _ref2$scale,
|
||||
_ref2$mimeType = _ref2.mimeType,
|
||||
mimeType = _ref2$mimeType === undefined ? 'image/png' : _ref2$mimeType,
|
||||
_ref2$quality = _ref2.quality,
|
||||
quality = _ref2$quality === undefined ? 1 : _ref2$quality;
|
||||
|
||||
var multiplier = scale;
|
||||
|
||||
var format = void 0;
|
||||
if (mimeType == 'image/jpeg') {
|
||||
format = 'jpeg';
|
||||
} else if (mimeType == 'image/png') {
|
||||
format = 'png';
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
fabric.loadSVGFromString(svgXml, function (objects, options) {
|
||||
// If there's something wrong with the SVG, Fabric may return an empty array of objects. Graphviz appears to give us at least one <g> element back even given an empty graph, so we will assume an error in this case.
|
||||
if (objects.length == 0) {
|
||||
reject(new Error('Error loading SVG with Fabric'));
|
||||
}
|
||||
|
||||
var element = document.createElement("canvas");
|
||||
element.width = options.width;
|
||||
element.height = options.height;
|
||||
|
||||
var canvas = new fabric.Canvas(element, { enableRetinaScaling: false });
|
||||
var obj = fabric.util.groupSVGElements(objects, options);
|
||||
canvas.add(obj).renderAll();
|
||||
|
||||
var image = new Image();
|
||||
image.src = canvas.toDataURL({ format: format, multiplier: multiplier, quality: quality });
|
||||
image.width = options.width;
|
||||
image.height = options.height;
|
||||
|
||||
resolve(image);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var Viz = function () {
|
||||
function Viz() {
|
||||
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
||||
workerURL = _ref3.workerURL,
|
||||
worker = _ref3.worker,
|
||||
Module = _ref3.Module,
|
||||
render = _ref3.render;
|
||||
|
||||
classCallCheck(this, Viz);
|
||||
|
||||
if (typeof workerURL !== 'undefined') {
|
||||
this.wrapper = new WorkerWrapper(new Worker(workerURL));
|
||||
} else if (typeof worker !== 'undefined') {
|
||||
this.wrapper = new WorkerWrapper(worker);
|
||||
} else if (typeof Module !== 'undefined' && typeof render !== 'undefined') {
|
||||
this.wrapper = new ModuleWrapper(Module, render);
|
||||
} else if (typeof Viz.Module !== 'undefined' && typeof Viz.render !== 'undefined') {
|
||||
this.wrapper = new ModuleWrapper(Viz.Module, Viz.render);
|
||||
} else {
|
||||
throw new Error('Must specify workerURL or worker option, Module and render options, or include one of full.render.js or lite.render.js after viz.js.');
|
||||
}
|
||||
}
|
||||
|
||||
createClass(Viz, [{
|
||||
key: 'renderString',
|
||||
value: function renderString(src) {
|
||||
var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
||||
_ref4$format = _ref4.format,
|
||||
format = _ref4$format === undefined ? 'svg' : _ref4$format,
|
||||
_ref4$engine = _ref4.engine,
|
||||
engine = _ref4$engine === undefined ? 'dot' : _ref4$engine,
|
||||
_ref4$files = _ref4.files,
|
||||
files = _ref4$files === undefined ? [] : _ref4$files,
|
||||
_ref4$images = _ref4.images,
|
||||
images = _ref4$images === undefined ? [] : _ref4$images,
|
||||
_ref4$yInvert = _ref4.yInvert,
|
||||
yInvert = _ref4$yInvert === undefined ? false : _ref4$yInvert;
|
||||
|
||||
for (var i = 0; i < images.length; i++) {
|
||||
files.push({
|
||||
path: images[i].path,
|
||||
data: '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n<svg width="' + images[i].width + '" height="' + images[i].height + '"></svg>'
|
||||
});
|
||||
}
|
||||
|
||||
return this.wrapper.render(src, { format: format, engine: engine, files: files, images: images, yInvert: yInvert });
|
||||
}
|
||||
}, {
|
||||
key: 'renderSVGElement',
|
||||
value: function renderSVGElement(src) {
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
|
||||
return this.renderString(src, _extends({}, options, { format: 'svg' })).then(function (str) {
|
||||
var parser = new DOMParser();
|
||||
return parser.parseFromString(str, 'image/svg+xml').documentElement;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: 'renderImageElement',
|
||||
value: function renderImageElement(src) {
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
var scale = options.scale,
|
||||
mimeType = options.mimeType,
|
||||
quality = options.quality;
|
||||
|
||||
|
||||
return this.renderString(src, _extends({}, options, { format: 'svg' })).then(function (str) {
|
||||
if ((typeof fabric === 'undefined' ? 'undefined' : _typeof(fabric)) === "object" && fabric.loadSVGFromString) {
|
||||
return svgXmlToImageElementFabric(str, { scale: scale, mimeType: mimeType, quality: quality });
|
||||
} else {
|
||||
return svgXmlToImageElement(str, { scale: scale, mimeType: mimeType, quality: quality });
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: 'renderJSONObject',
|
||||
value: function renderJSONObject(src) {
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
var format = options.format;
|
||||
|
||||
|
||||
if (format !== 'json' || format !== 'json0') {
|
||||
format = 'json';
|
||||
}
|
||||
|
||||
return this.renderString(src, _extends({}, options, { format: format })).then(function (str) {
|
||||
return JSON.parse(str);
|
||||
});
|
||||
}
|
||||
}]);
|
||||
return Viz;
|
||||
}();
|
||||
|
||||
return Viz;
|
||||
|
||||
})));
|
|
@ -0,0 +1,32 @@
|
|||
syntax = "proto3";
|
||||
|
||||
message Config {
|
||||
repeated Switch switch = 1;
|
||||
repeated Machine machine = 2;
|
||||
};
|
||||
|
||||
message Switch {
|
||||
string name = 1;
|
||||
string control_address = 2;
|
||||
message SwitchPort {
|
||||
string name = 1;
|
||||
};
|
||||
repeated SwitchPort managed_port = 3;
|
||||
message Segment {
|
||||
enum Type {
|
||||
TYPE_INVALID = 0;
|
||||
TYPE_VLAN = 1;
|
||||
}
|
||||
Type segment_type = 1;
|
||||
int32 vlan_id = 2;
|
||||
};
|
||||
repeated Segment available_segment = 4;
|
||||
};
|
||||
|
||||
message Machine {
|
||||
string name = 1;
|
||||
message Port {
|
||||
string name = 1;
|
||||
};
|
||||
repeated Port managed_port = 2;
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
syntax = "proto3";
|
||||
package connector;
|
||||
|
||||
message GetPortsRequest {
|
||||
};
|
||||
|
||||
message SwitchPort {
|
||||
enum Speed {
|
||||
SPEED_INVALID = 0;
|
||||
SPEED_100M = 1;
|
||||
SPEED_1G = 2;
|
||||
SPEED_10G = 3;
|
||||
};
|
||||
string name = 1;
|
||||
Speed speed = 2;
|
||||
enum LinkState {
|
||||
LINKSTATE_INVALID = 0;
|
||||
LINKSTATE_DOWN = 1;
|
||||
LINKSTATE_UP = 2;
|
||||
};
|
||||
LinkState link_state = 3;
|
||||
enum PortMode {
|
||||
PORTMODE_INVALID = 0;
|
||||
// Interface is bridged to a VLAN, untagged.
|
||||
PORTMODE_SWITCHPORT_UNTAGGED = 1;
|
||||
// Interfaces is bridged to several tagged 802.1q VLANs.
|
||||
PORTMODE_SWITCHPORT_TAGGED = 2;
|
||||
// Interface is in 'generic', both tagged 802.1q and untagged mode.
|
||||
PORTMODE_SWITCHPORT_GENERIC = 3;
|
||||
// Interface is routed, ie routes packets from a separate L3 network
|
||||
// and the Switch is the default gateway for machines in this network.
|
||||
PORTMODE_ROUTED = 4;
|
||||
// Interface is in a configuration state that cannot be clearly stated
|
||||
// in terms of this enum, and should be reconfigured.
|
||||
PORTMODE_MANGLED = 5;
|
||||
};
|
||||
PortMode port_mode = 4;
|
||||
// For PORTMODE_SWITCHPORT_UNTAGGED and PORTMODE_SWITCHPORT_GENERIC, the
|
||||
// VLAN ID that this interface is natively bridged to.
|
||||
int32 vlan_native = 5;
|
||||
// For PORTMODE_SWITCHPORT_TAGGED and PORTMODE_SWITCHPORT_GENERIC, the VLAN
|
||||
// IDs that the interface is bridged to using 802.1q tags.
|
||||
repeated int32 vlan_tagged = 6;
|
||||
// For PORTMODE_ROUTED
|
||||
repeated string prefixes = 7;
|
||||
// Interface MTU
|
||||
int32 mtu = 8;
|
||||
enum SpanningTreeMode {
|
||||
SPANNING_TREE_MODE_INVALID = 0;
|
||||
// Send STP BPDU, on timeout switch to Forwarding.
|
||||
SPANNING_TREE_MODE_AUTO_PORTFAST = 1;
|
||||
// Switch to Forwarding immediately on link up.
|
||||
SPANNING_TREE_MODE_PORTFAST = 2;
|
||||
};
|
||||
SpanningTreeMode spanning_tree_mode = 9;
|
||||
};
|
||||
|
||||
message GetPortsResponse {
|
||||
repeated SwitchPort ports = 1;
|
||||
};
|
||||
|
||||
service SwitchControl {
|
||||
rpc GetPorts(GetPortsRequest) returns (GetPortsResponse);
|
||||
};
|
|
@ -0,0 +1,213 @@
|
|||
package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/digitalocean/go-netbox/netbox/client"
|
||||
"github.com/digitalocean/go-netbox/netbox/client/dcim"
|
||||
"github.com/digitalocean/go-netbox/netbox/models"
|
||||
"github.com/golang/glog"
|
||||
|
||||
confpb "code.hackerspace.pl/q3k/topo/proto/config"
|
||||
)
|
||||
|
||||
type MachinePort struct {
|
||||
Machine *Machine
|
||||
OtherEnd *SwitchPort
|
||||
Name string
|
||||
}
|
||||
|
||||
type SwitchPort struct {
|
||||
Switch *Switch
|
||||
OtherEnd *MachinePort
|
||||
Name string
|
||||
}
|
||||
|
||||
type Machine struct {
|
||||
Name string
|
||||
Complete bool
|
||||
|
||||
Ports map[string]*MachinePort
|
||||
}
|
||||
|
||||
type Switch struct {
|
||||
Name string
|
||||
Complete bool
|
||||
|
||||
Ports map[string]*SwitchPort
|
||||
}
|
||||
|
||||
type Graph struct {
|
||||
Switches map[string]*Switch
|
||||
Machines map[string]*Machine
|
||||
Mu sync.RWMutex
|
||||
}
|
||||
|
||||
func New() *Graph {
|
||||
return &Graph{
|
||||
Switches: make(map[string]*Switch),
|
||||
Machines: make(map[string]*Machine),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Graph) RemoveMachine(name string) {
|
||||
glog.Infof("Removed machine %q", name)
|
||||
}
|
||||
|
||||
func (g *Graph) RemoveSwitch(name string) {
|
||||
glog.Infof("Removed switch %q", name)
|
||||
}
|
||||
|
||||
func (g *Graph) LoadConfig(conf *confpb.Config) error {
|
||||
loadedMachines := make(map[string]bool)
|
||||
loadedSwitches := make(map[string]bool)
|
||||
|
||||
// Add new machines and switches.
|
||||
for _, machinepb := range conf.Machine {
|
||||
if machinepb.Name == "" {
|
||||
return fmt.Errorf("empty machine name")
|
||||
}
|
||||
if loadedMachines[machinepb.Name] {
|
||||
return fmt.Errorf("duplicate machine name: %v", machinepb.Name)
|
||||
}
|
||||
machine, ok := g.Machines[machinepb.Name]
|
||||
if !ok {
|
||||
machine = &Machine{
|
||||
Name: machinepb.Name,
|
||||
Ports: make(map[string]*MachinePort),
|
||||
}
|
||||
for _, portpb := range machinepb.ManagedPort {
|
||||
machine.Ports[portpb.Name] = &MachinePort{
|
||||
Name: portpb.Name,
|
||||
Machine: machine,
|
||||
}
|
||||
}
|
||||
g.Machines[machinepb.Name] = machine
|
||||
glog.Infof("Added machine %q with %d managed ports", machine.Name, len(machine.Ports))
|
||||
}
|
||||
machine.Complete = false
|
||||
loadedMachines[machinepb.Name] = true
|
||||
}
|
||||
for _, switchpb := range conf.Switch {
|
||||
if switchpb.Name == "" {
|
||||
return fmt.Errorf("empty switch name")
|
||||
}
|
||||
if loadedSwitches[switchpb.Name] {
|
||||
return fmt.Errorf("duplicate switch name: %v", switchpb.Name)
|
||||
}
|
||||
if loadedMachines[switchpb.Name] {
|
||||
return fmt.Errorf("switch name collides with machine name: %v", switchpb.Name)
|
||||
}
|
||||
sw, ok := g.Switches[switchpb.Name]
|
||||
if !ok {
|
||||
sw = &Switch{
|
||||
Name: switchpb.Name,
|
||||
Ports: make(map[string]*SwitchPort),
|
||||
}
|
||||
for _, portpb := range switchpb.ManagedPort {
|
||||
sw.Ports[portpb.Name] = &SwitchPort{
|
||||
Name: portpb.Name,
|
||||
Switch: sw,
|
||||
}
|
||||
}
|
||||
g.Switches[switchpb.Name] = sw
|
||||
glog.Infof("Added switch %q with %d managed ports", sw.Name, len(sw.Ports))
|
||||
}
|
||||
sw.Complete = false
|
||||
loadedSwitches[switchpb.Name] = true
|
||||
}
|
||||
|
||||
// Remove old machines and switches.
|
||||
removeMachines := make(map[string]bool)
|
||||
removeSwitches := make(map[string]bool)
|
||||
for name, _ := range g.Switches {
|
||||
if !loadedSwitches[name] {
|
||||
removeSwitches[name] = true
|
||||
}
|
||||
}
|
||||
for name, _ := range g.Machines {
|
||||
if !loadedMachines[name] {
|
||||
removeMachines[name] = true
|
||||
}
|
||||
}
|
||||
for name, _ := range removeMachines {
|
||||
g.RemoveMachine(name)
|
||||
}
|
||||
for name, _ := range removeSwitches {
|
||||
g.RemoveSwitch(name)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (g *Graph) FeedFromNetbox(ctx context.Context, nb *client.NetBox) error {
|
||||
// Clear all connections first, because it's easier that way.
|
||||
for _, machine := range g.Machines {
|
||||
for _, port := range machine.Ports {
|
||||
port.OtherEnd = nil
|
||||
}
|
||||
}
|
||||
for _, sw := range g.Switches {
|
||||
for _, port := range sw.Ports {
|
||||
port.OtherEnd = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Load new connections.
|
||||
// Iterating over just machines should be fine if all connections are
|
||||
// guaranteed to be between machines and switches (which is the model for
|
||||
// now).
|
||||
for _, machine := range g.Machines {
|
||||
req := &dcim.DcimInterfaceConnectionsListParams{
|
||||
Device: &machine.Name,
|
||||
Context: ctx,
|
||||
}
|
||||
res, err := nb.Dcim.DcimInterfaceConnectionsList(req, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while querying information about %q: %v", machine.Name, err)
|
||||
}
|
||||
for _, connection := range res.Payload.Results {
|
||||
ia := connection.InterfaceA
|
||||
ib := connection.InterfaceB
|
||||
if ia == nil || ib == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Find which way this thing actually connects.
|
||||
var thisSide, otherSide *models.PeerInterface
|
||||
if ia.Device.Name == machine.Name {
|
||||
thisSide = ia
|
||||
otherSide = ib
|
||||
} else if ib.Device.Name == machine.Name {
|
||||
thisSide = ib
|
||||
otherSide = ia
|
||||
} else {
|
||||
glog.Warning("Netbox connectivity for %q reported a link without it involced..?", machine.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
thisPort, ok := machine.Ports[*thisSide.Name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
sw, ok := g.Switches[otherSide.Device.Name]
|
||||
if !ok {
|
||||
glog.Warningf("Machine %q port %q is managed but connected to unknown device %q", machine.Name, thisPort.Name, otherSide.Device.Name)
|
||||
continue
|
||||
}
|
||||
otherPort, ok := sw.Ports[*otherSide.Name]
|
||||
if !ok {
|
||||
glog.Warningf("Machine %q port %q is managed but connected to unmanaged port %q on %q", machine.Name, thisPort.Name, otherSide.Name, sw.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
// Connect the two together!
|
||||
thisPort.OtherEnd = otherPort
|
||||
otherPort.OtherEnd = thisPort
|
||||
glog.Infof("Connected: %s/%s <-> %s/%s", machine.Name, thisPort.Name, sw.Name, otherPort.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package main
|
||||
|
||||
//go:generate packr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
|
||||
"code.hackerspace.pl/q3k/mirko"
|
||||
"github.com/digitalocean/go-netbox/netbox"
|
||||
"github.com/digitalocean/go-netbox/netbox/client"
|
||||
"github.com/golang/glog"
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
confpb "code.hackerspace.pl/q3k/topo/proto/config"
|
||||
|
||||
"code.hackerspace.pl/q3k/topo/graph"
|
||||
"code.hackerspace.pl/q3k/topo/state"
|
||||
)
|
||||
|
||||
var (
|
||||
flagConfigPath string
|
||||
flagNetboxHost string
|
||||
flagNetboxAPIKey string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Set("logtostderr", "true")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.StringVar(&flagConfigPath, "config_path", "./topo.pb.text", "Text proto configuration of Topo (per config.proto)")
|
||||
flag.StringVar(&flagNetboxHost, "netbox_host", "netbox.bgp.wtf", "Netbox host")
|
||||
flag.StringVar(&flagNetboxAPIKey, "netbox_api_key", "", "Netbox API key")
|
||||
flag.Parse()
|
||||
|
||||
m := mirko.New()
|
||||
if err := m.Listen(); err != nil {
|
||||
glog.Exitf("Listen(): %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
data, err := ioutil.ReadFile(flagConfigPath)
|
||||
if err != nil {
|
||||
glog.Exitf("Could not read config: %v", err)
|
||||
}
|
||||
|
||||
config := confpb.Config{}
|
||||
proto.UnmarshalText(string(data), &config)
|
||||
|
||||
stm := state.NewManager()
|
||||
err = stm.FetchState(ctx, &config)
|
||||
if err != nil {
|
||||
glog.Exitf("Initial state fetch failed: %v", err)
|
||||
}
|
||||
|
||||
gr := graph.New()
|
||||
err = gr.LoadConfig(&config)
|
||||
if err != nil {
|
||||
glog.Exitf("Initial config load failed: %v", err)
|
||||
}
|
||||
|
||||
client.DefaultSchemes = []string{"https"}
|
||||
nb := netbox.NewNetboxWithAPIKey(flagNetboxHost, flagNetboxAPIKey)
|
||||
err = gr.FeedFromNetbox(ctx, nb)
|
||||
if err != nil {
|
||||
glog.Exitf("Initial netbox feed failed: %v", err)
|
||||
}
|
||||
|
||||
s := NewService(gr, stm)
|
||||
s.Setup(m)
|
||||
|
||||
if err := m.Serve(); err != nil {
|
||||
glog.Exitf("Serve(): %v", err)
|
||||
}
|
||||
|
||||
select {}
|
||||
}
|
|
@ -0,0 +1,365 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: config.proto
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Switch_Segment_Type int32
|
||||
|
||||
const (
|
||||
Switch_Segment_TYPE_INVALID Switch_Segment_Type = 0
|
||||
Switch_Segment_TYPE_VLAN Switch_Segment_Type = 1
|
||||
)
|
||||
|
||||
var Switch_Segment_Type_name = map[int32]string{
|
||||
0: "TYPE_INVALID",
|
||||
1: "TYPE_VLAN",
|
||||
}
|
||||
|
||||
var Switch_Segment_Type_value = map[string]int32{
|
||||
"TYPE_INVALID": 0,
|
||||
"TYPE_VLAN": 1,
|
||||
}
|
||||
|
||||
func (x Switch_Segment_Type) String() string {
|
||||
return proto.EnumName(Switch_Segment_Type_name, int32(x))
|
||||
}
|
||||
|
||||
func (Switch_Segment_Type) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_3eaf2c85e69e9ea4, []int{1, 1, 0}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Switch []*Switch `protobuf:"bytes,1,rep,name=switch,proto3" json:"switch,omitempty"`
|
||||
Machine []*Machine `protobuf:"bytes,2,rep,name=machine,proto3" json:"machine,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Config) Reset() { *m = Config{} }
|
||||
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||
func (*Config) ProtoMessage() {}
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_3eaf2c85e69e9ea4, []int{0}
|
||||
}
|
||||
|
||||
func (m *Config) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Config.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Config) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Config.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Config) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Config.Merge(m, src)
|
||||
}
|
||||
func (m *Config) XXX_Size() int {
|
||||
return xxx_messageInfo_Config.Size(m)
|
||||
}
|
||||
func (m *Config) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Config.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Config proto.InternalMessageInfo
|
||||
|
||||
func (m *Config) GetSwitch() []*Switch {
|
||||
if m != nil {
|
||||
return m.Switch
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Config) GetMachine() []*Machine {
|
||||
if m != nil {
|
||||
return m.Machine
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Switch struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
ControlAddress string `protobuf:"bytes,2,opt,name=control_address,json=controlAddress,proto3" json:"control_address,omitempty"`
|
||||
ManagedPort []*Switch_SwitchPort `protobuf:"bytes,3,rep,name=managed_port,json=managedPort,proto3" json:"managed_port,omitempty"`
|
||||
AvailableSegment []*Switch_Segment `protobuf:"bytes,4,rep,name=available_segment,json=availableSegment,proto3" json:"available_segment,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Switch) Reset() { *m = Switch{} }
|
||||
func (m *Switch) String() string { return proto.CompactTextString(m) }
|
||||
func (*Switch) ProtoMessage() {}
|
||||
func (*Switch) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_3eaf2c85e69e9ea4, []int{1}
|
||||
}
|
||||
|
||||
func (m *Switch) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Switch.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Switch) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Switch.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Switch) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Switch.Merge(m, src)
|
||||
}
|
||||
func (m *Switch) XXX_Size() int {
|
||||
return xxx_messageInfo_Switch.Size(m)
|
||||
}
|
||||
func (m *Switch) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Switch.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Switch proto.InternalMessageInfo
|
||||
|
||||
func (m *Switch) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Switch) GetControlAddress() string {
|
||||
if m != nil {
|
||||
return m.ControlAddress
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Switch) GetManagedPort() []*Switch_SwitchPort {
|
||||
if m != nil {
|
||||
return m.ManagedPort
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Switch) GetAvailableSegment() []*Switch_Segment {
|
||||
if m != nil {
|
||||
return m.AvailableSegment
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Switch_SwitchPort struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Switch_SwitchPort) Reset() { *m = Switch_SwitchPort{} }
|
||||
func (m *Switch_SwitchPort) String() string { return proto.CompactTextString(m) }
|
||||
func (*Switch_SwitchPort) ProtoMessage() {}
|
||||
func (*Switch_SwitchPort) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_3eaf2c85e69e9ea4, []int{1, 0}
|
||||
}
|
||||
|
||||
func (m *Switch_SwitchPort) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Switch_SwitchPort.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Switch_SwitchPort) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Switch_SwitchPort.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Switch_SwitchPort) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Switch_SwitchPort.Merge(m, src)
|
||||
}
|
||||
func (m *Switch_SwitchPort) XXX_Size() int {
|
||||
return xxx_messageInfo_Switch_SwitchPort.Size(m)
|
||||
}
|
||||
func (m *Switch_SwitchPort) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Switch_SwitchPort.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Switch_SwitchPort proto.InternalMessageInfo
|
||||
|
||||
func (m *Switch_SwitchPort) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Switch_Segment struct {
|
||||
SegmentType Switch_Segment_Type `protobuf:"varint,1,opt,name=segment_type,json=segmentType,proto3,enum=Switch_Segment_Type" json:"segment_type,omitempty"`
|
||||
VlanId int32 `protobuf:"varint,2,opt,name=vlan_id,json=vlanId,proto3" json:"vlan_id,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Switch_Segment) Reset() { *m = Switch_Segment{} }
|
||||
func (m *Switch_Segment) String() string { return proto.CompactTextString(m) }
|
||||
func (*Switch_Segment) ProtoMessage() {}
|
||||
func (*Switch_Segment) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_3eaf2c85e69e9ea4, []int{1, 1}
|
||||
}
|
||||
|
||||
func (m *Switch_Segment) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Switch_Segment.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Switch_Segment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Switch_Segment.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Switch_Segment) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Switch_Segment.Merge(m, src)
|
||||
}
|
||||
func (m *Switch_Segment) XXX_Size() int {
|
||||
return xxx_messageInfo_Switch_Segment.Size(m)
|
||||
}
|
||||
func (m *Switch_Segment) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Switch_Segment.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Switch_Segment proto.InternalMessageInfo
|
||||
|
||||
func (m *Switch_Segment) GetSegmentType() Switch_Segment_Type {
|
||||
if m != nil {
|
||||
return m.SegmentType
|
||||
}
|
||||
return Switch_Segment_TYPE_INVALID
|
||||
}
|
||||
|
||||
func (m *Switch_Segment) GetVlanId() int32 {
|
||||
if m != nil {
|
||||
return m.VlanId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Machine struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
ManagedPort []*Machine_Port `protobuf:"bytes,2,rep,name=managed_port,json=managedPort,proto3" json:"managed_port,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Machine) Reset() { *m = Machine{} }
|
||||
func (m *Machine) String() string { return proto.CompactTextString(m) }
|
||||
func (*Machine) ProtoMessage() {}
|
||||
func (*Machine) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_3eaf2c85e69e9ea4, []int{2}
|
||||
}
|
||||
|
||||
func (m *Machine) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Machine.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Machine) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Machine.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Machine) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Machine.Merge(m, src)
|
||||
}
|
||||
func (m *Machine) XXX_Size() int {
|
||||
return xxx_messageInfo_Machine.Size(m)
|
||||
}
|
||||
func (m *Machine) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Machine.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Machine proto.InternalMessageInfo
|
||||
|
||||
func (m *Machine) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Machine) GetManagedPort() []*Machine_Port {
|
||||
if m != nil {
|
||||
return m.ManagedPort
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Machine_Port struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Machine_Port) Reset() { *m = Machine_Port{} }
|
||||
func (m *Machine_Port) String() string { return proto.CompactTextString(m) }
|
||||
func (*Machine_Port) ProtoMessage() {}
|
||||
func (*Machine_Port) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_3eaf2c85e69e9ea4, []int{2, 0}
|
||||
}
|
||||
|
||||
func (m *Machine_Port) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Machine_Port.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Machine_Port) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Machine_Port.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Machine_Port) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Machine_Port.Merge(m, src)
|
||||
}
|
||||
func (m *Machine_Port) XXX_Size() int {
|
||||
return xxx_messageInfo_Machine_Port.Size(m)
|
||||
}
|
||||
func (m *Machine_Port) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Machine_Port.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Machine_Port proto.InternalMessageInfo
|
||||
|
||||
func (m *Machine_Port) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("Switch_Segment_Type", Switch_Segment_Type_name, Switch_Segment_Type_value)
|
||||
proto.RegisterType((*Config)(nil), "Config")
|
||||
proto.RegisterType((*Switch)(nil), "Switch")
|
||||
proto.RegisterType((*Switch_SwitchPort)(nil), "Switch.SwitchPort")
|
||||
proto.RegisterType((*Switch_Segment)(nil), "Switch.Segment")
|
||||
proto.RegisterType((*Machine)(nil), "Machine")
|
||||
proto.RegisterType((*Machine_Port)(nil), "Machine.Port")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("config.proto", fileDescriptor_3eaf2c85e69e9ea4) }
|
||||
|
||||
var fileDescriptor_3eaf2c85e69e9ea4 = []byte{
|
||||
// 335 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0x41, 0x4f, 0xc2, 0x30,
|
||||
0x14, 0x76, 0x30, 0x37, 0x79, 0x0c, 0x98, 0x8d, 0x89, 0xcb, 0x2e, 0x92, 0x5d, 0xe0, 0xb4, 0x18,
|
||||
0x8c, 0xf1, 0xe2, 0x85, 0xa8, 0x07, 0x12, 0x20, 0xa4, 0x12, 0x12, 0x4f, 0x4b, 0xd9, 0x2a, 0x2c,
|
||||
0x6e, 0xed, 0xb2, 0x35, 0x18, 0xee, 0xfe, 0x53, 0xff, 0x88, 0xa1, 0x2d, 0x98, 0x20, 0x9e, 0xda,
|
||||
0xef, 0x7b, 0xdf, 0xeb, 0x7b, 0xef, 0x7b, 0x05, 0x27, 0xe6, 0xec, 0x3d, 0x5d, 0x85, 0x45, 0xc9,
|
||||
0x05, 0x0f, 0x26, 0x60, 0x3d, 0x49, 0x8c, 0x6e, 0xc0, 0xaa, 0x3e, 0x53, 0x11, 0xaf, 0x3d, 0xa3,
|
||||
0x5b, 0xef, 0x37, 0x07, 0x76, 0xf8, 0x2a, 0x21, 0xd6, 0x34, 0x0a, 0xc0, 0xce, 0x49, 0xbc, 0x4e,
|
||||
0x19, 0xf5, 0x6a, 0x52, 0x71, 0x11, 0x4e, 0x14, 0xc6, 0xfb, 0x40, 0xf0, 0x5d, 0x03, 0x4b, 0xa5,
|
||||
0x21, 0x04, 0x26, 0x23, 0x39, 0xf5, 0x8c, 0xae, 0xd1, 0x6f, 0x60, 0x79, 0x47, 0x3d, 0xe8, 0xc4,
|
||||
0x9c, 0x89, 0x92, 0x67, 0x11, 0x49, 0x92, 0x92, 0x56, 0x95, 0x57, 0x93, 0xe1, 0xb6, 0xa6, 0x87,
|
||||
0x8a, 0x45, 0xf7, 0xe0, 0xe4, 0x84, 0x91, 0x15, 0x4d, 0xa2, 0x82, 0x97, 0xc2, 0xab, 0xcb, 0x82,
|
||||
0x48, 0xb7, 0xa4, 0x8f, 0x19, 0x2f, 0x05, 0x6e, 0x6a, 0xdd, 0x0e, 0xa0, 0x47, 0xb8, 0x24, 0x1b,
|
||||
0x92, 0x66, 0x64, 0x99, 0xd1, 0xa8, 0xa2, 0xab, 0x9c, 0x32, 0xe1, 0x99, 0x32, 0xb7, 0x73, 0xc8,
|
||||
0x55, 0x34, 0x76, 0x0f, 0x4a, 0xcd, 0xf8, 0x5d, 0x80, 0xdf, 0x87, 0x4f, 0xf5, 0xef, 0x7f, 0x19,
|
||||
0x60, 0x6b, 0x35, 0x7a, 0x00, 0x47, 0x57, 0x88, 0xc4, 0xb6, 0x50, 0xba, 0xf6, 0xe0, 0xea, 0xa8,
|
||||
0x4c, 0x38, 0xdf, 0x16, 0x14, 0x37, 0xb5, 0x72, 0x07, 0xd0, 0x35, 0xd8, 0x9b, 0x8c, 0xb0, 0x28,
|
||||
0x4d, 0xe4, 0xf0, 0xe7, 0xd8, 0xda, 0xc1, 0x51, 0x12, 0xf4, 0xc0, 0x94, 0x02, 0x17, 0x9c, 0xf9,
|
||||
0xdb, 0xec, 0x25, 0x1a, 0x4d, 0x17, 0xc3, 0xf1, 0xe8, 0xd9, 0x3d, 0x43, 0x2d, 0x68, 0x48, 0x66,
|
||||
0x31, 0x1e, 0x4e, 0x5d, 0x23, 0xf8, 0x00, 0x5b, 0x3b, 0x7f, 0xd2, 0xe5, 0xdb, 0x23, 0xf3, 0xd4,
|
||||
0xb6, 0x5a, 0xfb, 0x6d, 0x85, 0x7f, 0x7c, 0xf3, 0x7d, 0x30, 0xff, 0x9b, 0x79, 0x69, 0xc9, 0x8f,
|
||||
0x72, 0xf7, 0x13, 0x00, 0x00, 0xff, 0xff, 0x58, 0xe3, 0x1a, 0xb1, 0x38, 0x02, 0x00, 0x00,
|
||||
}
|
|
@ -0,0 +1,459 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: control.proto
|
||||
|
||||
package connector
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type SwitchPort_Speed int32
|
||||
|
||||
const (
|
||||
SwitchPort_SPEED_INVALID SwitchPort_Speed = 0
|
||||
SwitchPort_SPEED_100M SwitchPort_Speed = 1
|
||||
SwitchPort_SPEED_1G SwitchPort_Speed = 2
|
||||
SwitchPort_SPEED_10G SwitchPort_Speed = 3
|
||||
)
|
||||
|
||||
var SwitchPort_Speed_name = map[int32]string{
|
||||
0: "SPEED_INVALID",
|
||||
1: "SPEED_100M",
|
||||
2: "SPEED_1G",
|
||||
3: "SPEED_10G",
|
||||
}
|
||||
|
||||
var SwitchPort_Speed_value = map[string]int32{
|
||||
"SPEED_INVALID": 0,
|
||||
"SPEED_100M": 1,
|
||||
"SPEED_1G": 2,
|
||||
"SPEED_10G": 3,
|
||||
}
|
||||
|
||||
func (x SwitchPort_Speed) String() string {
|
||||
return proto.EnumName(SwitchPort_Speed_name, int32(x))
|
||||
}
|
||||
|
||||
func (SwitchPort_Speed) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0c5120591600887d, []int{1, 0}
|
||||
}
|
||||
|
||||
type SwitchPort_LinkState int32
|
||||
|
||||
const (
|
||||
SwitchPort_LINKSTATE_INVALID SwitchPort_LinkState = 0
|
||||
SwitchPort_LINKSTATE_DOWN SwitchPort_LinkState = 1
|
||||
SwitchPort_LINKSTATE_UP SwitchPort_LinkState = 2
|
||||
)
|
||||
|
||||
var SwitchPort_LinkState_name = map[int32]string{
|
||||
0: "LINKSTATE_INVALID",
|
||||
1: "LINKSTATE_DOWN",
|
||||
2: "LINKSTATE_UP",
|
||||
}
|
||||
|
||||
var SwitchPort_LinkState_value = map[string]int32{
|
||||
"LINKSTATE_INVALID": 0,
|
||||
"LINKSTATE_DOWN": 1,
|
||||
"LINKSTATE_UP": 2,
|
||||
}
|
||||
|
||||
func (x SwitchPort_LinkState) String() string {
|
||||
return proto.EnumName(SwitchPort_LinkState_name, int32(x))
|
||||
}
|
||||
|
||||
func (SwitchPort_LinkState) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0c5120591600887d, []int{1, 1}
|
||||
}
|
||||
|
||||
type SwitchPort_PortMode int32
|
||||
|
||||
const (
|
||||
SwitchPort_PORTMODE_INVALID SwitchPort_PortMode = 0
|
||||
// Interface is bridged to a VLAN, untagged.
|
||||
SwitchPort_PORTMODE_SWITCHPORT_UNTAGGED SwitchPort_PortMode = 1
|
||||
// Interfaces is bridged to several tagged 802.1q VLANs.
|
||||
SwitchPort_PORTMODE_SWITCHPORT_TAGGED SwitchPort_PortMode = 2
|
||||
// Interface is in 'generic', both tagged 802.1q and untagged mode.
|
||||
SwitchPort_PORTMODE_SWITCHPORT_GENERIC SwitchPort_PortMode = 3
|
||||
// Interface is routed, ie routes packets from a separate L3 network
|
||||
// and the Switch is the default gateway for machines in this network.
|
||||
SwitchPort_PORTMODE_ROUTED SwitchPort_PortMode = 4
|
||||
// Interface is in a configuration state that cannot be clearly stated
|
||||
// in terms of this enum, and should be reconfigured.
|
||||
SwitchPort_PORTMODE_MANGLED SwitchPort_PortMode = 5
|
||||
)
|
||||
|
||||
var SwitchPort_PortMode_name = map[int32]string{
|
||||
0: "PORTMODE_INVALID",
|
||||
1: "PORTMODE_SWITCHPORT_UNTAGGED",
|
||||
2: "PORTMODE_SWITCHPORT_TAGGED",
|
||||
3: "PORTMODE_SWITCHPORT_GENERIC",
|
||||
4: "PORTMODE_ROUTED",
|
||||
5: "PORTMODE_MANGLED",
|
||||
}
|
||||
|
||||
var SwitchPort_PortMode_value = map[string]int32{
|
||||
"PORTMODE_INVALID": 0,
|
||||
"PORTMODE_SWITCHPORT_UNTAGGED": 1,
|
||||
"PORTMODE_SWITCHPORT_TAGGED": 2,
|
||||
"PORTMODE_SWITCHPORT_GENERIC": 3,
|
||||
"PORTMODE_ROUTED": 4,
|
||||
"PORTMODE_MANGLED": 5,
|
||||
}
|
||||
|
||||
func (x SwitchPort_PortMode) String() string {
|
||||
return proto.EnumName(SwitchPort_PortMode_name, int32(x))
|
||||
}
|
||||
|
||||
func (SwitchPort_PortMode) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0c5120591600887d, []int{1, 2}
|
||||
}
|
||||
|
||||
type SwitchPort_SpanningTreeMode int32
|
||||
|
||||
const (
|
||||
SwitchPort_SPANNING_TREE_MODE_INVALID SwitchPort_SpanningTreeMode = 0
|
||||
// Send STP BPDU, on timeout switch to Forwarding.
|
||||
SwitchPort_SPANNING_TREE_MODE_AUTO_PORTFAST SwitchPort_SpanningTreeMode = 1
|
||||
// Switch to Forwarding immediately on link up.
|
||||
SwitchPort_SPANNING_TREE_MODE_PORTFAST SwitchPort_SpanningTreeMode = 2
|
||||
)
|
||||
|
||||
var SwitchPort_SpanningTreeMode_name = map[int32]string{
|
||||
0: "SPANNING_TREE_MODE_INVALID",
|
||||
1: "SPANNING_TREE_MODE_AUTO_PORTFAST",
|
||||
2: "SPANNING_TREE_MODE_PORTFAST",
|
||||
}
|
||||
|
||||
var SwitchPort_SpanningTreeMode_value = map[string]int32{
|
||||
"SPANNING_TREE_MODE_INVALID": 0,
|
||||
"SPANNING_TREE_MODE_AUTO_PORTFAST": 1,
|
||||
"SPANNING_TREE_MODE_PORTFAST": 2,
|
||||
}
|
||||
|
||||
func (x SwitchPort_SpanningTreeMode) String() string {
|
||||
return proto.EnumName(SwitchPort_SpanningTreeMode_name, int32(x))
|
||||
}
|
||||
|
||||
func (SwitchPort_SpanningTreeMode) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0c5120591600887d, []int{1, 3}
|
||||
}
|
||||
|
||||
type GetPortsRequest struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *GetPortsRequest) Reset() { *m = GetPortsRequest{} }
|
||||
func (m *GetPortsRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetPortsRequest) ProtoMessage() {}
|
||||
func (*GetPortsRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0c5120591600887d, []int{0}
|
||||
}
|
||||
|
||||
func (m *GetPortsRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_GetPortsRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *GetPortsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_GetPortsRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *GetPortsRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_GetPortsRequest.Merge(m, src)
|
||||
}
|
||||
func (m *GetPortsRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_GetPortsRequest.Size(m)
|
||||
}
|
||||
func (m *GetPortsRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_GetPortsRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_GetPortsRequest proto.InternalMessageInfo
|
||||
|
||||
type SwitchPort struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Speed SwitchPort_Speed `protobuf:"varint,2,opt,name=speed,proto3,enum=connector.SwitchPort_Speed" json:"speed,omitempty"`
|
||||
LinkState SwitchPort_LinkState `protobuf:"varint,3,opt,name=link_state,json=linkState,proto3,enum=connector.SwitchPort_LinkState" json:"link_state,omitempty"`
|
||||
PortMode SwitchPort_PortMode `protobuf:"varint,4,opt,name=port_mode,json=portMode,proto3,enum=connector.SwitchPort_PortMode" json:"port_mode,omitempty"`
|
||||
// For PORTMODE_SWITCHPORT_UNTAGGED and PORTMODE_SWITCHPORT_GENERIC, the
|
||||
// VLAN ID that this interface is natively bridged to.
|
||||
VlanNative int32 `protobuf:"varint,5,opt,name=vlan_native,json=vlanNative,proto3" json:"vlan_native,omitempty"`
|
||||
// For PORTMODE_SWITCHPORT_TAGGED and PORTMODE_SWITCHPORT_GENERIC, the VLAN
|
||||
// IDs that the interface is bridged to using 802.1q tags.
|
||||
VlanTagged []int32 `protobuf:"varint,6,rep,packed,name=vlan_tagged,json=vlanTagged,proto3" json:"vlan_tagged,omitempty"`
|
||||
// For PORTMODE_ROUTED
|
||||
Prefixes []string `protobuf:"bytes,7,rep,name=prefixes,proto3" json:"prefixes,omitempty"`
|
||||
// Interface MTU
|
||||
Mtu int32 `protobuf:"varint,8,opt,name=mtu,proto3" json:"mtu,omitempty"`
|
||||
SpanningTreeMode SwitchPort_SpanningTreeMode `protobuf:"varint,9,opt,name=spanning_tree_mode,json=spanningTreeMode,proto3,enum=connector.SwitchPort_SpanningTreeMode" json:"spanning_tree_mode,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SwitchPort) Reset() { *m = SwitchPort{} }
|
||||
func (m *SwitchPort) String() string { return proto.CompactTextString(m) }
|
||||
func (*SwitchPort) ProtoMessage() {}
|
||||
func (*SwitchPort) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0c5120591600887d, []int{1}
|
||||
}
|
||||
|
||||
func (m *SwitchPort) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SwitchPort.Unmarshal(m, b)
|
||||
}
|
||||
func (m *SwitchPort) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_SwitchPort.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *SwitchPort) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_SwitchPort.Merge(m, src)
|
||||
}
|
||||
func (m *SwitchPort) XXX_Size() int {
|
||||
return xxx_messageInfo_SwitchPort.Size(m)
|
||||
}
|
||||
func (m *SwitchPort) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_SwitchPort.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_SwitchPort proto.InternalMessageInfo
|
||||
|
||||
func (m *SwitchPort) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SwitchPort) GetSpeed() SwitchPort_Speed {
|
||||
if m != nil {
|
||||
return m.Speed
|
||||
}
|
||||
return SwitchPort_SPEED_INVALID
|
||||
}
|
||||
|
||||
func (m *SwitchPort) GetLinkState() SwitchPort_LinkState {
|
||||
if m != nil {
|
||||
return m.LinkState
|
||||
}
|
||||
return SwitchPort_LINKSTATE_INVALID
|
||||
}
|
||||
|
||||
func (m *SwitchPort) GetPortMode() SwitchPort_PortMode {
|
||||
if m != nil {
|
||||
return m.PortMode
|
||||
}
|
||||
return SwitchPort_PORTMODE_INVALID
|
||||
}
|
||||
|
||||
func (m *SwitchPort) GetVlanNative() int32 {
|
||||
if m != nil {
|
||||
return m.VlanNative
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *SwitchPort) GetVlanTagged() []int32 {
|
||||
if m != nil {
|
||||
return m.VlanTagged
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SwitchPort) GetPrefixes() []string {
|
||||
if m != nil {
|
||||
return m.Prefixes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SwitchPort) GetMtu() int32 {
|
||||
if m != nil {
|
||||
return m.Mtu
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *SwitchPort) GetSpanningTreeMode() SwitchPort_SpanningTreeMode {
|
||||
if m != nil {
|
||||
return m.SpanningTreeMode
|
||||
}
|
||||
return SwitchPort_SPANNING_TREE_MODE_INVALID
|
||||
}
|
||||
|
||||
type GetPortsResponse struct {
|
||||
Ports []*SwitchPort `protobuf:"bytes,1,rep,name=ports,proto3" json:"ports,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *GetPortsResponse) Reset() { *m = GetPortsResponse{} }
|
||||
func (m *GetPortsResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetPortsResponse) ProtoMessage() {}
|
||||
func (*GetPortsResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0c5120591600887d, []int{2}
|
||||
}
|
||||
|
||||
func (m *GetPortsResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_GetPortsResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *GetPortsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_GetPortsResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *GetPortsResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_GetPortsResponse.Merge(m, src)
|
||||
}
|
||||
func (m *GetPortsResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_GetPortsResponse.Size(m)
|
||||
}
|
||||
func (m *GetPortsResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_GetPortsResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_GetPortsResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *GetPortsResponse) GetPorts() []*SwitchPort {
|
||||
if m != nil {
|
||||
return m.Ports
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("connector.SwitchPort_Speed", SwitchPort_Speed_name, SwitchPort_Speed_value)
|
||||
proto.RegisterEnum("connector.SwitchPort_LinkState", SwitchPort_LinkState_name, SwitchPort_LinkState_value)
|
||||
proto.RegisterEnum("connector.SwitchPort_PortMode", SwitchPort_PortMode_name, SwitchPort_PortMode_value)
|
||||
proto.RegisterEnum("connector.SwitchPort_SpanningTreeMode", SwitchPort_SpanningTreeMode_name, SwitchPort_SpanningTreeMode_value)
|
||||
proto.RegisterType((*GetPortsRequest)(nil), "connector.GetPortsRequest")
|
||||
proto.RegisterType((*SwitchPort)(nil), "connector.SwitchPort")
|
||||
proto.RegisterType((*GetPortsResponse)(nil), "connector.GetPortsResponse")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("control.proto", fileDescriptor_0c5120591600887d) }
|
||||
|
||||
var fileDescriptor_0c5120591600887d = []byte{
|
||||
// 561 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x54, 0xd1, 0x6e, 0x9b, 0x4a,
|
||||
0x10, 0xbd, 0x98, 0x38, 0x17, 0x26, 0x71, 0xb2, 0x99, 0x36, 0x12, 0x72, 0xaa, 0x04, 0xa1, 0xaa,
|
||||
0xb2, 0x54, 0xc9, 0x4a, 0xd2, 0xc7, 0x4a, 0xad, 0x90, 0xa1, 0x04, 0xd5, 0xc6, 0xd6, 0xb2, 0x6e,
|
||||
0x1e, 0x11, 0xb5, 0xb7, 0x29, 0x8a, 0xb3, 0x50, 0xd8, 0xa4, 0xed, 0x4f, 0xf5, 0x6f, 0xfa, 0x3f,
|
||||
0x15, 0x10, 0xe3, 0xd4, 0xa2, 0x2f, 0x68, 0xe6, 0xcc, 0x39, 0xb3, 0x87, 0xd1, 0xce, 0x42, 0x6f,
|
||||
0x91, 0x0a, 0x99, 0xa7, 0xab, 0x61, 0x96, 0xa7, 0x32, 0x45, 0x7d, 0x91, 0x0a, 0xc1, 0x17, 0x32,
|
||||
0xcd, 0xad, 0x23, 0x38, 0xf4, 0xb8, 0x9c, 0xa5, 0xb9, 0x2c, 0x28, 0xff, 0x76, 0xcf, 0x0b, 0x69,
|
||||
0xfd, 0xde, 0x05, 0x08, 0xbf, 0x27, 0x72, 0xf1, 0xb5, 0x84, 0x11, 0x61, 0x47, 0xc4, 0x77, 0xdc,
|
||||
0x50, 0x4c, 0x65, 0xa0, 0xd3, 0x2a, 0xc6, 0x0b, 0xe8, 0x16, 0x19, 0xe7, 0x4b, 0xa3, 0x63, 0x2a,
|
||||
0x83, 0x83, 0xcb, 0x93, 0x61, 0xd3, 0x70, 0xb8, 0x51, 0x0e, 0xc3, 0x92, 0x42, 0x6b, 0x26, 0xbe,
|
||||
0x03, 0x58, 0x25, 0xe2, 0x36, 0x2a, 0x64, 0x2c, 0xb9, 0xa1, 0x56, 0xba, 0xb3, 0x76, 0xdd, 0x38,
|
||||
0x11, 0xb7, 0x61, 0x49, 0xa3, 0xfa, 0x6a, 0x1d, 0xe2, 0x5b, 0xd0, 0xb3, 0x34, 0x97, 0xd1, 0x5d,
|
||||
0xba, 0xe4, 0xc6, 0x4e, 0x25, 0x3f, 0x6d, 0x97, 0x97, 0x9f, 0x49, 0xba, 0xe4, 0x54, 0xcb, 0x1e,
|
||||
0x23, 0x3c, 0x83, 0xbd, 0x87, 0x55, 0x2c, 0x22, 0x11, 0xcb, 0xe4, 0x81, 0x1b, 0x5d, 0x53, 0x19,
|
||||
0x74, 0x29, 0x94, 0x50, 0x50, 0x21, 0x0d, 0x41, 0xc6, 0x37, 0x37, 0x7c, 0x69, 0xec, 0x9a, 0xea,
|
||||
0x9a, 0xc0, 0x2a, 0x04, 0xfb, 0xa0, 0x65, 0x39, 0xff, 0x92, 0xfc, 0xe0, 0x85, 0xf1, 0xbf, 0xa9,
|
||||
0x0e, 0x74, 0xda, 0xe4, 0x48, 0x40, 0xbd, 0x93, 0xf7, 0x86, 0x56, 0x75, 0x2d, 0x43, 0x64, 0x80,
|
||||
0x45, 0x16, 0x0b, 0x91, 0x88, 0x9b, 0x48, 0xe6, 0x9c, 0xd7, 0xae, 0xf5, 0xca, 0xf5, 0xab, 0x7f,
|
||||
0x0d, 0xab, 0xe6, 0xb3, 0x9c, 0xf3, 0xca, 0x3d, 0x29, 0xb6, 0x10, 0xcb, 0x83, 0x6e, 0x35, 0x52,
|
||||
0x3c, 0x82, 0x5e, 0x38, 0x73, 0x5d, 0x27, 0xf2, 0x83, 0x4f, 0xf6, 0xd8, 0x77, 0xc8, 0x7f, 0x78,
|
||||
0x00, 0x50, 0x43, 0x17, 0xe7, 0xe7, 0x13, 0xa2, 0xe0, 0x3e, 0x68, 0x8f, 0xb9, 0x47, 0x3a, 0xd8,
|
||||
0x03, 0x7d, 0x5d, 0xf5, 0x88, 0x6a, 0x5d, 0x81, 0xde, 0xcc, 0x18, 0x8f, 0xe1, 0x68, 0xec, 0x07,
|
||||
0x1f, 0x43, 0x66, 0x33, 0xf7, 0x49, 0x43, 0x84, 0x83, 0x0d, 0xec, 0x4c, 0xaf, 0x03, 0xa2, 0x20,
|
||||
0x81, 0xfd, 0x0d, 0x36, 0x9f, 0x91, 0x8e, 0xf5, 0x4b, 0x01, 0x6d, 0x3d, 0x6f, 0x7c, 0x0e, 0x64,
|
||||
0x36, 0xa5, 0x6c, 0x32, 0x75, 0x9e, 0x36, 0x32, 0xe1, 0x45, 0x83, 0x86, 0xd7, 0x3e, 0x1b, 0x5d,
|
||||
0x95, 0x69, 0x34, 0x0f, 0x98, 0xed, 0x79, 0xae, 0x43, 0x14, 0x3c, 0x85, 0x7e, 0x1b, 0xe3, 0xb1,
|
||||
0xde, 0xc1, 0x33, 0x38, 0x69, 0xab, 0x7b, 0x6e, 0xe0, 0x52, 0x7f, 0x44, 0x54, 0x7c, 0x06, 0x87,
|
||||
0x0d, 0x81, 0x4e, 0xe7, 0xcc, 0x75, 0xc8, 0xce, 0x5f, 0x6e, 0x26, 0x76, 0xe0, 0x8d, 0x5d, 0x87,
|
||||
0x74, 0xad, 0x9f, 0x40, 0xb6, 0x27, 0x5d, 0x9e, 0x1f, 0xce, 0xec, 0x20, 0xf0, 0x03, 0x2f, 0x62,
|
||||
0xd4, 0x75, 0xa3, 0xad, 0x3f, 0x78, 0x09, 0x66, 0x4b, 0xdd, 0x9e, 0xb3, 0x69, 0x54, 0x9e, 0xf0,
|
||||
0xc1, 0x0e, 0x19, 0x51, 0x4a, 0x97, 0x2d, 0xac, 0x86, 0xd0, 0xb1, 0xde, 0x03, 0xd9, 0xac, 0x5a,
|
||||
0x91, 0xa5, 0xa2, 0xe0, 0xf8, 0x1a, 0xba, 0xe5, 0x25, 0x2d, 0x0c, 0xc5, 0x54, 0x07, 0x7b, 0x97,
|
||||
0xc7, 0xad, 0x77, 0x83, 0xd6, 0x9c, 0x4b, 0x06, 0xbd, 0x1a, 0x1c, 0xd5, 0xdb, 0x8c, 0x23, 0xd0,
|
||||
0xd6, 0x1d, 0xb1, 0xff, 0x44, 0xba, 0xb5, 0xd1, 0xfd, 0x93, 0xd6, 0x5a, 0x6d, 0xe1, 0xf3, 0x6e,
|
||||
0xf5, 0x26, 0xbc, 0xf9, 0x13, 0x00, 0x00, 0xff, 0xff, 0xae, 0x71, 0xa0, 0x75, 0x24, 0x04, 0x00,
|
||||
0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// SwitchControlClient is the client API for SwitchControl service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type SwitchControlClient interface {
|
||||
GetPorts(ctx context.Context, in *GetPortsRequest, opts ...grpc.CallOption) (*GetPortsResponse, error)
|
||||
}
|
||||
|
||||
type switchControlClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewSwitchControlClient(cc *grpc.ClientConn) SwitchControlClient {
|
||||
return &switchControlClient{cc}
|
||||
}
|
||||
|
||||
func (c *switchControlClient) GetPorts(ctx context.Context, in *GetPortsRequest, opts ...grpc.CallOption) (*GetPortsResponse, error) {
|
||||
out := new(GetPortsResponse)
|
||||
err := c.cc.Invoke(ctx, "/connector.SwitchControl/GetPorts", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SwitchControlServer is the server API for SwitchControl service.
|
||||
type SwitchControlServer interface {
|
||||
GetPorts(context.Context, *GetPortsRequest) (*GetPortsResponse, error)
|
||||
}
|
||||
|
||||
func RegisterSwitchControlServer(s *grpc.Server, srv SwitchControlServer) {
|
||||
s.RegisterService(&_SwitchControl_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _SwitchControl_GetPorts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetPortsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SwitchControlServer).GetPorts(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/connector.SwitchControl/GetPorts",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SwitchControlServer).GetPorts(ctx, req.(*GetPortsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _SwitchControl_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "connector.SwitchControl",
|
||||
HandlerType: (*SwitchControlServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetPorts",
|
||||
Handler: _SwitchControl_GetPorts_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "control.proto",
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
//go:generate protoc -I.. ../control.proto --go_out=plugins=grpc:control
|
||||
//go:generate protoc -I.. ../config.proto --go_out=plugins=grpc:config
|
||||
|
||||
package proto
|
|
@ -0,0 +1,224 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"code.hackerspace.pl/q3k/mirko"
|
||||
"github.com/gobuffalo/packr"
|
||||
"github.com/q3k/statusz"
|
||||
"vbom.ml/util/sortorder"
|
||||
|
||||
"code.hackerspace.pl/q3k/topo/graph"
|
||||
"code.hackerspace.pl/q3k/topo/state"
|
||||
|
||||
pb "code.hackerspace.pl/q3k/topo/proto/control"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
gr *graph.Graph
|
||||
stm *state.StateManager
|
||||
}
|
||||
|
||||
func NewService(gr *graph.Graph, stm *state.StateManager) *Service {
|
||||
return &Service{
|
||||
gr: gr,
|
||||
stm: stm,
|
||||
}
|
||||
}
|
||||
|
||||
const topologyFragment = `
|
||||
<script src="/assets/viz.js"></script>
|
||||
<script>
|
||||
|
||||
var viz = new Viz({ workerURL: '/assets/full.render.js' });
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
var dot = this.responseText;
|
||||
viz.renderSVGElement(dot)
|
||||
.then(function(element) {
|
||||
document.getElementById("graph").appendChild(element);
|
||||
});
|
||||
}
|
||||
};
|
||||
xmlhttp.open('GET', '/debug/graphviz');
|
||||
xmlhttp.send();
|
||||
|
||||
</script>
|
||||
<div id="graph" style="text-align: center;"></div>
|
||||
`
|
||||
|
||||
const switchportsFragment = `
|
||||
<style type="text/css">
|
||||
.table td,th {
|
||||
background-color: #eee;
|
||||
padding: 0.2em 0.4em 0.2em 0.4em;
|
||||
}
|
||||
.table th {
|
||||
background-color: #c0c0c0;
|
||||
}
|
||||
.table {
|
||||
background-color: #fff;
|
||||
border-spacing: 0.2em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Switch</th>
|
||||
<th>Port</th>
|
||||
<th>Link State</th>
|
||||
<th>Port Mode</th>
|
||||
<th>MTU</th>
|
||||
<th>Sync Status</th>
|
||||
</tr>
|
||||
{{range .Ports }}
|
||||
{{ if .Managed }}
|
||||
<tr>
|
||||
{{ else }}
|
||||
<tr style="opacity: 0.5">
|
||||
{{ end}}
|
||||
<td style="text-align: right;">{{ .Switch }}</td>
|
||||
<td>{{ .Name }}</td>
|
||||
{{ if eq .State "DOWN" }}
|
||||
<td style="background-color: #ff3030;">{{ .State }}</td>
|
||||
{{ else }}
|
||||
<td>{{ .State }}</td>
|
||||
{{ end }}
|
||||
<td>{{ .Mode }}</td>
|
||||
<td>{{ .MTU }}</td>
|
||||
{{ if .Managed }}
|
||||
<td style="background-color: #30ff30;">OK</td>
|
||||
{{ else }}
|
||||
<td><i>Unmanaged</i></td>
|
||||
{{ end }}
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
</div>
|
||||
`
|
||||
|
||||
func (s *Service) Setup(m *mirko.Mirko) {
|
||||
assets := packr.NewBox("./assets")
|
||||
m.HTTPMux().Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(assets)))
|
||||
m.HTTPMux().HandleFunc("/debug/graphviz", s.httpHandleGraphviz)
|
||||
statusz.AddStatusPart("Switch Ports", switchportsFragment, s.statusHandleSwitchports)
|
||||
statusz.AddStatusPart("Topology", topologyFragment, func(ctx context.Context) interface{} {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) statusHandleSwitchports(ctx context.Context) interface{} {
|
||||
managedPorts := make(map[string]bool)
|
||||
s.gr.Mu.RLock()
|
||||
for _, sw := range s.gr.Switches {
|
||||
for _, port := range sw.Ports {
|
||||
managedPorts[sw.Name+"|"+port.Name] = true
|
||||
}
|
||||
}
|
||||
s.gr.Mu.RUnlock()
|
||||
|
||||
s.stm.Mu.RLock()
|
||||
defer s.stm.Mu.RUnlock()
|
||||
|
||||
res := struct {
|
||||
Ports []*struct {
|
||||
Switch string
|
||||
Name string
|
||||
State string
|
||||
Mode string
|
||||
Managed bool
|
||||
MTU string
|
||||
}
|
||||
}{}
|
||||
for _, sw := range s.stm.Switches {
|
||||
for _, po := range sw.Ports {
|
||||
state := "INVALID"
|
||||
switch po.Proto.LinkState {
|
||||
case pb.SwitchPort_LINKSTATE_DOWN:
|
||||
state = "DOWN"
|
||||
case pb.SwitchPort_LINKSTATE_UP:
|
||||
state = "UP"
|
||||
}
|
||||
mode := "INVALID"
|
||||
switch po.Proto.PortMode {
|
||||
case pb.SwitchPort_PORTMODE_SWITCHPORT_UNTAGGED:
|
||||
mode = fmt.Sprintf("UNTAGGED (%d)", po.Proto.VlanNative)
|
||||
case pb.SwitchPort_PORTMODE_SWITCHPORT_TAGGED:
|
||||
mode = fmt.Sprintf("TAGGED (%v)", po.Proto.VlanTagged)
|
||||
case pb.SwitchPort_PORTMODE_SWITCHPORT_GENERIC:
|
||||
mode = "GENERIC"
|
||||
case pb.SwitchPort_PORTMODE_ROUTED:
|
||||
mode = "ROUTED"
|
||||
case pb.SwitchPort_PORTMODE_MANGLED:
|
||||
mode = "MANGLED"
|
||||
}
|
||||
|
||||
managed := managedPorts[sw.Name+"|"+po.Proto.Name]
|
||||
res.Ports = append(res.Ports, &struct {
|
||||
Switch string
|
||||
Name string
|
||||
State string
|
||||
Mode string
|
||||
Managed bool
|
||||
MTU string
|
||||
}{
|
||||
Switch: sw.Name,
|
||||
Name: po.Proto.Name,
|
||||
State: state,
|
||||
Mode: mode,
|
||||
Managed: managed,
|
||||
MTU: fmt.Sprintf("%d", po.Proto.Mtu),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *Service) httpHandleGraphviz(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "graph G {\n")
|
||||
fmt.Fprintf(w, " ranksep = 2\n")
|
||||
fmt.Fprintf(w, " splines = polyline\n")
|
||||
fmt.Fprintf(w, " rankdir = LR\n")
|
||||
for _, machine := range s.gr.Machines {
|
||||
portNames := []string{}
|
||||
for _, port := range machine.Ports {
|
||||
name := fmt.Sprintf("<%s> %s", port.Name, port.Name)
|
||||
portNames = append(portNames, name)
|
||||
}
|
||||
ports := strings.Join(portNames, "|")
|
||||
fmt.Fprintf(w, " %s [shape=record label=\"{ %s | { %s }}\"]\n", machine.Name, machine.Name, ports)
|
||||
}
|
||||
for _, sw := range s.gr.Switches {
|
||||
portNames := []string{}
|
||||
portsOrdered := []*graph.SwitchPort{}
|
||||
for _, port := range sw.Ports {
|
||||
portsOrdered = append(portsOrdered, port)
|
||||
}
|
||||
sort.Slice(portsOrdered, func(i, j int) bool {
|
||||
return sortorder.NaturalLess(portsOrdered[i].Name, portsOrdered[j].Name)
|
||||
})
|
||||
for _, port := range portsOrdered {
|
||||
name := fmt.Sprintf("<%s> %s", port.Name, port.Name)
|
||||
portNames = append(portNames, name)
|
||||
}
|
||||
ports := strings.Join(portNames, "|")
|
||||
fmt.Fprintf(w, " %s [shape=record label=\"{{ %s } | %s}\"]\n", sw.Name, ports, sw.Name)
|
||||
}
|
||||
for _, machine := range s.gr.Machines {
|
||||
for _, port := range machine.Ports {
|
||||
if port.OtherEnd == nil {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, " %s:%q:e -- %s:%q:w\n", machine.Name, port.Name, port.OtherEnd.Switch.Name, port.OtherEnd.Name)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
cpb "code.hackerspace.pl/q3k/topo/proto/config"
|
||||
pb "code.hackerspace.pl/q3k/topo/proto/control"
|
||||
|
||||
"code.hackerspace.pl/q3k/hspki"
|
||||
)
|
||||
|
||||
type SwitchportState struct {
|
||||
Proto *pb.SwitchPort
|
||||
}
|
||||
|
||||
type SwitchState struct {
|
||||
Name string
|
||||
Ports []*SwitchportState
|
||||
Stub pb.SwitchControlClient
|
||||
}
|
||||
|
||||
func (s *SwitchState) Fetch(ctx context.Context) error {
|
||||
req := pb.GetPortsRequest{}
|
||||
res, err := s.Stub.GetPorts(ctx, &req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetPorts: %v", err)
|
||||
}
|
||||
s.Ports = make([]*SwitchportState, len(res.Ports))
|
||||
for i, port := range res.Ports {
|
||||
s.Ports[i] = &SwitchportState{port}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type StateManager struct {
|
||||
Switches map[string]*SwitchState
|
||||
Conns map[string]*grpc.ClientConn
|
||||
Mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewManager() *StateManager {
|
||||
return &StateManager{
|
||||
Switches: make(map[string]*SwitchState),
|
||||
Conns: make(map[string]*grpc.ClientConn),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StateManager) FetchState(ctx context.Context, conf *cpb.Config) error {
|
||||
s.Mu.Lock()
|
||||
defer s.Mu.Unlock()
|
||||
for _, sw := range conf.Switch {
|
||||
conn, ok := s.Conns[sw.ControlAddress]
|
||||
if !ok {
|
||||
var err error
|
||||
conn, err = grpc.Dial(sw.ControlAddress, hspki.WithClientHSPKI())
|
||||
if err != nil {
|
||||
return fmt.Errorf("when connecting to switch %s: %v", sw.Name, err)
|
||||
}
|
||||
s.Conns[sw.ControlAddress] = conn
|
||||
}
|
||||
|
||||
s.Switches[sw.Name] = &SwitchState{
|
||||
Name: sw.Name,
|
||||
Stub: pb.NewSwitchControlClient(conn),
|
||||
}
|
||||
err := s.Switches[sw.Name].Fetch(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%q.Fetch: %v", sw.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue