danarchy_sys 0.2.10
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.txt +86 -0
- data/Rakefile +6 -0
- data/bin/console +6 -0
- data/bin/danarchy_sys +4 -0
- data/bin/setup +18 -0
- data/bin/setup.rb +86 -0
- data/danarchy_sys.gemspec +38 -0
- data/lib/danarchy_sys.rb +16 -0
- data/lib/danarchy_sys/aws.rb +9 -0
- data/lib/danarchy_sys/aws/compute.rb +13 -0
- data/lib/danarchy_sys/cli.rb +63 -0
- data/lib/danarchy_sys/cli/instance_manager.rb +130 -0
- data/lib/danarchy_sys/cli/instance_manager/instance_status.rb +50 -0
- data/lib/danarchy_sys/cli/instance_manager/prompts_create_instance.rb +182 -0
- data/lib/danarchy_sys/cli/menus.rb +60 -0
- data/lib/danarchy_sys/cli/providers.rb +33 -0
- data/lib/danarchy_sys/config.rb +66 -0
- data/lib/danarchy_sys/helpers.rb +61 -0
- data/lib/danarchy_sys/openstack.rb +9 -0
- data/lib/danarchy_sys/openstack/compute.rb +59 -0
- data/lib/danarchy_sys/openstack/compute/flavors.rb +47 -0
- data/lib/danarchy_sys/openstack/compute/images.rb +52 -0
- data/lib/danarchy_sys/openstack/compute/instances.rb +120 -0
- data/lib/danarchy_sys/openstack/compute/keypairs.rb +84 -0
- data/lib/danarchy_sys/openstack/compute/tests/answer_yes.rb +7 -0
- data/lib/danarchy_sys/openstack/compute/tests/flavors_test.rb +10 -0
- data/lib/danarchy_sys/openstack/compute/tests/images_test.rb +14 -0
- data/lib/danarchy_sys/openstack/compute/tests/instance_create_test.rb +14 -0
- data/lib/danarchy_sys/openstack/compute/tests/instance_manage_test.rb +24 -0
- data/lib/danarchy_sys/openstack/compute/tests/keypairs_prompt_test.rb +12 -0
- data/lib/danarchy_sys/openstack/compute/tests/keypairs_test.rb +38 -0
- data/lib/danarchy_sys/openstack/compute/tests/manage_test.rb +16 -0
- data/lib/danarchy_sys/printformats.rb +49 -0
- data/lib/danarchy_sys/version.rb +3 -0
- metadata +151 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
class InstanceStatus
|
3
|
+
def self.all_instances(os_compute, instances)
|
4
|
+
istats = {}
|
5
|
+
|
6
|
+
id = 1
|
7
|
+
instances.each do |instance|
|
8
|
+
istats[id] = single_instance(os_compute, instance)
|
9
|
+
id += 1
|
10
|
+
end
|
11
|
+
|
12
|
+
fields = %w[name state image vcpus ram disk keypair]
|
13
|
+
format = PrintFormats.printf_numhash_values(istats, fields)
|
14
|
+
_header(format)
|
15
|
+
|
16
|
+
istats.each do |id, i|
|
17
|
+
printf("#{format}\n", "#{id}.",
|
18
|
+
i['name'],
|
19
|
+
i['state'],
|
20
|
+
i['image'],
|
21
|
+
i['vcpus'],
|
22
|
+
i['ram'],
|
23
|
+
i['disk'],
|
24
|
+
i['keypair'],
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.single_instance(os_compute, instance)
|
30
|
+
comp_inst = os_compute.compute_instances
|
31
|
+
comp_imgs = os_compute.compute_images
|
32
|
+
comp_flvs = os_compute.compute_flavors
|
33
|
+
|
34
|
+
image = comp_imgs.get_image_by_id(instance.image['id'])
|
35
|
+
flavor = comp_flvs.get_flavor_by_id(instance.flavor['id'])
|
36
|
+
|
37
|
+
istats = { 'name' => instance.name,
|
38
|
+
'state' => instance.state,
|
39
|
+
'image' => image.name,
|
40
|
+
'vcpus' => flavor.vcpus,
|
41
|
+
'ram' => flavor.ram,
|
42
|
+
'disk' => flavor.disk,
|
43
|
+
'keypair' => instance.key_name,
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def self._header(format)
|
48
|
+
printf("#{format}\n", 'Id', 'Name', 'State', 'Image', 'VCPUS', 'RAM', 'Disk', 'KeyPair')
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
|
2
|
+
# CLI Prompt to create a new instance
|
3
|
+
class PromptsCreateInstance
|
4
|
+
def self.create_instance(os_compute, instance_name)
|
5
|
+
comp_inst = os_compute.compute_instances
|
6
|
+
comp_imgs = os_compute.compute_images
|
7
|
+
comp_flvs = os_compute.compute_flavors
|
8
|
+
comp_keys = os_compute.compute_keypairs
|
9
|
+
|
10
|
+
# Prompt for and check that instance_name is unused
|
11
|
+
if instance_name == 'nil'
|
12
|
+
print "\nWhat should we name the instance?: "
|
13
|
+
instance_name = gets.chomp
|
14
|
+
end
|
15
|
+
|
16
|
+
# Make sure instance_name isn't already in use
|
17
|
+
until comp_inst.check_instance(instance_name) == false
|
18
|
+
print "\n#{instance_name} already exists! Try another name: "
|
19
|
+
instance_name = gets.chomp
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "Creating instance: #{instance_name}"
|
23
|
+
|
24
|
+
# Prompt for image
|
25
|
+
puts "\nSelect an image (operating system) for #{instance_name}"
|
26
|
+
image = PromptsCreateInstance.image(comp_imgs)
|
27
|
+
|
28
|
+
# Prompt for flavor
|
29
|
+
puts "\nSelect a flavor (instance size) for #{instance_name}"
|
30
|
+
flavor = PromptsCreateInstance.flavor(comp_flvs)
|
31
|
+
|
32
|
+
# Prompt for keypair
|
33
|
+
puts "\nSelect a keypair (SSH key) for #{instance_name}"
|
34
|
+
keypair = PromptsCreateInstance.keypair(comp_keys)
|
35
|
+
|
36
|
+
# Print summary and prompt to continue
|
37
|
+
puts "\nInstance Name: #{instance_name}"
|
38
|
+
puts " Linux: #{image.name}"
|
39
|
+
puts "Instance Size: #{flavor.name}"
|
40
|
+
puts " Keypair: #{keypair.name}"
|
41
|
+
|
42
|
+
print 'Should we continue with creating the instance? (Y/N): '
|
43
|
+
instance = 'nil'
|
44
|
+
continue = gets.chomp
|
45
|
+
|
46
|
+
if continue =~ /^y(es)?$/i
|
47
|
+
puts "Creating instance: #{instance_name}"
|
48
|
+
instance = comp_inst.create_instance(instance_name, image.id, flavor.id, keypair.name)
|
49
|
+
else
|
50
|
+
puts "Abandoning creation of #{instance_name}"
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
|
54
|
+
instance_check = comp_inst.check_instance(instance_name)
|
55
|
+
|
56
|
+
if instance_check == true
|
57
|
+
puts "Instance #{instance.name} is ready!"
|
58
|
+
return instance
|
59
|
+
else
|
60
|
+
raise "Error: Could not create instance: #{instance_name}" if instance_check == false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.image(comp_imgs)
|
65
|
+
images_numbered = Helpers.array_to_numhash(comp_imgs.list_images)
|
66
|
+
image_name = 'nil'
|
67
|
+
|
68
|
+
# List available images in a numbered hash.
|
69
|
+
puts "\nAvailable Images:"
|
70
|
+
i_name_length = Helpers.hash_largest_value(images_numbered).length
|
71
|
+
printf("%0s %-#{i_name_length}s\n", 'Id', 'Image')
|
72
|
+
images_numbered.each do |id, i_name|
|
73
|
+
printf("%0s %-#{i_name_length}s\n", "#{id}.", i_name)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Loop input until existing image is selected
|
77
|
+
print 'Which image should we use for this instance?: '
|
78
|
+
until images_numbered.values.include?(image_name)
|
79
|
+
image_name = gets.chomp
|
80
|
+
|
81
|
+
if image_name =~ /^[0-9]*$/
|
82
|
+
until images_numbered.keys.include?(image_name)
|
83
|
+
print "#{image_name} is not a valid Id. Enter an Id from above: "
|
84
|
+
image_name = gets.chomp
|
85
|
+
end
|
86
|
+
|
87
|
+
image_name = images_numbered[image_name.to_s]
|
88
|
+
end
|
89
|
+
|
90
|
+
image_check = images_numbered.values.include?(image_name)
|
91
|
+
print "#{image_name} is not a valid image. Please enter an option from above: " if image_check == false
|
92
|
+
end
|
93
|
+
|
94
|
+
print "Image Name: #{image_name}\n"
|
95
|
+
comp_imgs.get_image_by_name(image_name)
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.flavor(comp_flvs)
|
99
|
+
flavors = Helpers.objects_to_numhash(comp_flvs.all_flavors.sort_by(&:ram))
|
100
|
+
flavor_name = 'nil'
|
101
|
+
|
102
|
+
puts "\nAvailable Instance Flavors:"
|
103
|
+
puts sprintf("%0s %-15s %-10s %-10s %0s", 'Id', 'Name', 'RAM', 'VCPUs', 'Disk')
|
104
|
+
flavors.each do |id, flavor|
|
105
|
+
print sprintf("%0s %-15s %-10s %-10s %0s\n",
|
106
|
+
"#{id}.", flavor[:name].split('.')[1], flavor[:ram], flavor[:vcpus], flavor[:disk])
|
107
|
+
end
|
108
|
+
|
109
|
+
print 'Which flavor should we use for this instance?: '
|
110
|
+
flavor_check = false
|
111
|
+
|
112
|
+
until flavor_check == true
|
113
|
+
flavor_name = gets.chomp
|
114
|
+
|
115
|
+
if flavor_name =~ /^[0-9]*$/
|
116
|
+
until flavors.keys.include?(flavor_name.to_i)
|
117
|
+
print "#{flavor_name} is not a valid Id. Enter an Id from above: "
|
118
|
+
flavor_name = gets.chomp
|
119
|
+
end
|
120
|
+
|
121
|
+
flavor_name = flavors[flavor_name.to_i][:name].split('.')[1]
|
122
|
+
end
|
123
|
+
|
124
|
+
flavors.each_value do |flavor|
|
125
|
+
flavor_check = true if flavor[:name].end_with?(flavor_name)
|
126
|
+
end
|
127
|
+
|
128
|
+
print "#{flavor_name} is not a valid flavor. Please enter an option from above: " if flavor_check == false
|
129
|
+
end
|
130
|
+
|
131
|
+
print "Flavor Name: #{flavor_name}\n"
|
132
|
+
comp_flvs.get_flavor(flavor_name)
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.keypair(comp_keys)
|
136
|
+
keypairs = Helpers.objects_to_numhash(comp_keys.all_keypairs)
|
137
|
+
keypair_name = 'nil'
|
138
|
+
|
139
|
+
# List available keypairs
|
140
|
+
puts "\nAvailable Keypairs:"
|
141
|
+
print sprintf("%0s %-15s\n", 'Id', 'KeyPair Name')
|
142
|
+
keypairs.each do |id, key|
|
143
|
+
print sprintf("%0s %-15s\n", "#{id}.", key[:name])
|
144
|
+
end
|
145
|
+
|
146
|
+
# Loop input until existing flavor is selected or create a new one
|
147
|
+
print 'Enter a keypair to use for this instance or enter a name for a new keypair : '
|
148
|
+
keypair_check = false
|
149
|
+
|
150
|
+
until keypair_check == true
|
151
|
+
keypair_name = gets.chomp
|
152
|
+
|
153
|
+
# Accept keypair Id as an entry
|
154
|
+
if keypair_name =~ /^[0-9]*$/
|
155
|
+
until keypairs.keys.include?(keypair_name.to_i)
|
156
|
+
print "#{keypair_name} is not a valid Id.
|
157
|
+
Enter an Id from above, or \'return\' to restart keypair selection. : "
|
158
|
+
keypair_name = gets.chomp
|
159
|
+
return keypair(settings, compute) if keypair_name == 'return'
|
160
|
+
end
|
161
|
+
|
162
|
+
keypair_name = keypairs[keypair_name.to_i][:name]
|
163
|
+
end
|
164
|
+
|
165
|
+
keypair_check = Helpers.check_nested_hash_value(keypairs, :name, keypair_name)
|
166
|
+
|
167
|
+
if keypair_check == false
|
168
|
+
print "#{keypair_name} is not an existing keypair.
|
169
|
+
Should we create a new keypair named #{keypair_name}? (Y/N): "
|
170
|
+
|
171
|
+
if gets.chomp =~ /^y(es)?$/i
|
172
|
+
puts "Creating keypair: #{keypair_name}!"
|
173
|
+
return comp_keys.create_keypair(keypair_name)
|
174
|
+
else
|
175
|
+
print 'Please enter an option from above: '
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
comp_keys.get_keypair(keypair_name)
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
class Menus
|
3
|
+
def self.get_menu(menu)
|
4
|
+
menus = { 'main' => { 'instance' => 'Instance Manager',
|
5
|
+
'keypair' => 'Keypair Manager (Not yet implemented!)',
|
6
|
+
'help' => 'Outputs commands for current the menu level',
|
7
|
+
'exit' => 'Exit dAnarchy_sys'},
|
8
|
+
'instance' => { 'status' => 'Current running status of instance',
|
9
|
+
'connect' => 'Connect to instance through SSH',
|
10
|
+
'start' => 'Start a currently stopped instance',
|
11
|
+
'stop' => 'Stop a currently running instance',
|
12
|
+
'pause' => 'Pause instance (to RAM)',
|
13
|
+
'unpause' => 'Unpause instance from paused state',
|
14
|
+
'suspend' => 'Suspend Instance (to disk)',
|
15
|
+
'resume' => 'Resume instance from suspended state',
|
16
|
+
'create' => 'Create a new instance',
|
17
|
+
'delete' => 'Delete this instance'}
|
18
|
+
}
|
19
|
+
|
20
|
+
menus[menu]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.numbered_menu(menu)
|
24
|
+
numbered_menu = Helpers.hash_to_numhash(get_menu(menu))
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.print_menu(menu)
|
28
|
+
if menu == 'main'
|
29
|
+
puts 'dAnarchy_sys main menu commands:'
|
30
|
+
puts 'Enter \'help\' to view available commands or \'exit\' to leave.'
|
31
|
+
# print_menu(menu)
|
32
|
+
elsif menu == 'instance'
|
33
|
+
puts 'Instance Manager commands: '
|
34
|
+
puts 'Enter \'help\' to view available commands or \'back\' for the main menu.'
|
35
|
+
# print_menu(menu)
|
36
|
+
elsif menu == 'keypair'
|
37
|
+
puts 'Keypair Manager commands: '
|
38
|
+
puts 'Not yet implemented!'
|
39
|
+
return
|
40
|
+
# print_menu(menu)
|
41
|
+
elsif menu == 'storage'
|
42
|
+
puts 'Storage Manager commands: '
|
43
|
+
puts 'Not yet implemented!'
|
44
|
+
return
|
45
|
+
# print_menu(menu)
|
46
|
+
end
|
47
|
+
|
48
|
+
# numbered_menu = Helpers.hash_to_numhash(menu)
|
49
|
+
numbered_menu = numbered_menu(menu)
|
50
|
+
menu = get_menu(menu)
|
51
|
+
|
52
|
+
fields = PrintFormats.printf_numhash(numbered_menu)
|
53
|
+
|
54
|
+
numbered_menu.each do |id, v|
|
55
|
+
v.each do |name, info|
|
56
|
+
printf("#{fields}\n", "#{id}.", "#{name}:", info)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
class Providers
|
3
|
+
def self.chooser
|
4
|
+
config = ConfigMgr.new
|
5
|
+
danarchysys_config = config.load
|
6
|
+
providers = Helpers.array_to_numhash(danarchysys_config[:connections].keys)
|
7
|
+
provider = 'nil'
|
8
|
+
|
9
|
+
if providers.count == 1
|
10
|
+
provider = providers['1']
|
11
|
+
return provider
|
12
|
+
end
|
13
|
+
|
14
|
+
fields = PrintFormats.printf_hash(providers)
|
15
|
+
printf("#{fields}\n", 'Id', 'Provider')
|
16
|
+
providers.each do |id, provider|
|
17
|
+
printf("#{fields}\n", "#{id}.", provider)
|
18
|
+
end
|
19
|
+
|
20
|
+
until providers.values.include?(provider)
|
21
|
+
print 'Which provider should we use? (enter \'exit\' to leave): '
|
22
|
+
provider = gets.chomp
|
23
|
+
|
24
|
+
abort('Exiting') if provider == 'exit'
|
25
|
+
|
26
|
+
if provider =~ /^[0-9]*$/ # select by Id
|
27
|
+
provider = providers[provider.to_s]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
provider
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
# dAnarchy_sys config management
|
5
|
+
class ConfigMgr
|
6
|
+
def initialize
|
7
|
+
@danarchysys_path = File.realpath(File.join(File.dirname(__FILE__), '..', '..'))
|
8
|
+
@config_file = File.join(@danarchysys_path, 'config', 'danarchysys.yml')
|
9
|
+
end
|
10
|
+
|
11
|
+
def config_template
|
12
|
+
config_template = {
|
13
|
+
:connections => {},
|
14
|
+
:settings => {}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def load
|
19
|
+
if File.exists?(@config_file)
|
20
|
+
return YAML.load_file(@config_file)
|
21
|
+
else
|
22
|
+
return config_template
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def save(param_hash)
|
27
|
+
File.write(@config_file, param_hash.to_yaml)
|
28
|
+
end
|
29
|
+
|
30
|
+
def connection_add(provider, openstack_auth_url, openstack_username, openstack_api_key, openstack_tenant)
|
31
|
+
danarchysys_config = load
|
32
|
+
|
33
|
+
danarchysys_config[:connections][provider.to_sym] = {
|
34
|
+
openstack_auth_url: openstack_auth_url,
|
35
|
+
openstack_username: openstack_username,
|
36
|
+
openstack_api_key: openstack_api_key,
|
37
|
+
openstack_tenant: openstack_tenant
|
38
|
+
}
|
39
|
+
|
40
|
+
danarchysys_config
|
41
|
+
end
|
42
|
+
|
43
|
+
def connection_delete(provider)
|
44
|
+
danarchysys_config = load
|
45
|
+
|
46
|
+
danarchysys_config[:connections].delete(provider.to_sym)
|
47
|
+
|
48
|
+
danarchysys_config
|
49
|
+
end
|
50
|
+
|
51
|
+
def setting_add(name, value)
|
52
|
+
danarchysys_config = load
|
53
|
+
|
54
|
+
danarchysys_config[:settings][name.to_sym] = value
|
55
|
+
|
56
|
+
danarchysys_config
|
57
|
+
end
|
58
|
+
|
59
|
+
def setting_delete(name)
|
60
|
+
danarchysys_config = load
|
61
|
+
|
62
|
+
danarchysys_config[:settings].delete(name.to_sym)
|
63
|
+
|
64
|
+
danarchysys_config
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
# Routine methods for DanarchySys
|
3
|
+
class Helpers
|
4
|
+
def self.array_to_numhash(array)
|
5
|
+
numbered_hash = {}
|
6
|
+
|
7
|
+
count = 1
|
8
|
+
array.sort.each do |item|
|
9
|
+
numbered_hash[count.to_s] = item
|
10
|
+
count += 1
|
11
|
+
end
|
12
|
+
|
13
|
+
numbered_hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.hash_to_numhash(hash)
|
17
|
+
numbered_hash = {}
|
18
|
+
|
19
|
+
hash.map.with_index(1) do | (k, v), index |
|
20
|
+
numbered_hash[index] = {k => v}
|
21
|
+
end
|
22
|
+
|
23
|
+
numbered_hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.objects_to_numhash(objects)
|
27
|
+
numbered_object_hash = {}
|
28
|
+
|
29
|
+
objects.map.with_index(1) do | obj, index |
|
30
|
+
numbered_object_hash[index] = obj.all_attributes
|
31
|
+
end
|
32
|
+
|
33
|
+
numbered_object_hash
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.hash_largest_key(hash)
|
37
|
+
hash.keys.map(&:to_s).max_by(&:size)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.hash_largest_value(hash)
|
41
|
+
hash.values.map(&:to_s).max_by(&:size)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.hash_largest_nested_key(hash)
|
45
|
+
hash.each_value.flat_map(&:keys).max_by(&:size)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.hash_largest_nested_value(hash)
|
49
|
+
hash.each_value.flat_map(&:values).max_by(&:size)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.check_nested_hash_value(hash, key, value)
|
53
|
+
check = false
|
54
|
+
|
55
|
+
hash.each_value do |val|
|
56
|
+
check = true if val[key].end_with?(value)
|
57
|
+
end
|
58
|
+
|
59
|
+
check
|
60
|
+
end
|
61
|
+
end
|