Isard.ps1

# https://gitlab.com/isard/isardvdi/-/tree/main/api/src/api/schemas

# /api/v3/user
# /api/v3/user/config


function Connect-Isard {

  param(
    [Parameter(Position = 0, HelpMessage = "Nom de la màquina virtual")]
    [string] $Name
  )

  if (-Not(Get-Command "remote-viewer.exe" -ErrorAction SilentlyContinue)) {
    Install-Scoop virt-viewer -Bucket "extras"
  }

  $vm = Get-Isard -Name $Name -Raw
  if ($Null -eq $vm) {
    Write-IsardLog $Name
    Write-Host -ForegroundColor Red "No existeix cap màquina amb aquest nom"
    return
  }

  # TODO comprovar màquina arrencada

  $data = Invoke-IsardRequest -Endpoint "/api/v3/desktop/$($vm.id)/viewer/file-spice"
  $data = $data | ConvertFrom-Json


  $file = Get-IsardPath -Name "isard-spice.vv"
  $content = $data.content
  $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False

  [System.IO.File]::WriteAllLines($file, $content, $Utf8NoBomEncoding)

  remote-viewer.exe $file
}

function Get-Isard {
  
  param(
    [Parameter(Position = 0, HelpMessage = "Nom de la màquina virtual")]
    [string] $Name,
    [switch] $Raw
  )

  $vms = Invoke-IsardRequest -Endpoint "/api/v3/user/desktops"
  $vms = $vms | ConvertFrom-Json

 
  if ($Name -ne "") {
 
    $vms = $vms | Where-Object { $_.name -eq $Name } | Select-Object -First 1

    if (($Null -eq $vms) -And -Not($Raw)) {
      Write-IsardLog $Name
      Write-Host -ForegroundColor Red "No existeix cap màquina amb aquest nom"
      return
    }
  }

  if ($Raw) {
    return $vms
  }
    
  $vms | Sort-Object name | Format-Table -AutoSize -Property @{
    Label      = "Nom"
    Expression = { $_.name }
  }, @{
    Label      = "Estat"
    Expression = { $_.state }
  },
  @{
    Label      = "IP"
    Expression = { $_.ip }
  }
}

function New-Isard {

  Invoke-IsardRequest -Endpoint "/test"

}


function Start-Isard {

  param(
    [Parameter(Position = 0, Mandatory = $true, HelpMessage = "Nom de la màquina virtual")]
    [string] $Name
  )

  #https://gitlab.com/isard/isardvdi/-/blob/main/api/src/api/views/DesktopsPersistentView.py

  $vm = Get-Isard -Name $Name -Raw
  if ($Null -eq $vm) {
    Write-IsardLog $Name
    Write-Host -ForegroundColor Red "No existeix cap màquina amb aquest nom"
    return
  }

  if ($vm.state -eq "Started") {
    Write-IsardLog $Name
    Write-Host -ForegroundColor Yellow "La màquina ja està engegada"
    return
  }
  
  if ($vm.state -eq "WaitingIP") {
    Write-IsardLog $Name
    Write-Host -ForegroundColor Yellow "La màquina està esperant la IP"
    return
  }

  $endpoint = "/api/v3/desktop/start/$($vm.id)"

  #Write-Host "STATE $($vm.state)"

  Write-IsardLog $Name
  Write-Host "Engegant la màquina ... " -NoNewline
  $data = Invoke-IsardRequest -Endpoint $endpoint
  # TODO check ok {"id": "ba4d3752-7dd5-49e2-8fd9-b237eaeea4f5"}
  Write-Host -ForegroundColor Green "Fet"
}

function Stop-Isard {

  param(
    [Parameter(Position = 0, Mandatory = $true, HelpMessage = "Nom de la màquina virtual")]
    [string] $Name
  )

  #https://gitlab.com/isard/isardvdi/-/blob/main/api/src/api/views/DesktopsPersistentView.py

  $vm = Get-Isard -Name $Name -Raw
  if ($Null -eq $vm) {
    Write-IsardLog $Name
    Write-Host -ForegroundColor Red "No existeix cap màquina amb aquest nom"
    return
  }

  if ($vm.state -eq "Stopped") {
    Write-IsardLog $Name
    Write-Host -ForegroundColor Yellow "La màquina ja està aturada"
    return
  }

  if ($vm.state -eq "Shutting-down") {
    Write-IsardLog $Name
    Write-Host -ForegroundColor Yellow "La màquina s'està aturant"
    return
  }

  #Write-Host "STATE $($vm.state)"

  $endpoint = "/api/v3/desktop/stop/$($vm.id)"
  
  Write-IsardLog $Name
  Write-Host "Aturant la màquina ... " -NoNewline
  $data = Invoke-IsardRequest -Endpoint $endpoint
  # TODO check ok {"id": "ba4d3752-7dd5-49e2-8fd9-b237eaeea4f5"}
  Write-Host -ForegroundColor Green "Fet"
}

##### Private


function Get-IsardPath {
  param(
    [string] $Name
  )

  $path = Get-BoxPath -Path "isard"
  if ($Name) {
    $path = Join-Path $path $Name
  }
  return $path
}

# https://gitlab.com/isard/isardvdi/-/blob/main/api/src/api/views/AuthenticationView.py
function Get-IsardToken {
  param (
    [switch] $Update
  )

  $tokenFile = Get-IsardPath -Name token
  
  if ($Update) {
    Remove-Item -Path $tokenFile
  }

  if (Test-Path $tokenFile) {
    return (Get-Content $tokenFile)
  }

  Write-Host -ForegroundColor Green "Inciar Sessió"
  $username = Read-Host "Nom d'usuari"
  $password = Read-Host "Contrasenya" -AsSecureString
  $password = (New-Object PSCredential 0, $password).GetNetworkCredential().Password

  $endpoint = "/authentication/login"


  $response = Invoke-IsardRequest2 -Endpoint $endpoint -Form @{
    provider    = "p:form";
    category_id = "p:db79b78a-5408-4ff9-9853-a1172b7eadb2"; # Provençana
    username    = $username;
    password    = $password 
  }

  $token = $response.Content
  $token | Out-File $tokenFile

  return $token
}

function Invoke-IsardRequest {
  param(
    [string] $Endpoint,
    [hashtable] $Form,
    [string] $OutFile
  )
  try {

    $token = Get-IsardToken
    try {
      $response = Invoke-IsardRequest2 -Endpoint $Endpoint -Form $Form -Token $token -OutFile $OutFile
      return $response.Content
    }
    catch {
      $status = [int]$_.Exception.Response.StatusCode
      if ($status -ne 401) {
        throw $_.Exception
      }

      $token = Get-IsardToken -Update
      $response = Invoke-IsardRequest2 -Endpoint $Endpoint -Form $Form -Token $token -OutFile $OutFile
      return $response
    }
  }
  catch {

    $response = $_.Exception.Response
    #StatusDescription

    Write-IsardLog "HTTP"
    Write-Host -ForegroundColor Yellow "($([int]$response.StatusCode) $($response.StatusCode))" -NoNewline
    Write-Host ": " -NoNewline
    Write-Host $_.ErrorDetails.Message

    throw $_.Exception
  }

}

function Invoke-IsardRequest2 {
  param(
    [string] $Endpoint,
    [hashtable] $Form,
    [string] $Token
  )


  $uri = "https://pilotfp.gencat.isardvdi.com${Endpoint}"
  $headers = @()
  $method = "Get"
  $contentType = $Null
  $body = $Null

  if ($Null -ne $Token) {
    $headers = @{Authorization = "Bearer $token" }
  }

  if ($Null -ne $Form) {

    $boundary = [System.Guid]::NewGuid().ToString(); 
    $LF = "`r`n"

    $method = "Post"
    $contentType = "multipart/form-data; boundary=`"$boundary`""
  
    $query = $Null
    $body = @()
    foreach ($item in $Form.GetEnumerator()) {
      $body += "--$boundary"
      $body += "Content-Disposition: form-data; name=`"$($item.Name)`"$LF"
    
      $value = $item.Value
      if (-Not($value.StartsWith("p:"))) {
        $body += $value
      }
      else {
        $value = $value.Substring(2)
        $body += $value
        if ($Null -eq $query) {
          $query += "?"
        }
        else {
          $query += "&"
        }
        $query += "$($item.Name)=$value"
      }
    }
    $body += "--$boundary--$LF" 
    $body = $body -join $LF

    if ($Null -ne $query) {
      $uri += $query
    }
  }

  $response = Invoke-WebRequest -Method $method -Uri $uri -Header $headers -ContentType $contentType -Body $body

  # session https://superuser.com/questions/1275923/using-invoke-webrequest-in-powershell-with-cookies

  $setCookie = $response.Headers['Set-Cookie']

  return $response
}

function Write-IsardLog {
  param(
    [string] $Name
  )

  Write-Host -ForegroundColor Blue -NoNewline $Name 
  Write-Host ": " -NoNewline
}