knife-clc 0.0.1.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.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +295 -0
- data/lib/chef/knife/clc_base.rb +71 -0
- data/lib/chef/knife/clc_datacenter_list.rb +35 -0
- data/lib/chef/knife/clc_group_create.rb +111 -0
- data/lib/chef/knife/clc_group_list.rb +109 -0
- data/lib/chef/knife/clc_ip_create.rb +129 -0
- data/lib/chef/knife/clc_ip_delete.rb +48 -0
- data/lib/chef/knife/clc_operation_show.rb +38 -0
- data/lib/chef/knife/clc_server_create.rb +595 -0
- data/lib/chef/knife/clc_server_delete.rb +38 -0
- data/lib/chef/knife/clc_server_list.rb +119 -0
- data/lib/chef/knife/clc_server_power_off.rb +38 -0
- data/lib/chef/knife/clc_server_power_on.rb +38 -0
- data/lib/chef/knife/clc_server_reboot.rb +38 -0
- data/lib/chef/knife/clc_server_show.rb +188 -0
- data/lib/chef/knife/clc_template_list.rb +91 -0
- data/lib/clc/client.rb +204 -0
- data/lib/clc/cloud_exceptions.rb +39 -0
- data/lib/clc.rb +2 -0
- data/lib/knife-clc/version.rb +5 -0
- metadata +250 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'chef/knife/clc_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class ClcGroupCreate < Knife
|
6
|
+
include Knife::ClcBase
|
7
|
+
|
8
|
+
banner 'knife clc group create (options)'
|
9
|
+
|
10
|
+
option :clc_name,
|
11
|
+
:long => '--name NAME',
|
12
|
+
:description => 'Name of the group to create',
|
13
|
+
:on => :head
|
14
|
+
|
15
|
+
option :clc_description,
|
16
|
+
:long => '--description DESCRIPTION',
|
17
|
+
:description => 'User-defined description of this group',
|
18
|
+
:on => :head
|
19
|
+
|
20
|
+
option :clc_parent,
|
21
|
+
:long => '--parent ID',
|
22
|
+
:description => "ID of the parent group. Retrieved from query to parent group, or by looking at the URL on the UI pages in the Control Portal",
|
23
|
+
:on => :head
|
24
|
+
|
25
|
+
option :clc_custom_fields,
|
26
|
+
:long => '--custom-field KEY=VALUE',
|
27
|
+
:description => 'Custom field key-value pair',
|
28
|
+
:on => :head,
|
29
|
+
:proc => ->(param) do
|
30
|
+
Chef::Config[:knife][:clc_custom_fields] ||= []
|
31
|
+
Chef::Config[:knife][:clc_custom_fields] << param
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_and_validate_parameters
|
35
|
+
unless config[:clc_name]
|
36
|
+
errors << 'Name is required'
|
37
|
+
end
|
38
|
+
|
39
|
+
unless config[:clc_parent]
|
40
|
+
errors << 'Parent Group ID is required'
|
41
|
+
end
|
42
|
+
|
43
|
+
custom_fields = config[:clc_custom_fields]
|
44
|
+
if custom_fields && custom_fields.any?
|
45
|
+
parse_custom_fields(custom_fields)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_custom_fields(custom_fields)
|
50
|
+
custom_fields.map! do |param|
|
51
|
+
key, value = param.split('=', 2)
|
52
|
+
|
53
|
+
unless key && value
|
54
|
+
errors << "Custom field definition #{param} is malformed"
|
55
|
+
next
|
56
|
+
end
|
57
|
+
|
58
|
+
{ 'id' => key, 'value' => value }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def prepare_group_params
|
63
|
+
{
|
64
|
+
'name' => config[:clc_name],
|
65
|
+
'description' => config[:clc_description],
|
66
|
+
'parentGroupId' => config[:clc_parent],
|
67
|
+
'customFields' => config[:clc_custom_fields]
|
68
|
+
}.delete_if { |_, value| value.nil? || value.empty? }
|
69
|
+
end
|
70
|
+
|
71
|
+
def execute
|
72
|
+
group = connection.create_group(prepare_group_params)
|
73
|
+
parent_link = group['links'].detect { |link| link['rel'] == 'parentGroup' }
|
74
|
+
group['parent'] = parent_link['id']
|
75
|
+
|
76
|
+
context[:group] = group
|
77
|
+
|
78
|
+
render
|
79
|
+
end
|
80
|
+
|
81
|
+
def fields
|
82
|
+
%w(name id locationId description type status parent)
|
83
|
+
end
|
84
|
+
|
85
|
+
def headers
|
86
|
+
{
|
87
|
+
'name' => 'Name',
|
88
|
+
'id' => 'ID',
|
89
|
+
'locationId' => 'Location',
|
90
|
+
'description' => 'Description',
|
91
|
+
'type' => 'Type',
|
92
|
+
'status' => 'Status',
|
93
|
+
'parent' => 'Parent'
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def render
|
98
|
+
group = context[:group]
|
99
|
+
|
100
|
+
fields.each do |field|
|
101
|
+
header = headers.fetch(field, field.capitalize)
|
102
|
+
value = group.fetch(field, '-')
|
103
|
+
|
104
|
+
if value
|
105
|
+
ui.info ui.color(header, :bold) + ': ' + value.to_s
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'chef/knife/clc_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class ClcGroupList < Knife
|
6
|
+
include Knife::ClcBase
|
7
|
+
|
8
|
+
banner 'knife clc group list (options)'
|
9
|
+
|
10
|
+
option :clc_datacenter,
|
11
|
+
:long => '--datacenter ID',
|
12
|
+
:short => '-D ID',
|
13
|
+
:description => 'Datacenter ID to show templates from',
|
14
|
+
:on => :head
|
15
|
+
|
16
|
+
option :clc_view,
|
17
|
+
:long => '--view VIEW',
|
18
|
+
:default => 'tree',
|
19
|
+
:description => 'Display output either as a table or a tree',
|
20
|
+
:on => :head
|
21
|
+
|
22
|
+
def parse_and_validate_parameters
|
23
|
+
unless config[:clc_datacenter]
|
24
|
+
errors << 'Datacenter ID is required'
|
25
|
+
end
|
26
|
+
|
27
|
+
unless %w(tree table).include?(config[:clc_view])
|
28
|
+
errors << 'View parameter should be either table or a tree'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def execute
|
33
|
+
context[:groups] = connection.list_groups(config[:clc_datacenter]).map do |group|
|
34
|
+
parent_link = group['links'].find { |link| link['rel'] == 'parentGroup' }
|
35
|
+
group['parentId'] = parent_link['id'] if parent_link
|
36
|
+
group
|
37
|
+
end
|
38
|
+
|
39
|
+
render
|
40
|
+
end
|
41
|
+
|
42
|
+
def filters
|
43
|
+
{
|
44
|
+
'serversCount' => ->(count) { count.zero? ? '-' : count },
|
45
|
+
'parentId' => ->(id) { id ? id : '-' },
|
46
|
+
'description' => ->(description) { description.to_s.empty? ? '-' : description }
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def width_limits
|
51
|
+
{
|
52
|
+
'description' => 0.2
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def fields
|
57
|
+
# TODO AS: Displaying shortened list of fields for now
|
58
|
+
# %w(name id parentId description serversCount type status)
|
59
|
+
%w(name id serversCount type)
|
60
|
+
end
|
61
|
+
|
62
|
+
def headers
|
63
|
+
{
|
64
|
+
'name' => 'Name',
|
65
|
+
'id' => 'ID',
|
66
|
+
'parentId' => 'Parent',
|
67
|
+
'description' => 'Description',
|
68
|
+
'serversCount' => 'Servers',
|
69
|
+
'type' => 'Type',
|
70
|
+
'status' => 'Status'
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def render
|
75
|
+
case config[:clc_view]
|
76
|
+
when 'tree' then render_tree
|
77
|
+
when 'table' then render_table
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def render_tree
|
82
|
+
display_value = ->(group) { "#{group['name']} (#{group['id']})" }
|
83
|
+
|
84
|
+
group_children = ->(parent_group) do
|
85
|
+
context[:groups].select { |group| group['parentId'] == parent_group['id'] }
|
86
|
+
end
|
87
|
+
|
88
|
+
root = context[:groups].find { |group| group['parentId'].nil? }
|
89
|
+
|
90
|
+
return unless root
|
91
|
+
|
92
|
+
ui.info Hirb::Helpers::ParentChildTree.render(root,
|
93
|
+
:type => :directory,
|
94
|
+
:value_method => display_value,
|
95
|
+
:children_method => group_children)
|
96
|
+
end
|
97
|
+
|
98
|
+
def render_table
|
99
|
+
ui.info Hirb::Helpers::AutoTable.render(context[:groups],
|
100
|
+
:fields => fields,
|
101
|
+
:headers => headers,
|
102
|
+
:filters => filters,
|
103
|
+
:max_fields => width_limits,
|
104
|
+
:resize => false,
|
105
|
+
:description => false)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'chef/knife/clc_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class ClcIpCreate < Knife
|
6
|
+
include Knife::ClcBase
|
7
|
+
|
8
|
+
banner 'knife clc ip create (options)'
|
9
|
+
|
10
|
+
option :clc_server,
|
11
|
+
:long => '--server ID',
|
12
|
+
:description => 'ID of the server to assign IP to',
|
13
|
+
:on => :head
|
14
|
+
|
15
|
+
option :clc_internal_ip,
|
16
|
+
:long => '--internal IP',
|
17
|
+
:description => 'The internal (private) IP address to map to the new public IP address',
|
18
|
+
:on => :head
|
19
|
+
|
20
|
+
option :clc_allowed_protocols,
|
21
|
+
:long => '--allow PROTOCOL:FROM[-TO]',
|
22
|
+
:description => 'Assigns public IP with permissions for specified protocol',
|
23
|
+
:on => :head,
|
24
|
+
:proc => ->(param) do
|
25
|
+
Chef::Config[:knife][:clc_allowed_protocols] ||= []
|
26
|
+
Chef::Config[:knife][:clc_allowed_protocols] << param
|
27
|
+
end
|
28
|
+
|
29
|
+
option :clc_sources,
|
30
|
+
:long => '--source CIDR',
|
31
|
+
:description => 'The source IP address range allowed to access the new public IP address',
|
32
|
+
:on => :head,
|
33
|
+
:proc => ->(param) do
|
34
|
+
Chef::Config[:knife][:clc_sources] ||= []
|
35
|
+
Chef::Config[:knife][:clc_sources] << param
|
36
|
+
end
|
37
|
+
|
38
|
+
option :clc_wait,
|
39
|
+
:long => '--wait',
|
40
|
+
:description => 'Wait for operation completion',
|
41
|
+
:boolean => true,
|
42
|
+
:default => false,
|
43
|
+
:on => :head
|
44
|
+
|
45
|
+
def parse_and_validate_parameters
|
46
|
+
unless config[:clc_server]
|
47
|
+
errors << 'Server ID is required'
|
48
|
+
end
|
49
|
+
|
50
|
+
permissions = config[:clc_allowed_protocols]
|
51
|
+
if permissions && permissions.any?
|
52
|
+
parse_protocol_permissions(permissions)
|
53
|
+
else
|
54
|
+
errors << 'At least one protocol permission is required'
|
55
|
+
end
|
56
|
+
|
57
|
+
sources = config[:clc_sources]
|
58
|
+
if sources && sources.any?
|
59
|
+
parse_sources(sources)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_protocol_permissions(permissions)
|
64
|
+
permissions.map! do |param|
|
65
|
+
protocol, port_range = param.split(':', 2)
|
66
|
+
|
67
|
+
case protocol.downcase
|
68
|
+
when 'ssh', 'sftp' then { 'protocol' => 'tcp', 'port' => 22 }
|
69
|
+
when 'rdp' then { 'protocol' => 'tcp', 'port' => 3389 }
|
70
|
+
when 'icmp' then { 'protocol' => 'icmp' }
|
71
|
+
when 'http' then [{ 'protocol' => 'tcp', 'port' => 80 }, { 'protocol' => 'tcp', 'port' => 8080 }]
|
72
|
+
when 'https' then { 'protocol' => 'tcp', 'port' => 443 }
|
73
|
+
when 'ftp' then { 'protocol' => 'tcp', 'port' => 21 }
|
74
|
+
when 'ftps' then { 'protocol' => 'tcp', 'port' => 990 }
|
75
|
+
when 'udp', 'tcp'
|
76
|
+
unless port_range
|
77
|
+
errors << "No ports specified for #{param}"
|
78
|
+
else
|
79
|
+
ports = port_range.split('-').map do |port_string|
|
80
|
+
Integer(port_string) rescue nil
|
81
|
+
end
|
82
|
+
|
83
|
+
if ports.any?(&:nil?) || ports.size > 2 || ports.size < 1
|
84
|
+
errors << "Malformed port range for #{param}"
|
85
|
+
end
|
86
|
+
|
87
|
+
{
|
88
|
+
'protocol' => protocol.downcase,
|
89
|
+
'port' => ports[0],
|
90
|
+
'portTo' => ports[1]
|
91
|
+
}.keep_if { |_, value| value }
|
92
|
+
end
|
93
|
+
else
|
94
|
+
errors << "Unsupported protocol for #{param}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
permissions.flatten!
|
99
|
+
end
|
100
|
+
|
101
|
+
def parse_sources(sources)
|
102
|
+
sources.map! { |cidr| { 'cidr' => cidr } }
|
103
|
+
end
|
104
|
+
|
105
|
+
def prepare_ip_params
|
106
|
+
{
|
107
|
+
'ports' => config[:clc_allowed_protocols],
|
108
|
+
'sourceRestrictions' => config[:clc_sources],
|
109
|
+
'internalIPAddress' => config[:clc_internal_ip]
|
110
|
+
}.delete_if { |_, value| value.nil? || value.empty? }
|
111
|
+
end
|
112
|
+
|
113
|
+
def execute
|
114
|
+
ui.info 'Requesting public IP...'
|
115
|
+
links = connection.create_ip_address(config[:clc_server], prepare_ip_params)
|
116
|
+
|
117
|
+
if config[:clc_wait]
|
118
|
+
connection.wait_for(links['operation']['id']) { putc '.' }
|
119
|
+
ui.info "\n"
|
120
|
+
ui.info 'Public IP has been assigned'
|
121
|
+
ui.info "You can look at new network configuration with `knife clc server show #{config[:clc_server]} --ports"
|
122
|
+
else
|
123
|
+
ui.info 'IP assignment request has been sent'
|
124
|
+
ui.info "You can check assignment operation status with 'knife clc operation show #{links['operation']['id']}'"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'chef/knife/clc_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class ClcIpDelete < Knife
|
6
|
+
include Knife::ClcBase
|
7
|
+
|
8
|
+
banner 'knife clc ip delete IP (options)'
|
9
|
+
|
10
|
+
option :clc_server,
|
11
|
+
:long => '--server ID',
|
12
|
+
:description => 'ID of the server to assign IP to',
|
13
|
+
:on => :head
|
14
|
+
|
15
|
+
option :clc_wait,
|
16
|
+
:long => '--wait',
|
17
|
+
:description => 'Wait for operation completion',
|
18
|
+
:boolean => true,
|
19
|
+
:default => false,
|
20
|
+
:on => :head
|
21
|
+
|
22
|
+
def parse_and_validate_parameters
|
23
|
+
unless name_args[0]
|
24
|
+
errors << 'IP string is required'
|
25
|
+
end
|
26
|
+
|
27
|
+
unless config[:clc_server]
|
28
|
+
errors << 'Server ID is required'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def execute
|
33
|
+
ui.info 'Requesting IP deletion...'
|
34
|
+
links = connection.delete_ip_address(config[:clc_server], name_args[0])
|
35
|
+
|
36
|
+
if config[:clc_wait]
|
37
|
+
connection.wait_for(links['operation']['id']) { putc '.' }
|
38
|
+
ui.info "\n"
|
39
|
+
ui.info 'IP address has been deleted'
|
40
|
+
ui.info "You can look at new network configuration with `knife clc server show #{config[:clc_server]} --ports"
|
41
|
+
else
|
42
|
+
ui.info 'Deletion request has been sent'
|
43
|
+
ui.info "You can check deletion operation status with 'knife clc operation show #{links['operation']['id']}'"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'chef/knife/clc_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class ClcOperationShow < Knife
|
6
|
+
include Knife::ClcBase
|
7
|
+
|
8
|
+
banner 'knife clc operation show ID (options)'
|
9
|
+
|
10
|
+
option :clc_wait,
|
11
|
+
:long => '--wait',
|
12
|
+
:description => 'Wait for operation completion',
|
13
|
+
:boolean => true,
|
14
|
+
:default => false,
|
15
|
+
:on => :head
|
16
|
+
|
17
|
+
def parse_and_validate_parameters
|
18
|
+
unless name_args[0]
|
19
|
+
errors << 'Operation ID is required'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute
|
24
|
+
operation_id = name_args[0]
|
25
|
+
|
26
|
+
if config[:clc_wait]
|
27
|
+
ui.info 'Waiting for operation completion...'
|
28
|
+
connection.wait_for(operation_id) { putc '.' }
|
29
|
+
ui.info "\n"
|
30
|
+
ui.info 'Operation has been completed'
|
31
|
+
else
|
32
|
+
status = connection.show_operation(operation_id)['status']
|
33
|
+
ui.info "#{ui.color('Status', :bold)}: #{status}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|