public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestMessage req, TraceWriter log) { var payload = await req.Content.ReadAsStringAsync(); var nodes = JsonConvert.DeserializeObject <Request>(payload); if (nodes.nodes == null || nodes.nodes.Length == 0) { return(req.CreateErrorResponse(HttpStatusCode.BadRequest, "Nodes payload not found.")); } List <string> deallocatedVMs = new List <string>(); foreach (var node in nodes.nodes) { log.Info($"Reported {node.nodeId} has {node.rooms} rooms"); //get the state of the corresponding VM var vm = await TableStorageHelper.Instance.GetVMByID(node.nodeId.Trim()); if (node.rooms == 0) { if (vm == null) { log.Error($"VM {vm.VMID} not found in DB"); continue; } if (vm.State == VMState.MarkedForDeallocation) { deallocatedVMs.Add(vm.VMID); //VM has zero rooms and marked for deallocation //it's fate is sealed, bye bye! :) await AzureAPIHelper.DeallocateVMAsync(vm.VMID, vm.ResourceGroup); //mark it with the deallocating state in table storage //VMMonitor Function will be called when it is finally deallocated vm.State = VMState.Deallocating; } } vm.RoomsNumber = node.rooms; await TableStorageHelper.Instance.ModifyVMDetailsAsync(vm); } var resp = req.CreateResponse(HttpStatusCode.OK); resp.Content = new StringContent(JsonConvert.SerializeObject(new { VMsInDeallocatingState = deallocatedVMs })); return(resp); }
public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", Route = "node/deallocate")] HttpRequestMessage req, TraceWriter log) { log.Info("MarkVMForDeallocation Function was called"); // Get POST body dynamic data = JsonConvert.DeserializeObject(await req.Content.ReadAsStringAsync()); // Set name to query string or body data string vmName = data?.vmName; //trim it just in case vmName = vmName.Trim(); var vm = await TableStorageHelper.Instance.GetVMByID(vmName); if (vm == null) { return(req.CreateErrorResponse(HttpStatusCode.BadRequest, $"VM {vmName} not found")); } //set the state of the requested VM as MarkedForDeallocation vm.State = VMState.MarkedForDeallocation; //however, if there are no games running on this VM, deallocate it immediately if (vm.RoomsNumber == 0) { log.Info($"VM with ID {vmName} has zero game rooms so it will be deallocated immediately"); await AzureAPIHelper.DeallocateVMAsync(vm.VMID, vm.ResourceGroup); vm.State = VMState.Deallocating; } //modify the VM state in the DB with the new value await TableStorageHelper.Instance.ModifyVMDetailsAsync(vm); return(vmName == null ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a vmName in the request body") : req.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(vm))); }
public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = "VMMonitor")] HttpRequestMessage req, TraceWriter log) { dynamic dataobject = await req.Content.ReadAsAsync <object>(); //log.Info(dataobject.ToString()); log.Info("----------------------------------------------"); //get Azure Monitor detailed activity log var activityLog = dataobject.data.context.activityLog; log.Info(activityLog.ToString()); //confirm that this is indeed a VM operation if (activityLog.operationName.ToString().StartsWith(VM_OPERATION)) { string resourceGroup = activityLog.resourceGroupName; //get the resource group //get the VM ID string resourceId = activityLog.resourceId.ToString(); // returns /subscriptions/6bd0e514-c783-4dac-92d2-6788744eee7a/resourceGroups/lala3/providers/Microsoft.Compute/virtualMachines/lala3 string VMID = resourceId.Substring(resourceId.LastIndexOf('/') + 1); var vm = await TableStorageHelper.Instance.GetVMByID(VMID); if (vm == null) { log.Info($"VM {VMID} was not found in our DB, skipping"); return(req.CreateResponse(HttpStatusCode.OK)); } if (activityLog.status == OPERATION_FAILED) { vm.State = VMState.Failed; log.Error($"VM {VMID} is failed: {activityLog.subStatus}"); await TableStorageHelper.Instance.ModifyVMDetailsAsync(vm); return(req.CreateResponse(HttpStatusCode.InternalServerError)); } if (activityLog.status != OPERATION_SUCCEEDED) { log.Error($"VM {VMID} is in a state we aren't interested in: <{activityLog.status}>"); return(req.CreateResponse(HttpStatusCode.OK)); } string operationName = (string)activityLog.operationName; string ip; switch (operationName) { case CREATE_VM_OPERATION: log.Info($"VM with name {VMID} created"); //when the VM is finally created we need to i)set its state as Running and ii)get its Public IP ip = await AzureAPIHelper.GetVMPublicIP(VMID, resourceGroup); vm.State = VMState.Running; break; case RESTART_VM_OPERATION: log.Info($"VM with name {VMID} rebooted"); vm.State = VMState.Running; break; case DEALLOCATE_VM_OPERATION: log.Info($"VM with name {VMID} deallocated"); //when the VM is deallocated its public IP is removed, too vm.State = VMState.Deallocated; break; case START_VM_OPERATION: log.Info($"VM with name {VMID} started - was deallocated before"); //when the VM is started from deallocation it gets a new public IP, so add it to the DB vm.IP = await AzureAPIHelper.GetVMPublicIP(VMID, resourceGroup); vm.State = VMState.Running; break; } await TableStorageHelper.Instance.ModifyVMDetailsAsync(vm); log.Info("----------------------------------------------"); return(req.CreateResponse(HttpStatusCode.OK, $"WebHook call for VM:'{VMID}' with operation:'{activityLog.operationName}' and status:'{activityLog.status}' was successful")); } else { string msg = "No VM operation, something went wrong"; log.Error(msg); return(req.CreateErrorResponse(HttpStatusCode.BadRequest, msg)); } }
public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = "node/create")] HttpRequestMessage req, TraceWriter log, ExecutionContext context) { var content = await req.Content.ReadAsStringAsync(); var nodeParams = JsonConvert.DeserializeObject <NodeParameters>(content); var err = ValidateRequest(req, nodeParams, log); if (err != null) { return(err); } //find out if there is any VM in MarkedForDeallocation state var vm = (await TableStorageHelper.Instance.GetMatchingVMsInStateAsync( nodeParams.ResourceGroup, nodeParams.Region, nodeParams.Size, VMState.MarkedForDeallocation)) .FirstOrDefault(); if (vm != null) { //set it as running so as not to be deallocated when game rooms are 0 vm.State = VMState.Running; await TableStorageHelper.Instance.ModifyVMDetailsAsync(vm); //we're done, this VM can now be used return(req.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(vm), "application/json")); } //search if there are any deallocated VMs var deallocatedVM = (await TableStorageHelper.Instance .GetMatchingVMsInStateAsync(nodeParams.ResourceGroup, nodeParams.Region, nodeParams.Size, VMState.Deallocated)) .FirstOrDefault(); //no deallocated VMs, so let's create a new one if (deallocatedVM == null) { string vmName = "node" + System.Guid.NewGuid().ToString("N").Substring(0, 7); string sshKey = ConfigurationManager.AppSettings["VM_ADMIN_KEY"]?.ToString() ?? System.IO.File.ReadAllText(context.FunctionAppDirectory + "/default_key_rsa.pub"); string deploymentTemplate = System.IO.File.ReadAllText(context.FunctionAppDirectory + "/vmDeploy.json"); await DeployNodeAsync(vmName, nodeParams, sshKey, deploymentTemplate, log); var details = new VMDetails(vmName, nodeParams.ResourceGroup, VMState.Creating, nodeParams.Size, nodeParams.Region); await TableStorageHelper.Instance.AddVMEntityAsync(details); return(req.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(details), "application/json")); } else//there's at least one deallocated VM, so start the .First() one { await AzureAPIHelper.StartDeallocatedVMAsync(deallocatedVM); deallocatedVM.State = VMState.Creating; await TableStorageHelper.Instance.ModifyVMDetailsAsync(deallocatedVM); return(req.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(deallocatedVM), "application/json")); } }