diff --git a/server/build.zig b/server/build.zig index 3f221e9..ed4e33d 100644 --- a/server/build.zig +++ b/server/build.zig @@ -1,4 +1,6 @@ const std = @import("std"); +const Build = std.Build; +const Step = Build.Step; pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); @@ -38,14 +40,35 @@ pub fn build(b: *std.Build) !void { const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); - const exe_unit_tests = b.addTest(.{ - .root_source_file = b.path("src/main.zig"), + const libvirt_test = b.addTest(.{ + .root_source_file = b.path("src/libvirt.zig"), .target = target, .optimize = optimize, + .link_libc = true, }); + const libvirt_connection_test = b.addTest(.{ + .root_source_file = b.path("src/libvirt-connection.zig"), + .target = target, + .optimize = optimize, + .link_libc = true, + }); + const libvirt_unit_tests = [_]*Step.Compile{ + libvirt_test, + libvirt_connection_test, + }; + for (libvirt_unit_tests) |tests| { + tests.root_module.addIncludePath(b.path("libvirt/include")); + tests.addLibraryPath(b.path("libvirt/lib")); + tests.addObjectFile(b.path("libvirt/lib/libvirt.so.0.10000.0")); + tests.addObjectFile(b.path("libvirt/lib/libvirt-admin.so.0.10000.0")); + tests.addObjectFile(b.path("libvirt/lib/libvirt-qemu.so.0.10000.0")); + } - const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + const run_libvirt_tests = [_]*Step.Run{ + b.addRunArtifact(libvirt_test), + b.addRunArtifact(libvirt_connection_test), + }; const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&run_exe_unit_tests.step); + for (run_libvirt_tests) |tests| test_step.dependOn(&tests.step); } diff --git a/server/src/libvirt-connection.zig b/server/src/libvirt-connection.zig index 7719b28..65b7880 100644 --- a/server/src/libvirt-connection.zig +++ b/server/src/libvirt-connection.zig @@ -25,7 +25,7 @@ pub fn deinit(self: *const Connection) void { _ = c.virConnectClose(self.ptr); } -pub fn getURI(self: *const Connection) ![]u8 { +pub fn getURI(self: *const Connection) Error![]u8 { const uri = c.virConnectGetURI(self.ptr); defer std.c.free(uri); const str = h.string(uri); @@ -35,24 +35,13 @@ pub fn freeURI(self: *const Connection, uri: []u8) void { self.allocator.free(uri); } -// pub fn numOfPools(self: *const Connection) !u32 { -// return numOf(c.virConnectPtr, self.ptr, c.virConnectNumOfStoragePools); -// } -// pub fn numOfDefinedPools(self: *const Connection) !u32 { -// return numOf(c.virConnectPtr, self.ptr, c.virConnectNumOfDefinedStoragePools); -// } +test "connection" { + const conn = try Connection.init("qemu:///system", std.testing.allocator); + defer conn.deinit(); -// pub fn iteratePools(self: *const Connection, flags: []const Pool.ListFlags) !Pool.PoolIterator { -// return Pool.PoolIterator.init( -// c.virConnect, -// Connection, -// Pool.ListFlags, -// self, -// flags, -// self.allocator, -// c.virConnectListAllStoragePools, -// ); -// } + const uri = try conn.getURI(); + defer conn.freeURI(uri); +} // pub fn createPool(self: *const Connection, xml: String, flags: []const Pool.CreateFlags) !Pool { // const pool = c.virStoragePoolCreateXML(self.ptr, @ptrCast(xml.str), intFromFlags(Pool.CreateFlags, flags)); @@ -63,6 +52,44 @@ pub fn freeURI(self: *const Connection, uri: []u8) void { // return if (pool) |p| Pool.init(p, self.allocator) else handleError(); // } +pub const Error = mem.Allocator.Error || VirError; + +pub fn pools(self: *const Connection) Error![]Pool { + const list = try Pool.listAllStoragePools(self, &[_]Pool.ListFlags{}); + var l = try self.allocator.alloc(Pool, list.len); + for (list, 0..) |p, i| l[i] = Pool.init(p, self.allocator); + return l; +} +pub fn freePools(self: *const Connection, slice: []Pool) void { + // for (slice) |pool| pool.deinit(); + self.allocator.free(slice); +} +pub fn iteratePools(self: *const Connection) Error!PoolIterator { + return .{ + .allocator = self.allocator, + .values = try self.pools(), + }; +} +pub const PoolIterator = struct { + allocator: mem.Allocator, + values: []Pool, + index: usize = 0, + + pub fn deinit(it: *PoolIterator) void { + for (it.values) |v| v.free() catch @panic("pool free error"); + it.allocator.free(it.values); + } + pub fn next(it: *PoolIterator) ?Pool { + if (it.index >= it.values.len) return null; + const v = it.values[it.index]; + it.index += 1; + return v; + } + pub fn reset(it: *PoolIterator) void { + it.index = 0; + } +}; + pub fn numOfDomains(self: *const Connection) VirError!u32 { return Domain.numOfDomains(self); } @@ -70,18 +97,6 @@ pub fn numOfDefinedDomains(self: *const Connection) VirError!u32 { return Domain.numOfDefinedDomains(self); } -// pub fn iterateDomains(self: *const Connection, flags: []const Domain.ListFlags) !Domain.DomainIterator { -// return Domain.DomainIterator.init( -// c.virConnect, -// Connection, -// Domain.ListFlags, -// self, -// flags, -// self.allocator, -// c.virConnectListAllDomains, -// ); -// } - // pub fn createDomain(self: *const Connection, xml: String, flags: []const Domain.CreateFlags) !Domain { // const domain = c.virDomainCreateXML(self.ptr, @ptrCast(xml.str), intFromFlags(Domain.CreateFlags, flags)); // return if (domain) |d| Domain.init(d, self.allocator) else handleError(); @@ -94,3 +109,133 @@ pub fn numOfDefinedDomains(self: *const Connection) VirError!u32 { // const domain = c.virDomainDefineXMLFlags(self.ptr, @ptrCast(xml.str), intFromFlags(Domain.DefineFlags, flags)); // return if (domain) |d| Domain.init(d, self.allocator) else handleError(); // } + +pub fn createDomain(self: *const Connection, xml: h.String, flags: []const Domain.CreateFlags) VirError!Domain { + const ptr = try Domain.createXML(self, xml, flags); + return Domain.init(ptr, self.allocator); +} + +pub fn domains(self: *const Connection) Error![]Domain { + const list = try Domain.listAllDomains(self, &[_]Domain.ListFlags{}); + var l = try self.allocator.alloc(Domain, list.len); + for (list, 0..) |p, i| l[i] = Domain.init(p, self.allocator); + return l; +} +pub fn freeDomains(self: *const Connection, slice: []Domain) void { + // for (slice) |domain| domain.deinit(); + self.allocator.free(slice); +} +pub fn iterateDomains(self: *const Connection) Error!DomainIterator { + return .{ + .allocator = self.allocator, + .values = try self.domains(), + }; +} +pub const DomainIterator = struct { + allocator: mem.Allocator, + values: []Domain, + index: usize = 0, + + pub fn deinit(it: *DomainIterator) void { + for (it.values) |v| v.free() catch @panic("domain free error"); + it.allocator.free(it.values); + } + pub fn next(it: *DomainIterator) ?Domain { + if (it.index >= it.values.len) return null; + const v = it.values[it.index]; + it.index += 1; + return v; + } + pub fn reset(it: *DomainIterator) void { + it.index = 0; + } +}; + +test "domains" { + const conn = try Connection.init("qemu:///system", std.testing.allocator); + defer conn.deinit(); + + // Enumerate domains + const num_active = try conn.numOfDomains(); + const num_inactive = try conn.numOfDefinedDomains(); + _ = .{ num_active, num_inactive }; + + // Iterate domains + var domain_it = try conn.iterateDomains(); + defer domain_it.deinit(); + while (domain_it.next()) |domain| { + const active = try domain.isActive(); + const name = try domain.getName(); + _ = .{ active, name }; + } + + // Create and destory domain + const xml = try h.String.init(std.testing.allocator, + \\ + \\ vm1 + \\ 48dd0909-6346-4028-b922-1b3a13571360 + \\ 1048576 + \\ 1048576 + \\ 1 + \\ + \\ hvm + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ destroy + \\ restart + \\ destroy + \\ + \\ + \\ + \\ + \\ + \\ /run/libvirt/nix-emulators/qemu-system-x86_64 + \\ + \\
+ \\ + \\ + \\ + \\
+ \\ + \\ + \\ + \\
+ \\ + \\ + \\ + \\
+ \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\