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.
@@ -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