triton-ops 0.18.4.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,123 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require 'contracts' # BSD-2-Clause License
5
+
6
+ require_relative '../../support/feature/comparable_as_hash'
7
+ require_relative '../../support/feature/hash_from_initialization_contract'
8
+ require_relative '../../support/types'
9
+
10
+ module TritonOps
11
+ module Resource
12
+ class VirtualMachine
13
+ class Disk
14
+ include ::Contracts::Core
15
+ include ::Contracts::Builtin
16
+ include ::TritonOps::Support::Feature::ComparableAsHash
17
+ include ::TritonOps::Support::Feature::HashFromInitializationContract
18
+ include ::TritonOps::Support::Types
19
+
20
+ Media = ::Contracts::Enum[*%w(disk cdrom)]
21
+ Model = ::Contracts::Enum[*%w(virtio ide scsi)]
22
+
23
+ Contract ({
24
+ :block_size => Integer,
25
+ :compression => CompressionAlgorithm,
26
+ :media => Media,
27
+ :model => Model,
28
+ :path => String,
29
+ :refreservation => Integer,
30
+ :size => Integer,
31
+ :zfs_filesystem => String,
32
+ :zpool => String,
33
+ :boot => Maybe[Bool],
34
+ :image_size => Maybe[Integer],
35
+ :image_uuid => Maybe[String],
36
+ }) => Disk
37
+ def initialize(**options)
38
+ @options = options
39
+ self.to_h
40
+ remove_instance_variable '@options'
41
+ self
42
+ end
43
+
44
+ #######################
45
+ # Required Properties #
46
+ #######################
47
+
48
+ Contract None => Integer
49
+ def block_size
50
+ @block_size ||= @options.fetch :block_size
51
+ end
52
+
53
+ Contract None => CompressionAlgorithm
54
+ def compression
55
+ @compression ||= @options.fetch :compression
56
+ end
57
+
58
+ Contract None => Media
59
+ def media
60
+ @media ||= @options.fetch :media
61
+ end
62
+
63
+ Contract None => Model
64
+ def model
65
+ @model ||= @options.fetch :model
66
+ end
67
+
68
+ Contract None => String
69
+ def path
70
+ @path ||= @options.fetch :path
71
+ end
72
+
73
+ Contract None => Integer
74
+ def refreservation
75
+ @refreservation ||= @options.fetch :refreservation
76
+ end
77
+
78
+ Contract None => Integer
79
+ def size
80
+ @size ||= @options.fetch :size
81
+ end
82
+
83
+ Contract None => String
84
+ def zfs_filesystem
85
+ @zfs_filesystem ||= @options.fetch :zfs_filesystem
86
+ end
87
+
88
+ Contract None => String
89
+ def zpool
90
+ @zpool ||= @options.fetch :zpool
91
+ end
92
+
93
+ #######################
94
+ # Optional Properties #
95
+ #######################
96
+
97
+ Contract None => Maybe[Bool]
98
+ def boot
99
+ @boot ||= (@options || {}).fetch(:boot, nil)
100
+ end
101
+
102
+ Contract None => Maybe[Integer]
103
+ def image_size
104
+ @image_size ||= (@options || {}).fetch(:image_size, nil)
105
+ end
106
+
107
+ Contract None => Maybe[String]
108
+ def image_uuid
109
+ @image_uuid ||= (@options || {}).fetch(:image_uuid, nil)
110
+ end
111
+
112
+ #######################
113
+ # Convenience Methods #
114
+ #######################
115
+
116
+ Contract None => Bool
117
+ def image?
118
+ image_uuid ? true : false
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,119 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require 'contracts' # BSD-2-Clause License
5
+
6
+ require_relative '../../support/feature/comparable_as_hash'
7
+ require_relative '../../support/feature/hash_from_initialization_contract'
8
+ require_relative '../../support/types'
9
+
10
+ module TritonOps
11
+ module Resource
12
+ class VirtualMachine
13
+ class NetworkInterface
14
+ include ::Contracts::Core
15
+ include ::Contracts::Builtin
16
+ include ::TritonOps::Support::Feature::ComparableAsHash
17
+ include ::TritonOps::Support::Feature::HashFromInitializationContract
18
+ include ::TritonOps::Support::Types
19
+
20
+ Model = ::Contracts::Enum[*%w(virtio e1000 rtl8139)]
21
+
22
+ Contract ({
23
+ :interface => String,
24
+ :ip => String,
25
+ :ips => ArrayOf[String],
26
+ :mac => String,
27
+ :nic_tag => String,
28
+ :gateway => Maybe[String],
29
+ :gateways => Maybe[ArrayOf[String]],
30
+ :model => Maybe[Model],
31
+ :mtu => Maybe[Integer],
32
+ :netmask => Maybe[String],
33
+ :network_uuid => Maybe[String],
34
+ :primary => Maybe[Bool],
35
+ :vlan_id => Maybe[Integer],
36
+ }) => NetworkInterface
37
+ def initialize(**options)
38
+ @options = options
39
+ self.to_h
40
+ remove_instance_variable '@options'
41
+ self
42
+ end
43
+
44
+ #######################
45
+ # Required Properties #
46
+ #######################
47
+
48
+ Contract None => String
49
+ def interface
50
+ @interface ||= @options.fetch :interface
51
+ end
52
+
53
+ Contract None => String
54
+ def ip
55
+ @ip ||= @options.fetch :ip
56
+ end
57
+
58
+ Contract None => ArrayOf[String]
59
+ def ips
60
+ @ips ||= @options.fetch :ips
61
+ end
62
+
63
+ Contract None => String
64
+ def mac
65
+ @mac ||= @options.fetch :mac
66
+ end
67
+
68
+ Contract None => String
69
+ def nic_tag
70
+ @nic_tag ||= @options.fetch :nic_tag
71
+ end
72
+
73
+ #######################
74
+ # Optional Properties #
75
+ #######################
76
+
77
+ Contract None => Maybe[String]
78
+ def gateway
79
+ @gateway ||= (@options || {}).fetch(:gateway, nil)
80
+ end
81
+
82
+ Contract None => ArrayOf[String]
83
+ def gateways
84
+ @gateways ||= @options.fetch(:gateways, [])
85
+ end
86
+
87
+ Contract None => Maybe[Model]
88
+ def model
89
+ @model ||= (@options || {}).fetch(:model, nil)
90
+ end
91
+
92
+ Contract None => Maybe[Integer]
93
+ def mtu
94
+ @mtu ||= (@options || {}).fetch(:mtu, nil)
95
+ end
96
+
97
+ Contract None => Maybe[String]
98
+ def netmask
99
+ @netmask ||= (@options || {}).fetch(:netmask, nil)
100
+ end
101
+
102
+ Contract None => Maybe[String]
103
+ def network_uuid
104
+ @network_uuid ||= (@options || {}).fetch(:network_uuid, nil)
105
+ end
106
+
107
+ Contract None => Maybe[Bool]
108
+ def primary
109
+ @primary ||= (@options || {}).fetch(:primary, nil)
110
+ end
111
+
112
+ Contract None => Maybe[Integer]
113
+ def vlan_id
114
+ @vlan_id ||= (@options || {}).fetch(:vlan_id, nil)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,14 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require_relative 'virtual_machine'
5
+
6
+ module TritonOps
7
+ module Resource
8
+ module VM
9
+ def self.new(**options)
10
+ TritonOps::Resource::VirtualMachine.new options
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require_relative 'resource/image'
5
+ require_relative 'resource/platform'
6
+ require_relative 'resource/server'
7
+ require_relative 'resource/user'
8
+ require_relative 'resource/virtual_machine'
9
+ require_relative 'resource/virtual_machine/disk'
10
+ require_relative 'resource/virtual_machine/network_interface'
11
+ require_relative 'resource/vm'
@@ -0,0 +1,56 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require 'contracts' # BSD-2-Clause License
5
+ require 'redis' # MIT License
6
+ require 'json' # Ruby Standard Library
7
+
8
+ require_relative 'support/type_coercion'
9
+ require_relative 'resources'
10
+
11
+ module TritonOps
12
+ class Snapshot
13
+ include ::Contracts::Core
14
+ include ::Contracts::Builtin
15
+ include ::TritonOps::Support::TypeCoercion
16
+
17
+ def initialize(headnode, timestamp, **options)
18
+ @headnode = headnode
19
+ @timestamp = Coerce.to_time timestamp
20
+ @namespace = options.fetch(:namespace) { '' }
21
+ self
22
+ end
23
+
24
+ attr_reader :headnode, :timestamp
25
+
26
+ def prefix
27
+ @prefix ||= "#{@namespace}/headnode/#{headnode}/snapshot/#{timestamp.iso8601}"
28
+ end
29
+
30
+ def array_from_json_at_redis_prefix
31
+ raise TypeError unless type = ::TritonOps::Resource.constants.find { |name| name.to_s.downcase == __callee__.to_s.chop }
32
+
33
+ if instance_variable_defined? "@#{__callee__}"
34
+ instance_variable_get "@#{__callee__}"
35
+ else
36
+ instance_variable_set(
37
+ "@#{__callee__}",
38
+ JSON.parse(
39
+ (redis.get("#{prefix}/#{__callee__}") || '[]'),
40
+ symbolize_names: true).map { |spec| ::TritonOps::Resource.const_get(type).new spec })
41
+ end
42
+ end
43
+
44
+ alias images array_from_json_at_redis_prefix
45
+ alias platforms array_from_json_at_redis_prefix
46
+ alias servers array_from_json_at_redis_prefix
47
+ alias users array_from_json_at_redis_prefix
48
+ alias vms array_from_json_at_redis_prefix
49
+
50
+ private
51
+
52
+ def redis
53
+ @redis ||= Redis.new
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,42 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require 'redis' # MIT License
5
+ require_relative '../snapshot'
6
+
7
+ module TritonOps
8
+ class Snapshot
9
+ class Catalog
10
+ def initialize(**options)
11
+ @namespace = options.fetch(:namespace) { '' }
12
+ self
13
+ end
14
+
15
+ attr_accessor :namespace
16
+
17
+ def headnodes
18
+ redis.smembers("#{namespace}/headnodes") || []
19
+ end
20
+
21
+ def snapshots(headnode)
22
+ return [] unless headnodes.include? headnode
23
+ redis.smembers("#{namespace}/headnode/#{headnode}/snapshots") || []
24
+ end
25
+
26
+ def properties(headnode, snapshot)
27
+ return [] unless snapshots(headnode).include? snapshot
28
+ redis.smembers("#{namespace}/headnode/#{headnode}/snapshot/#{snapshot}") || []
29
+ end
30
+
31
+ def get(headnode, snapshot, property = :all)
32
+ TritonOps::Snapshot.new headnode, snapshot, namespace: @namespace
33
+ end
34
+
35
+ private
36
+
37
+ def redis
38
+ @redis ||= Redis.new
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,96 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require 'contracts' # BSD-2-Clause License
5
+ require 'json' # Ruby Standard Library
6
+ require 'redis' # MIT License
7
+ require 'time' # Ruby Standard Library
8
+
9
+ module TritonOps
10
+ class Snapshot
11
+ class Collector
12
+ include ::Contracts::Core
13
+ include ::Contracts::Builtin
14
+
15
+ Contract String, KeywordArgs[namespace: Optional[String]] => Collector
16
+ def initialize(headnode, **options)
17
+ @headnode = headnode
18
+ @namespace = options.fetch(:namespace) { '' }
19
+ self
20
+ end
21
+
22
+ attr_reader :headnode
23
+ attr_accessor :namespace
24
+
25
+ COMMANDS = {
26
+ images: %q(/opt/smartdc/bin/sdc-imgadm list --json),
27
+ platforms: %q(/opt/smartdc/bin/sdcadm platform list --json),
28
+ servers: %q(/opt/smartdc/bin/sdc-server lookup -j),
29
+ timestamp: %q(/usr/xpg4/bin/date +%s),
30
+ users: %q(/opt/smartdc/bin/sdc-useradm search --json 'uuid=*'),
31
+ vms: %q(/opt/smartdc/bin/sdc-vmadm list --json),
32
+ }
33
+
34
+ TARGETS = COMMANDS.keys - %i(timestamp)
35
+
36
+ Contract RespondTo[:to_s], String => Any
37
+ def record(key, value)
38
+ snapshot = timestamp.iso8601
39
+ redis.multi do
40
+ redis.set "#{namespace}/headnode/#{headnode}/snapshot/#{snapshot}/#{key}", value
41
+ redis.sadd "#{namespace}/headnode/#{headnode}/snapshot/#{snapshot}", key
42
+ redis.sadd "#{namespace}/headnode/#{headnode}/snapshots", snapshot
43
+ redis.sadd "#{namespace}/headnodes", headnode
44
+ end
45
+ end
46
+
47
+ Contract RespondTo[:to_s], Any => Any
48
+ def record(key, value)
49
+ record key, JSON.dump(value)
50
+ end
51
+
52
+ # Most of the commands we need to execute output a JSON array.
53
+ def array_from_json_output_of_remote_command
54
+ if instance_variable_defined? "@#{__callee__}"
55
+ instance_variable_get "@#{__callee__}"
56
+ else
57
+ instance_variable_set(
58
+ "@#{__callee__}",
59
+ JSON.parse(execute_remote!(command(__callee__))))
60
+ end
61
+ end
62
+
63
+ alias images array_from_json_output_of_remote_command
64
+ alias platforms array_from_json_output_of_remote_command
65
+ alias servers array_from_json_output_of_remote_command
66
+ alias users array_from_json_output_of_remote_command
67
+ alias vms array_from_json_output_of_remote_command
68
+
69
+ # This one needs special handling, since it outputs one JSON object per line.
70
+ def servers
71
+ @servers ||= execute_remote!(command(:servers)).each_line.map { |line| JSON.parse line }
72
+ end
73
+
74
+ # This one needs special handling, since there is no JSON involved.
75
+ Contract None => Time
76
+ def timestamp
77
+ @timestamp ||= Time.at(
78
+ execute_remote!(command(:timestamp)).to_i).utc
79
+ end
80
+
81
+ private
82
+
83
+ def command(target)
84
+ COMMANDS.fetch target
85
+ end
86
+
87
+ def execute_remote!(command)
88
+ %x(ssh #{headnode} -- #{command})
89
+ end
90
+
91
+ def redis
92
+ @redis ||= Redis.new
93
+ end
94
+ end
95
+ end
96
+ end