Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
40c3859
change job to run every minute
WarriorAchilles Dec 15, 2021
8b7c2c5
stubbed out code for TestLabVmConnection service and added VmStatusJo…
WarriorAchilles Dec 23, 2021
f5a5353
removed code that isn't part of card, removed unneeded try/catch
WarriorAchilles Dec 23, 2021
67d7053
Merge branch 'dev' into ze/labvm-status-job
WarriorAchilles Dec 24, 2021
4e99555
implemented attempts to restart vm if vm is down
WarriorAchilles Dec 27, 2021
695903e
changed implementation of TestLabVmConnection to use correct method c…
WarriorAchilles Dec 28, 2021
3479d33
removed unused variable
WarriorAchilles Dec 28, 2021
f36704c
renamed file to better reflect usage
WarriorAchilles Dec 28, 2021
1e3e2a5
removed WIP code unrelated to card
WarriorAchilles Dec 28, 2021
9e2a103
refactored code into recursive function
WarriorAchilles Dec 30, 2021
3c8f762
Merge branch 'dev' into ze/labvm-status-job
WarriorAchilles Dec 30, 2021
34b4be4
added database context to ProxmoxApi and fixed resulting errors
WarriorAchilles Jan 4, 2022
354f071
added code to save vm status to database
WarriorAchilles Jan 4, 2022
f49158b
added ProxmoxDBApi class to handle Api calls that require the database
WarriorAchilles Jan 28, 2022
47acc84
changed job to run every minute
WarriorAchilles Jan 28, 2022
77123c8
removed unused imports
WarriorAchilles Jan 28, 2022
5b7dd18
removed another unused import
WarriorAchilles Jan 28, 2022
952828b
started testing and fixed some errors. Print statements will need rem…
WarriorAchilles Feb 24, 2022
4928be0
fixed problems and removed debug print statements
WarriorAchilles Feb 24, 2022
3c8328d
added line to save changes to database in ProxmoxDBApi
WarriorAchilles Feb 24, 2022
ce76973
fixed a logic error
WarriorAchilles Feb 24, 2022
820d90e
changed time interval to what it should be and removed a couple lefto…
WarriorAchilles Feb 24, 2022
04050a9
requested changes to make job more testable later on
WarriorAchilles Mar 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CSLabs.Api/Jobs/JobRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ public class JobRegistry : Registry
{
public JobRegistry(IServiceProvider provider)
{
// Every minute check the status of each Lab VM within each Lab
// Every 3 minutes check to see if we have quorum (at least 50% of the ProxMox nodes are up)

// Schedule new jobs here
Schedule(() => new ExampleJob(provider)).ToRunEvery(1).Minutes();
Schedule(() => new VmStatusJob(provider)).ToRunEvery(1).Minutes();

}
}
Expand Down
28 changes: 28 additions & 0 deletions CSLabs.Api/Jobs/VmStatusJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Threading.Tasks;
using CSLabs.Api.Models;
using CSLabs.Api.Services;
using Microsoft.Extensions.DependencyInjection;

namespace CSLabs.Api.Jobs
{
public class VmStatusJob : AsyncJob
{
private readonly IServiceProvider _provider;
public VmStatusJob(IServiceProvider provider)
{
_provider = provider;
}

protected override async Task ExecuteAsync()
{
using var scope = _provider.CreateScope();
using var context = scope.ServiceProvider.GetService<DefaultContext>();

// Do job
var connectionService = new TestVmConnectionService(context);
await connectionService.TestLabVmConnection();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the following to the ServiceProvider.ProvideAppServices method:

 services.AddScoped<TestVmConnectionService>();

Then rewrite this section to handle the dependency injection:

Suggested change
using var context = scope.ServiceProvider.GetService<DefaultContext>();
// Do job
var connectionService = new TestVmConnectionService(context);
await connectionService.TestLabVmConnection();
var connectionService = scope.ServiceProvider.GetService<TestVmConnectionService>();
await connectionService.TestLabVmConnection();

Then anything in the constructor of the TestVmConnectionService will be automatically injected.

}
}
}
3 changes: 3 additions & 0 deletions CSLabs.Api/Models/UserModels/UserLabVm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,16 @@ public class UserLabVm : Trackable, IQemu
[InverseProperty(nameof(VmInterfaceInstance.UserLabVm))]
public List<VmInterfaceInstance> InterfaceInstances { get; set; } = new List<VmInterfaceInstance>();

public bool Running { get; set; } = true;


public static void OnModelCreating(ModelBuilder builder)
{
builder.TimeStamps<UserLabVm>();
builder.Entity<UserLabVm>().HasIndex(u => new {u.UserLabId, u.LabVmId}).IsUnique();
builder.Entity<UserLabVm>().HasIndex(u => new {u.UserLabId});
builder.Entity<UserLabVm>().HasIndex(u => new {u.LabVmId});
builder.Entity<UserLabVm>().HasIndex(u => new {u.Running});
}


Expand Down
36 changes: 36 additions & 0 deletions CSLabs.Api/Proxmox/ProxmoxDBApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Threading.Tasks;
using CSLabs.Api.Models;
using CSLabs.Api.Models.HypervisorModels;

namespace CSLabs.Api.Proxmox
{
public class ProxmoxDBApi : ProxmoxApi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You havent configured this ProxmoxDBApito be used anywhere but one place, it should be used instead of the Proxmox api since you want to track the start and stop states of the VM when initiated by the UI.

{
private DefaultContext _context;
public ProxmoxDBApi(HypervisorNode hypervisorNode, string password, DefaultContext context) : base(hypervisorNode, password)
{
_context = context;
}

public new async Task StartVM(int vmId, string targetNode = null)
{
await base.StartVM(vmId, targetNode);
// Save in the database that the VM is started
_context.UserLabVms.Find(vmId).Running = true;
}

public new async Task StopVM(int vmId)
{
await base.StopVM(vmId);
// Save in the database that the VM is stopped
_context.UserLabVms.Find(vmId).Running = false;
}

public new async Task ShutdownVm(int vmId, int timeout = 20)
{
await base.ShutdownVm(vmId, timeout);
// Save in the database that the VM is stopped
_context.UserLabVms.Find(vmId).Running = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vm id here is the proxmox vmid so the .Find(vmId) will fail.

}
}
}
6 changes: 6 additions & 0 deletions CSLabs.Api/Proxmox/ProxmoxManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ public ProxmoxApi GetProxmoxApi(HypervisorNode node)
return new ProxmoxApi(node, password);
}

public ProxmoxApi GetProxmoxDBApi(HypervisorNode node)
{
var password = Cryptography.DecryptString(node.Hypervisor.Password, _encryptionKey);
return new ProxmoxDBApi(node, password, _context);
}

public ProxmoxApi GetProxmoxApi(UserLab userLab)
{
return GetProxmoxApi(userLab.HypervisorNode);
Expand Down
99 changes: 99 additions & 0 deletions CSLabs.Api/Services/TestVmConnectionService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System.Linq;
using System.Threading.Tasks;
using CSLabs.Api.Models;
using CSLabs.Api.Proxmox;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace CSLabs.Api.Services
{
public class TestVmConnectionService
{
private DefaultContext Context { get; }

private ProxmoxManager ProxmoxManager;

public TestVmConnectionService(DefaultContext context)
{
Context = context;
}

// Recursive helper function
public async void AttemptStart(int attempt, int labId, ProxmoxApi api)
{
try
{
if (attempt != 3) // first or second attempt
{
await api.StartVM(labId);
}
else // third attempt
{
await api.StopVM(labId);
await api.StartVM(labId);
}

}
catch (ProxmoxRequestException)
{
if (attempt != 3)
{
AttemptStart(attempt + 1, labId, api);
}
else
{
// TODO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ses sender is merged, so you should be able to continue with this.

// third attempt at restarting has failed. Something really bad has happened
// and the maintainers need to be emailed
}
}
}

public async Task<IActionResult> TestLabVmConnection()
{
var hypervisors = await Context.Hypervisors
.Include(h => h.HypervisorNodes)
.ToListAsync();

var labVms = await Context.LabVms
.Include(v => v.Id)
.ToListAsync();

var userLabVms = await Context.UserLabVms
.Include(v => v.Id)
.ToListAsync();

foreach (var hypervisor in hypervisors)
{
var api = ProxmoxManager.GetProxmoxDBApi(hypervisor.HypervisorNodes.First());

foreach (var labVm in labVms)
{
try
{
//actual status of VM
var vmStatus = await api.GetVmStatus(labVm.Id);

//value stored in database
var userLab = userLabVms.Find(x => x.LabVmId.Equals(labVm.Id));
var userLabStatus = userLab.Running;

// check if actual status is opposite what is stored in the database
if (vmStatus.IsStopped() != userLabStatus)
{
AttemptStart(1, labVm.Id, api);
}
}
catch (ProxmoxRequestException)
{
// something went wrong getting the vm status
}

}
}

return new OkObjectResult("All LabVMs are up and responding");
}

}
}