From 8d31a7a69c1379699a77d8f02e141daf522daf0c Mon Sep 17 00:00:00 2001
From: Glenn <glenux@glenux.net>
Date: Sat, 9 Jul 2022 20:03:54 +0200
Subject: [PATCH] Add missing libraries

---
 src/lib/actions.cr                            | 26 ++++++
 src/lib/actions/apply.cr                      | 91 +++++++++++++++++++
 src/lib/actions/none.cr                       | 17 ++++
 src/lib/actions/plan.cr                       | 21 +++++
 src/lib/models.cr                             | 11 +++
 src/lib/models/config.cr                      | 11 +++
 src/lib/models/os_vol_list_item.cr            | 74 +++++++++++++++
 .../models/os_vol_list_item_attach_item.cr    | 20 ++++
 src/lib/models/os_vol_snap_create_item.cr     | 42 +++++++++
 src/lib/models/os_vol_snap_list_item.cr       | 51 +++++++++++
 src/lib/models/retention.cr                   | 10 ++
 src/lib/models/target.cr                      | 11 +++
 src/lib/openstack.cr                          | 77 ++++++++++++++++
 src/lib/types.cr                              | 16 ++++
 src/lib/version.cr                            |  7 ++
 15 files changed, 485 insertions(+)
 create mode 100644 src/lib/actions.cr
 create mode 100644 src/lib/actions/apply.cr
 create mode 100644 src/lib/actions/none.cr
 create mode 100644 src/lib/actions/plan.cr
 create mode 100644 src/lib/models.cr
 create mode 100644 src/lib/models/config.cr
 create mode 100644 src/lib/models/os_vol_list_item.cr
 create mode 100644 src/lib/models/os_vol_list_item_attach_item.cr
 create mode 100644 src/lib/models/os_vol_snap_create_item.cr
 create mode 100644 src/lib/models/os_vol_snap_list_item.cr
 create mode 100644 src/lib/models/retention.cr
 create mode 100644 src/lib/models/target.cr
 create mode 100644 src/lib/openstack.cr
 create mode 100644 src/lib/types.cr
 create mode 100644 src/lib/version.cr

diff --git a/src/lib/actions.cr b/src/lib/actions.cr
new file mode 100644
index 0000000..b51d8c9
--- /dev/null
+++ b/src/lib/actions.cr
@@ -0,0 +1,26 @@
+
+require "tablo"
+
+module Arkisto
+  class Action
+    def initialize(_config : ConfigModel, _options : ActionOptions)
+      # Do nothing by default
+      return
+    end
+
+    def perform
+      # Do nothing by default
+      return
+    end
+  end
+
+  class ActionFactory
+    def self.build(action_type : Action.class, config, options)
+      action = action_type.new(config, options)
+    end
+  end
+end
+
+require "./actions/*"
+
+
diff --git a/src/lib/actions/apply.cr b/src/lib/actions/apply.cr
new file mode 100644
index 0000000..be319b9
--- /dev/null
+++ b/src/lib/actions/apply.cr
@@ -0,0 +1,91 @@
+
+require "../openstack"
+require "../actions"
+
+module Arkisto
+  class ApplyAction < Action
+    def initialize(config : ConfigModel, options : ActionOptions)
+      puts "action = Apply"
+      @config = config
+    end
+
+    # First create today's backup
+    # Then remove old backups
+    def perform
+      _create_backup_all
+      _delete_backup_all
+      return
+    end
+
+    def _create_backup_all
+      puts "_create_backup_all".colorize(:yellow)
+      os_volumes = OpenStack.volume_list
+
+      @config.targets.each do |config_volume_item|
+        os_volume_item = 
+          os_volumes.select {|vol| vol.id == config_volume_item.volume_id }.first
+
+        if os_volume_item.nil?
+          puts "  ERROR: no volume found with this id".colorize(:red)
+          exit(1)
+        end
+
+        _create_backup(config_volume_item, os_volume_item)
+      end
+    end
+
+    def _create_backup(config_item : TargetItemModel, volume_item : OSVolume::ListItemModel)
+      puts "_create_backup".colorize(:yellow)
+      puts "+ openstack volume snapshot #{config_item.name.colorize(:yellow)} {"
+      puts "+   volume_id #{config_item.volume_id}"
+      puts "+   name #{volume_item.name}"
+      puts "+   size #{volume_item.size}"
+      puts "+ }"
+
+      created_volume = OpenStack.volume_snapshot_create(volume_item, config_item.name)
+    end
+
+    def _delete_backup_all
+      puts "_delete_backup_all".colorize(:yellow)
+      os_volumes = OpenStack.volume_list
+      os_all_snapshots = OpenStack.volume_snapshot_list
+
+      @config.targets.each do |config_volume_item|
+        os_volume_item = os_volumes.select {|vol| 
+          vol.id == config_volume_item.volume_id 
+        }.try &.first
+
+        os_volume_snapshots = os_all_snapshots.select {|snap| 
+          snap.volume == config_volume_item.volume_id
+        }
+
+        os_volume_snapshots.each do |snap_item|
+          _delete_backup(config_volume_item, snap_item)
+          pp snap_item
+        end
+
+        # _delete_backup(config_volume_item, os_volume_snapshot_item)
+        # volume_snapshots.each do |target_snapshot|
+        # os_volume_item = 
+        # os_volumes.select {|vol| vol.id == config_volume_item.volume_id }.first
+      end
+    end
+
+    def _delete_backup(config_item : TargetItemModel, snap_item : OSVolume::SnapshotListItemModel)
+      puts "_delete_backup".colorize(:yellow)
+
+      puts "- openstack volume snapshot #{config_item.name.colorize(:yellow)} {"
+      puts "-   volume_id #{config_item.volume_id}"
+      puts "-   created_at #{snap_item.created_at}"
+      puts "-   snap_id #{snap_item.id}"
+      puts "-   name #{snap_item.name}"
+      puts "-   size #{snap_item.size}"
+      puts "- }"
+
+      pp config_item
+      pp snap_item
+
+      # FIXME: not implemented
+    end
+  end
+end
diff --git a/src/lib/actions/none.cr b/src/lib/actions/none.cr
new file mode 100644
index 0000000..8276f07
--- /dev/null
+++ b/src/lib/actions/none.cr
@@ -0,0 +1,17 @@
+
+require "../actions"
+
+module Arkisto
+  class NoneAction < Action
+    def initialize(config : ConfigModel, options : ActionOptions)
+      puts "action = None"
+      # @global_options = global_options
+      # @config = config
+    end
+
+    def perform
+      # Do nothing by default
+      return
+    end
+  end
+end
diff --git a/src/lib/actions/plan.cr b/src/lib/actions/plan.cr
new file mode 100644
index 0000000..d6354e4
--- /dev/null
+++ b/src/lib/actions/plan.cr
@@ -0,0 +1,21 @@
+
+require "../actions"
+
+module Arkisto
+  class PlanAction < Action
+    def initialize(config : ConfigModel, options : ActionOptions)
+      @config = config
+      puts "action = Plan"
+    end
+
+    def perform
+      @config.targets.each do |target|
+        puts "#{target.name}"
+        puts "- volume_id #{target.volume_id}"
+        # puts "- snapshot_prefix #{target.snapshot_prefix}"
+        puts ""
+      end
+      return
+    end
+  end
+end
diff --git a/src/lib/models.cr b/src/lib/models.cr
new file mode 100644
index 0000000..64f9f36
--- /dev/null
+++ b/src/lib/models.cr
@@ -0,0 +1,11 @@
+
+require "yaml"
+
+module Arkisto
+  class Model
+    include YAML::Serializable
+  end
+end
+
+require "./types"
+require "./models/*"
diff --git a/src/lib/models/config.cr b/src/lib/models/config.cr
new file mode 100644
index 0000000..7c570dd
--- /dev/null
+++ b/src/lib/models/config.cr
@@ -0,0 +1,11 @@
+
+require "../models"
+
+module Arkisto
+    class ConfigModel < Model
+      property version  : String
+      property retention : RetentionModel
+      property targets : Array(TargetItemModel)
+  end
+end
+
diff --git a/src/lib/models/os_vol_list_item.cr b/src/lib/models/os_vol_list_item.cr
new file mode 100644
index 0000000..858c0a9
--- /dev/null
+++ b/src/lib/models/os_vol_list_item.cr
@@ -0,0 +1,74 @@
+
+
+# - Attached to: []
+#   ID: 8c87c196-1dd6-4190-bff5-00b543965093
+#   Name: ovh-managed-kubernetes-e6j07m-pvc-9fad19b4-b32c-452a-9893-524fcdc3a4b1
+#   Size: 50
+#   Status: available
+# - Attached to:
+#   - attached_at: '2022-06-27T17:38:53.000000'
+#     attachment_id: 88a7dfc4-5bdf-4864-b38d-c82169c37b9d
+#     device: /dev/sdc
+#     host_name: null
+#     id: c9b11fcb-a6a4-4387-b55b-34121f97dac4
+#     server_id: 37220c33-19e7-45cc-9c6a-ddf957be3c76
+#     volume_id: c9b11fcb-a6a4-4387-b55b-34121f97dac4
+#   ID: c9b11fcb-a6a4-4387-b55b-34121f97dac4
+#   Name: ovh-managed-kubernetes-e6j07m-pvc-13dcac68-71c9-4794-b0ab-d48e76d843ef
+#   Size: 5800
+#   Status: in-use
+
+## LONG
+
+# - Attached to:
+#   - attached_at: '2022-06-27T17:31:53.000000'
+#     attachment_id: 845a7ff2-ad54-4040-b69b-fce7fdfdece2
+#     device: /dev/sdb
+#     host_name: null
+#     id: 6d4492e5-6b38-4ad3-b9bf-27ba86e2506b
+#     server_id: 37220c33-19e7-45cc-9c6a-ddf957be3c76
+#     volume_id: 6d4492e5-6b38-4ad3-b9bf-27ba86e2506b
+#   Bootable: 'false'
+#   ID: 6d4492e5-6b38-4ad3-b9bf-27ba86e2506b
+#   Name: ovh-managed-kubernetes-e6j07m-pvc-7bf995bf-ac82-4f42-94ca-78cd9c78959d
+#   Properties:
+#     attached_mode: rw
+#     cinder.csi.openstack.org/cluster: kubernetes
+#     readonly: 'False'
+#   Size: 1
+#   Status: in-use
+#   Type: high-speed
+
+
+require "../models"
+
+module Arkisto
+  module OSVolume
+    class ListItemModel < Model
+      @[YAML::Field(key: "Attached to")]
+      property attached_to : Array(OSVolume::ListItemAttachment::ListItemModel)
+
+      @[YAML::Field(key: "Bootable")]
+      property bootable : String
+
+      @[YAML::Field(key: "ID")]
+      property id : String
+
+      @[YAML::Field(key: "Name")]
+      property name : String
+
+      @[YAML::Field(key: "Properties")]
+      property properties : YAML::Any
+
+      @[YAML::Field(key: "Size")]
+      property size : UInt16
+
+      @[YAML::Field(key: "Status")]
+      property status : String
+
+      @[YAML::Field(key: "Type")]
+      property type : String
+    end
+  end
+end
+
diff --git a/src/lib/models/os_vol_list_item_attach_item.cr b/src/lib/models/os_vol_list_item_attach_item.cr
new file mode 100644
index 0000000..e869fd6
--- /dev/null
+++ b/src/lib/models/os_vol_list_item_attach_item.cr
@@ -0,0 +1,20 @@
+
+require "../models"
+
+module Arkisto
+  module OSVolume
+    class ListItemAttachment
+      class ListItemModel < Model
+        property attached_at : String
+        property attachment_id : String
+        property device : String
+        property host_name : String
+        property id : String
+        property server_id : String
+        property volume_id : String
+      end
+    end
+  end
+end
+
+
diff --git a/src/lib/models/os_vol_snap_create_item.cr b/src/lib/models/os_vol_snap_create_item.cr
new file mode 100644
index 0000000..fa551e4
--- /dev/null
+++ b/src/lib/models/os_vol_snap_create_item.cr
@@ -0,0 +1,42 @@
+
+# created_at: '2022-06-28T11:26:06.266927'
+# description: null
+# id: 52fa0022-3ed8-43bc-90d9-08d22597fb35
+# name: pouet
+# properties: {}
+# size: 50
+# status: creating
+# updated_at: null
+# volume_id: de7b118f-413b-4962-a028-a742df4bc4fc
+
+require "../models"
+
+module Arkisto
+  module OSVolume
+    class SnapshotCreateItemModel < Model
+      @[YAML::Field(key: "created_at")]
+      property created_at : String
+
+      @[YAML::Field(key: "description")]
+      property description : String
+
+      @[YAML::Field(key: "id")]
+      property id : String  ## UUID
+
+      @[YAML::Field(key: "name")]
+      property name : String
+
+      @[YAML::Field(key: "properties")]
+      property properties : YAML::Any
+
+      @[YAML::Field(key: "size")]
+      property size : UInt16
+
+      @[YAML::Field(key: "status")]
+      property status : String
+
+      @[YAML::Field(key: "volume_id")]
+      property volume_id : String
+    end
+  end
+end
diff --git a/src/lib/models/os_vol_snap_list_item.cr b/src/lib/models/os_vol_snap_list_item.cr
new file mode 100644
index 0000000..1ff6f05
--- /dev/null
+++ b/src/lib/models/os_vol_snap_list_item.cr
@@ -0,0 +1,51 @@
+
+## volume snapshot list -f yaml
+
+# - Description: null
+#   ID: 86367f4d-fbc2-4e09-b0e7-1b4b73f72baa
+#   Name: db-snapshot01
+#   Size: 50
+#   Status: available
+
+## volume snapshot list --long -f yaml
+
+# - Created At: '2022-06-27T14:09:47.000000'
+#   Description: null
+#   ID: 86367f4d-fbc2-4e09-b0e7-1b4b73f72baa
+#   Name: db-snapshot01
+#   Properties: {}
+#   Size: 50
+#   Status: available
+#   Volume: 8c87c196-1dd6-4190-bff5-00b543965093
+
+require "../models"
+
+module Arkisto
+  module OSVolume
+    class SnapshotListItemModel < Model
+      @[YAML::Field(key: "Created At")]
+      property created_at : String
+
+      @[YAML::Field(key: "Description")]
+      property description : String
+
+      @[YAML::Field(key: "ID")]
+      property id : String  ## UUID
+
+      @[YAML::Field(key: "Name")]
+      property name : String
+
+      @[YAML::Field(key: "Properties")]
+      property properties : YAML::Any
+
+      @[YAML::Field(key: "Size")]
+      property size : UInt16
+
+      @[YAML::Field(key: "Status")]
+      property status : String
+
+      @[YAML::Field(key: "Volume")]
+      property volume : String
+    end
+  end
+end
diff --git a/src/lib/models/retention.cr b/src/lib/models/retention.cr
new file mode 100644
index 0000000..3f4d066
--- /dev/null
+++ b/src/lib/models/retention.cr
@@ -0,0 +1,10 @@
+
+require "../models"
+
+module Arkisto
+  class RetentionModel < Model
+    property days : UInt8
+    property cycle : Bool
+    property max_items : UInt8
+  end
+end
diff --git a/src/lib/models/target.cr b/src/lib/models/target.cr
new file mode 100644
index 0000000..7dcc7f3
--- /dev/null
+++ b/src/lib/models/target.cr
@@ -0,0 +1,11 @@
+
+require "../models"
+
+module Arkisto
+  class TargetItemModel < Model
+    property name : String
+    property volume_id : String
+    # property snapshot_prefix : String
+  end
+end
+
diff --git a/src/lib/openstack.cr b/src/lib/openstack.cr
new file mode 100644
index 0000000..91351c8
--- /dev/null
+++ b/src/lib/openstack.cr
@@ -0,0 +1,77 @@
+
+module Arkisto
+  class OpenStack
+    #
+    # openstack volume list
+    #
+    def self.volume_list
+      cmd = "openstack"
+      args = [
+        "volume", "list",
+        "--long",
+        "--format", "yaml"
+      ]
+      stdout = IO::Memory.new
+      stderr = IO::Memory.new
+
+      status = Process.run(cmd, args: args, output: stdout, error: stderr)
+      if ! status.success?
+        raise RuntimeError.new("command failed for: #{cmd} #{args.join(" ")}")  # FIXME: improve message
+      end
+
+      return Array(OSVolume::ListItemModel).from_yaml(stdout.to_s)
+    rescue ex : RuntimeError
+      puts "ERROR: #{ex.message}".colorize(:red)
+      exit(1)
+    end
+
+    #
+    # openstack volume snapshot list
+    #
+    def self.volume_snapshot_list
+      cmd = "openstack"
+      args =[
+        "volume", "snapshot", "list",
+        "--long",
+        "--format", "yaml"
+      ]
+      stdout = IO::Memory.new
+      stderr = IO::Memory.new
+
+      status = Process.run(cmd, args: args, output: stdout, error: stderr)
+      if ! status.success?
+        raise RuntimeError.new("command failed for: #{cmd} #{args.join(" ")}")  # FIXME: improve message
+      end
+
+      return Array(OSVolume::SnapshotListItemModel).from_yaml(stdout.to_s)
+    rescue ex : RuntimeError
+      puts "ERROR: #{ex.message}".colorize(:red)
+      exit(1)
+    end
+
+    #
+    # openstack volume snapshot create --volume VOLUME_ID  --force SNAPSHOT_NAME -f yaml 
+    #
+    def self.volume_snapshot_create(target_volume, snap_name)
+      cmd = "openstack"
+      args = ["volume", "snapshot", "create",
+              "--volume", target_volume.id,
+              "--force",  
+              snap_name,
+              "--format", "yaml"]
+      stdout = IO::Memory.new
+      stderr = IO::Memory.new
+
+      status = Process.run(cmd, args: args, output: stdout, error: stderr)
+      if ! status.success?
+        raise RuntimeError.new("#{cmd} #{args.join(" ")}\n#{stderr.to_s}")  # FIXME: improve message
+      end
+
+      return OSVolume::SnapshotCreateItemModel.from_yaml(stdout.to_s)
+
+    rescue ex : RuntimeError
+      puts "ERROR: #{ex.message}".colorize(:red)
+      exit(1)
+    end
+  end
+end
diff --git a/src/lib/types.cr b/src/lib/types.cr
new file mode 100644
index 0000000..532bb9b
--- /dev/null
+++ b/src/lib/types.cr
@@ -0,0 +1,16 @@
+
+require "./actions"
+
+module Arkisto
+  alias GlobalOptions = {
+    action: Action.class,
+    verbose: Bool,
+    dry_run: Bool,
+    config_file: String
+  }
+
+  alias ActionOptions = {
+    verbose: Bool,
+    dry_run: Bool
+  }
+end
diff --git a/src/lib/version.cr b/src/lib/version.cr
new file mode 100644
index 0000000..faedf5f
--- /dev/null
+++ b/src/lib/version.cr
@@ -0,0 +1,7 @@
+module Arkisto
+  class Version
+    PROGRAM_ARKISTOCTL = "arkistoctl"
+    VERSION = "0.1.0"
+  end
+end
+