This commit is contained in:
Jeffrey C. Ollie 2024-05-14 15:23:31 -05:00
parent 866a655ca1
commit 2e68db33aa
Signed by: jeff
GPG key ID: 6F86035A6D97044E
8 changed files with 325 additions and 333 deletions

View file

@ -54,15 +54,15 @@ pub fn build(b: *std.Build) void {
exe.root_module.addImport("anzi", anzi.module("anzi")); exe.root_module.addImport("anzi", anzi.module("anzi"));
const netboxz = b.dependency( // const netboxz = b.dependency(
"netboxz", // "netboxz",
.{ // .{
.target = target, // .target = target,
.optimize = optimize, // .optimize = optimize,
}, // },
); // );
exe.root_module.addImport("netboxz", netboxz.module("netboxz")); // exe.root_module.addImport("netboxz", netboxz.module("netboxz"));
const logz = b.dependency( const logz = b.dependency(
"logz", "logz",

View file

@ -4,21 +4,23 @@
.minimum_zig_version = "0.12.0", .minimum_zig_version = "0.12.0",
.dependencies = .{ .dependencies = .{
.logz = .{ .logz = .{
.url = "https://github.com/karlseguin/log.zig/archive/eb86766b91cf2ca0a6e87dd8568fab80695cf2e8.tar.gz", .url = "https://github.com/karlseguin/log.zig/archive/2f17be4b6bbd32884df562a8d0501c15ec3bb928.tar.gz",
.hash = "1220b6cf56de826aead943a6db2a62ed40d81a150c24dcb019b3eb88673c82a6e7b7", // .hash = "1220b6cf56de826aead943a6db2a62ed40d81a150c24dcb019b3eb88673c82a6e7b7",
.hash = "1220347c287af8b265e36cffaf1c29e22da887e8fdcf38310e7b7d3c65ce64acf4f0",
}, },
.anzi = .{ .anzi = .{
.url = "https://git.ocjtech.us/jeff/anzi/archive/0e9d395a55b16da1d5bf7bfb59e2cfa59a7f2630.tar.gz", .url = "https://git.ocjtech.us/jeff/anzi/archive/0e9d395a55b16da1d5bf7bfb59e2cfa59a7f2630.tar.gz",
.hash = "12203f0ed986047bcd860b00496979a7734c2f462da4e56a72add4b17a1a7981f8ec", .hash = "12203f0ed986047bcd860b00496979a7734c2f462da4e56a72add4b17a1a7981f8ec",
}, },
.netboxz = .{ // .netboxz = .{
// .url = "https://github.com/jcollie/netboxz/archive/354caea69a1e033cf34da6c5e514dc4eb755af06.tar.gz", // // .url = "https://github.com/jcollie/netboxz/archive/354caea69a1e033cf34da6c5e514dc4eb755af06.tar.gz",
// .hash = "1220374f4587ec4d1e820a45039cd4bedd7cd560a3a668fdae2db0b5a6609f97c793", // // .hash = "1220374f4587ec4d1e820a45039cd4bedd7cd560a3a668fdae2db0b5a6609f97c793",
.path = "../netboxz", // .path = "../netboxz",
}, // },
.@"zig-cli" = .{ .@"zig-cli" = .{
.url = "https://github.com/sam701/zig-cli/archive/50201f0086da689cd104c2fef350f3dbe894eea7.tar.gz", .url = "https://github.com/sam701/zig-cli/archive/9a94c4803a52e54c26b198096d63fb5bde752da2.tar.gz",
.hash = "12208a4377c7699927605e5a797bad5ae2ba8be4b49f68b9f522b3a755597a21f1cf", // .hash = "12208a4377c7699927605e5a797bad5ae2ba8be4b49f68b9f522b3a755597a21f1cf",
.hash = "1220ab73fb7cc11b2308edc3364988e05efcddbcac31b707f55e6216d1b9c0da13f1",
}, },
}, },
.paths = .{ .paths = .{

View file

@ -9,7 +9,7 @@ pub const Config = struct {
name: []const u8, name: []const u8,
type: ConnectionType, type: ConnectionType,
comment: []const u8, comment: []const u8,
addresses: [][]const u8, address: []const u8,
port: u16, port: u16,
username: []const u8, username: []const u8,
manufacturer: []const u8, manufacturer: []const u8,
@ -31,13 +31,14 @@ pub const Config = struct {
.name = try alloc.dupe(u8, self.name), .name = try alloc.dupe(u8, self.name),
.type = self.type, .type = self.type,
.comment = try alloc.dupe(u8, self.comment), .comment = try alloc.dupe(u8, self.comment),
.addresses = addr: { .address = try alloc.dupe(u8, self.address),
var addrs = try alloc.alloc([]const u8, self.addresses.len); // .addresses = addr: {
for (self.addresses, 0..) |a, i| { // var addrs = try alloc.alloc([]const u8, self.addresses.len);
addrs[i] = try alloc.dupe(u8, a); // for (self.addresses, 0..) |a, i| {
} // addrs[i] = try alloc.dupe(u8, a);
break :addr addrs; // }
}, // break :addr addrs;
// },
.port = self.port, .port = self.port,
.username = try alloc.dupe(u8, self.username), .username = try alloc.dupe(u8, self.username),
.manufacturer = try alloc.dupe(u8, self.manufacturer), .manufacturer = try alloc.dupe(u8, self.manufacturer),

View file

@ -147,6 +147,6 @@ pub fn connect() !void {
); );
try bw.flush(); try bw.flush();
const envp = @as([*:null]const ?[*:0]const u8, @ptrCast(std.os.environ.ptr)); const envp = @as([*:null]const ?[*:0]const u8, @ptrCast(std.os.environ.ptr));
return std.os.execveZ(pathZ, argsZ, envp); return std.posix.execveZ(pathZ, argsZ, envp);
} }
} }

View file

@ -2,8 +2,9 @@ const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const build_options = @import("build_options"); const build_options = @import("build_options");
const cli = @import("zig-cli"); const cli = @import("zig-cli");
const connect = @import("connect.zig");
const options = @import("options.zig"); const options = @import("options.zig");
const connect = @import("connect.zig");
const update = @import("update.zig");
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@ -11,5 +12,73 @@ pub fn main() !void {
options.options.alloc = alloc; options.options.alloc = alloc;
return cli.run(options.app, alloc); var r = try cli.AppRunner.init(alloc);
const app = cli.App{
.command = cli.Command{
.name = "hostapps",
.description = cli.Description{
.one_line = "hostapps",
},
.target = cli.CommandTarget{
.subcommands = &.{
cli.Command{
.name = "connect",
.description = cli.Description{
.one_line = "connect",
},
.options = &.{
cli.Option{
.long_name = "config",
.help = "config file",
.value_ref = r.mkRef(&options.options.config),
.required = true,
},
cli.Option{
.long_name = "identity",
.help = "ssh identity",
.value_ref = r.mkRef(&options.options.identities),
},
cli.Option{
.long_name = "proxy-jump",
.help = "proxy jump",
.value_ref = r.mkRef(&options.options.proxy_jump),
},
cli.Option{
.long_name = "ssh-command",
.help = "ssh command",
.value_ref = r.mkRef(&options.options.ssh_path),
},
cli.Option{
.long_name = "telnet-command",
.help = "telnet command",
.value_ref = r.mkRef(&options.options.telnet_path),
},
},
.target = cli.CommandTarget{
.action = cli.CommandAction{
.exec = connect.connect,
},
},
},
cli.Command{
.name = "update",
.description = cli.Description{
.one_line = "update",
},
.options = &.{},
.target = cli.CommandTarget{
.action = cli.CommandAction{
.exec = update.update,
},
},
},
},
},
},
.version = "0.1.0",
.author = "Jeffrey C. Ollie <jcollie@dmacc.edu>",
};
return r.run(&app);
} }

View file

@ -17,83 +17,3 @@ pub var options: Options = .{
.ssh_path = build_options.ssh_path, .ssh_path = build_options.ssh_path,
.telnet_path = build_options.telnet_path, .telnet_path = build_options.telnet_path,
}; };
var config_option = cli.Option{
.long_name = "config",
.help = "config file",
.value_ref = cli.mkRef(&options.config),
.required = true,
};
var identity_option = cli.Option{
.long_name = "identity",
.help = "ssh identity",
.value_ref = cli.mkRef(&options.identities),
};
var proxy_jump_option = cli.Option{
.long_name = "proxy-jump",
.help = "proxy jump",
.value_ref = cli.mkRef(&options.proxy_jump),
};
var ssh_command_option = cli.Option{
.long_name = "ssh-command",
.help = "ssh command",
.value_ref = cli.mkRef(&options.ssh_path),
};
var telnet_command_option = cli.Option{
.long_name = "telnet-command",
.help = "telnet command",
.value_ref = cli.mkRef(&options.telnet_path),
};
var connect_command = cli.Command{
.name = "connect",
.description = cli.Description{
.one_line = "connect",
},
.options = &.{
&config_option,
&identity_option,
&proxy_jump_option,
&ssh_command_option,
&telnet_command_option,
},
.target = cli.CommandTarget{
.action = cli.CommandAction{
.exec = connect.connect,
},
},
};
var update_command = cli.Command{
.name = "update",
.description = cli.Description{
.one_line = "update",
},
.options = &.{},
.target = cli.CommandTarget{
.action = cli.CommandAction{
.exec = update.update,
},
},
};
pub const app = &cli.App{
.command = cli.Command{
.name = "hostapps",
.description = cli.Description{
.one_line = "hostapps",
},
.target = cli.CommandTarget{
.subcommands = &.{
&connect_command,
&update_command,
},
},
},
.version = "0.1.0",
.author = "Jeffrey C. Ollie <jcollie@dmacc.edu>",
};

View file

@ -8,7 +8,7 @@ pub fn expandPath(alloc: std.mem.Allocator, cmd: []const u8) !?[]u8 {
const path = try std.unicode.utf16leToUtf8Alloc(alloc, win_path); const path = try std.unicode.utf16leToUtf8Alloc(alloc, win_path);
break :blk path; break :blk path;
}, },
else => std.os.getenvZ("PATH") orelse return null, else => std.posix.getenvZ("PATH") orelse return null,
}; };
defer if (builtin.os.tag == .windows) alloc.free(PATH); defer if (builtin.os.tag == .windows) alloc.free(PATH);

View file

@ -4,245 +4,245 @@ const Config = @import("config.zig").Config;
const ConnectionType = @import("config.zig").ConnectionType; const ConnectionType = @import("config.zig").ConnectionType;
const options = @import("options.zig"); const options = @import("options.zig");
const ansi = @import("anzi").ANSI(.{}); const ansi = @import("anzi").ANSI(.{});
const netboxz = @import("netboxz"); // const netboxz = @import("netboxz");
const HashMap = @import("hashmap"); const HashMap = @import("hashmap");
const log = std.log.scoped(.update); const log = std.log.scoped(.update);
pub fn update() !void { pub fn update() !void {
var arena = std.heap.ArenaAllocator.init(options.options.alloc); // var arena = std.heap.ArenaAllocator.init(options.options.alloc);
const alloc = arena.allocator(); // const alloc = arena.allocator();
defer arena.deinit(); // defer arena.deinit();
const nb = try netboxz.init(alloc, "https://netbox.dmacc.net", "814e66254d019e7ef37de53bacc8e88e411da855"); // const nb = try netboxz.init(alloc, "https://", "");
const mfg_cisco_id = id: { // const mfg_cisco_id = id: {
var one = try nb.dcim().manufacturers().one( // var one = try nb.dcim().manufacturers().one(
&[_]netboxz.FilterOperation{ // &[_]netboxz.FilterOperation{
.{ // .{
.string = .{ // .string = .{
.key = "name", // .key = "name",
.value = "Cisco", // .value = "Cisco",
.comparison = .eq, // .comparison = .eq,
}, // },
}, // },
}, // },
); // );
defer one.deinit(); // defer one.deinit();
log.info("one: {any}", .{one.ok.value.name}); // log.info("one: {any}", .{one.ok.value.name});
switch (one) { // switch (one) {
.ok => |r| { // .ok => |r| {
log.info("found manufacturer {d} {s}", .{ r.value.id, r.value.name }); // log.info("found manufacturer {d} {s}", .{ r.value.id, r.value.name });
break :id r.value.id; // break :id r.value.id;
}, // },
.notfound => { // .notfound => {
log.err("could not find Cisco", .{}); // log.err("could not find Cisco", .{});
return; // return;
}, // },
.toomany => |r| { // .toomany => |r| {
log.err("{} is too many", .{r.count}); // log.err("{} is too many", .{r.count});
return; // return;
}, // },
.err => |r| { // .err => |r| {
log.err("error from netbox: {s}", .{r.detail}); // log.err("error from netbox: {s}", .{r.detail});
return; // return;
}, // },
} // }
}; // };
log.info("found mfg id {}", .{mfg_cisco_id}); // log.info("found mfg id {}", .{mfg_cisco_id});
const network_role_id = id: { // const network_role_id = id: {
var one = try nb.dcim().device_roles().one( // var one = try nb.dcim().device_roles().one(
&[_]netboxz.FilterOperation{ // &[_]netboxz.FilterOperation{
.{ // .{
.string = .{ // .string = .{
.key = "name", // .key = "name",
.value = "Network", // .value = "Network",
.comparison = .eq, // .comparison = .eq,
}, // },
}, // },
}, // },
); // );
defer one.deinit(); // defer one.deinit();
switch (one) { // switch (one) {
.ok => |r| { // .ok => |r| {
break :id r.value.id; // break :id r.value.id;
}, // },
.notfound => { // .notfound => {
log.err("could not find Network", .{}); // log.err("could not find Network", .{});
return; // return;
}, // },
.toomany => |r| { // .toomany => |r| {
log.err("{} is too many", .{r.count}); // log.err("{} is too many", .{r.count});
return; // return;
}, // },
.err => |r| { // .err => |r| {
log.err("error from netbox: {s}", .{r.detail}); // log.err("error from netbox: {s}", .{r.detail});
return; // return;
}, // },
} // }
}; // };
log.info("found role id {}", .{network_role_id}); // log.info("found role id {}", .{network_role_id});
{ // {
var iter = try nb.dcim().devices().list( // var iter = try nb.dcim().devices().list(
.{ // .{
.filters = &[_]netboxz.FilterOperation{ // .filters = &[_]netboxz.FilterOperation{
.{ // .{
.fk = .{ // .fk = .{
.key = "manufacturer_id", // .key = "manufacturer_id",
.value = mfg_cisco_id, // .value = mfg_cisco_id,
}, // },
}, // },
.{ // .{
.fk = .{ // .fk = .{
.key = "role_id", // .key = "role_id",
.value = network_role_id, // .value = network_role_id,
}, // },
}, // },
.{ // .{
.choice = .{ // .choice = .{
.key = "status", // .key = "status",
.value = "active", // .value = "active",
}, // },
}, // },
.{ // .{
.boolean = .{ // .boolean = .{
.key = "has_primary_ip", // .key = "has_primary_ip",
.value = true, // .value = true,
}, // },
}, // },
}, // },
}, // },
); // );
var part_number_map = HashMap.init(alloc); // var part_number_map = HashMap.init(alloc);
while (try iter.next()) |device_result| { // while (try iter.next()) |device_result| {
defer device_result.deinit(); // defer device_result.deinit();
switch (device_result) { // switch (device_result) {
.ok => |devices| { // .ok => |devices| {
for (devices.items) |device| { // for (devices.items) |device| {
const name = name: { // const name = name: {
if (device.virtual_chassis) |vc| break :name vc.name; // if (device.virtual_chassis) |vc| break :name vc.name;
if (device.name) |name| break :name name; // if (device.name) |name| break :name name;
break :name device.display; // break :name device.display;
}; // };
log.info("found device {d} '{s}' '{s}'", .{ device.id, name, device.display }); // log.info("found device {d} '{s}' '{s}'", .{ device.id, name, device.display });
const part_number = pn: { // const part_number = pn: {
if (part_number_map.get(device.device_type.id)) |pn| break :pn pn; // if (part_number_map.get(device.device_type.id)) |pn| break :pn pn;
const pn_result = try device.device_type.full(nb); // const pn_result = try device.device_type.full(nb);
switch (pn_result) { // switch (pn_result) {
.ok => |r| { // .ok => |r| {
part_number_map.put(r.item.id, r.item.part_number); // part_number_map.put(r.item.id, r.item.part_number);
break :pn r.item.part_number; // break :pn r.item.part_number;
}, // },
.err => |r| { // .err => |r| {
log.err("error: {}", .{r.detail}); // log.err("error: {}", .{r.detail});
return; // return;
}, // },
} // }
}; // };
var service_iter = try nb.ipam().services().list( // var service_iter = try nb.ipam().services().list(
.{ // .{
.filters = &[_]netboxz.FilterOperation{ // .filters = &[_]netboxz.FilterOperation{
.{ // .{
.fk = .{ // .fk = .{
.key = "device_id", // .key = "device_id",
.value = device.id, // .value = device.id,
}, // },
}, // },
.{ // .{
.string = .{ // .string = .{
.key = "name", // .key = "name",
.value = "ssh", // .value = "ssh",
.comparison = .ie, // .comparison = .ie,
}, // },
}, // },
.{ // .{
.string = .{ // .string = .{
.key = "name", // .key = "name",
.value = "telnet", // .value = "telnet",
.comparison = .ie, // .comparison = .ie,
}, // },
}, // },
}, // },
}, // },
); // );
defer service_iter.deinit(); // defer service_iter.deinit();
while (try service_iter.next()) |service_result| { // while (try service_iter.next()) |service_result| {
defer service_result.deinit(); // defer service_result.deinit();
switch (service_result) { // switch (service_result) {
.ok => |services| { // .ok => |services| {
for (services.items) |service| { // for (services.items) |service| {
for (service.ports) |port| { // for (service.ports) |port| {
var addresses = std.ArrayList([]const u8); // var addresses = std.ArrayList([]const u8);
errdefer addresses.deinit(); // errdefer addresses.deinit();
if (device.primary_ip6) |ip| { // if (device.primary_ip6) |ip| {
log.info("found service {d} {s} {s} {s} {d}", .{ device.id, name, ip.ip(), service.name, port }); // log.info("found service {d} {s} {s} {s} {d}", .{ device.id, name, ip.ip(), service.name, port });
try addresses.append(ip.ip()); // try addresses.append(ip.ip());
} // }
if (device.primary_ip4) |ip| { // if (device.primary_ip4) |ip| {
log.info("found service {d} {s} {s} {s} {d}", .{ device.id, name, ip.ip(), service.name, port }); // log.info("found service {d} {s} {s} {s} {d}", .{ device.id, name, ip.ip(), service.name, port });
try addresses.append(ip.ip()); // try addresses.append(ip.ip());
} // }
const config: Config = .{ // const config: Config = .{
.name = name, // .name = name,
.type = t: { // .type = t: {
var output: [32]u8 = undefined; // var output: [32]u8 = undefined;
std.ascii.lowerString(&output, service.name); // std.ascii.lowerString(&output, service.name);
break :t try std.meta.stringToEnum(ConnectionType, std.ascii.lowerString(output, service.name)); // break :t try std.meta.stringToEnum(ConnectionType, std.ascii.lowerString(output, service.name));
}, // },
.comment = c: { // .comment = c: {
const c = try alloc.dupe(u8, name); // const c = try alloc.dupe(u8, name);
std.mem.replaceScalar(u8, c, '-', ' '); // std.mem.replaceScalar(u8, c, '-', ' ');
break :c c; // break :c c;
}, // },
.addresses = addresses.toOwnedSlice(), // .addresses = addresses.toOwnedSlice(),
.port = port, // .port = port,
.username = try alloc.dupe(u8, "jcollie"), // .username = try alloc.dupe(u8, "jcollie"),
.manufacturer = try alloc.dupe(u8, device.device_type.manufacturer.name), // .manufacturer = try alloc.dupe(u8, device.device_type.manufacturer.name),
.model = try alloc.dupe(u8, device.device_type.model), // .model = try alloc.dupe(u8, device.device_type.model),
.part_number = try alloc.dupe(u8, part_number), // .part_number = try alloc.dupe(u8, part_number),
.class = c: { // .class = c: {
const c = try alloc.dupe(u8, name); // const c = try alloc.dupe(u8, name);
for (c, 0..) |b, i| { // for (c, 0..) |b, i| {
switch (b) { // switch (b) {
'a'...'z' => {}, // 'a'...'z' => {},
'A'...'Z' => {}, // 'A'...'Z' => {},
'0'...'9' => {}, // '0'...'9' => {},
'_' => {}, // '_' => {},
else => c[i] = '_', // else => c[i] = '_',
} // }
} // }
break :c c; // break :c c;
}, // },
}; // };
{ // {
var buf = std.ArrayList(u8).init(alloc); // var buf = std.ArrayList(u8).init(alloc);
try std.json.stringify(config, .{}, buf.writer()); // try std.json.stringify(config, .{}, buf.writer());
} // }
} // }
} // }
}, // },
.err => |err| { // .err => |err| {
log.err("error from netbox: {s}", .{err.detail}); // log.err("error from netbox: {s}", .{err.detail});
}, // },
} // }
} // }
} // }
}, // },
.err => |err| { // .err => |err| {
log.err("error from netbox: {s}", .{err.detail}); // log.err("error from netbox: {s}", .{err.detail});
return; // return;
}, // },
} // }
} // }
} // }
} }