knife-cloudstack 0.0.15 → 0.0.16

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.
Files changed (35) hide show
  1. data/CHANGES.rdoc +15 -0
  2. data/README.rdoc +18 -0
  3. data/lib/chef/knife/cs_aag_list.rb +58 -0
  4. data/lib/chef/knife/cs_account_list.rb +32 -75
  5. data/lib/chef/knife/cs_base.rb +2 -5
  6. data/lib/chef/knife/cs_baselist.rb +37 -7
  7. data/lib/chef/knife/cs_cluster_list.rb +18 -51
  8. data/lib/chef/knife/cs_config_list.rb +14 -43
  9. data/lib/chef/knife/cs_disk_list.rb +16 -47
  10. data/lib/chef/knife/cs_domain_list.rb +15 -45
  11. data/lib/chef/knife/cs_firewallrule_list.rb +19 -52
  12. data/lib/chef/knife/cs_host_list.rb +19 -53
  13. data/lib/chef/knife/cs_iso_list.rb +30 -44
  14. data/lib/chef/knife/cs_keypair_list.rb +12 -51
  15. data/lib/chef/knife/cs_network_list.rb +20 -51
  16. data/lib/chef/knife/cs_oscategory_list.rb +12 -40
  17. data/lib/chef/knife/cs_ostype_list.rb +15 -43
  18. data/lib/chef/knife/cs_pod_list.rb +18 -51
  19. data/lib/chef/knife/cs_project_list.rb +16 -45
  20. data/lib/chef/knife/cs_publicip_list.rb +16 -49
  21. data/lib/chef/knife/cs_router_list.rb +16 -46
  22. data/lib/chef/knife/cs_securitygroup_list.rb +14 -89
  23. data/lib/chef/knife/cs_server_add_nic.rb +109 -0
  24. data/lib/chef/knife/cs_server_create.rb +25 -5
  25. data/lib/chef/knife/cs_server_list.rb +35 -62
  26. data/lib/chef/knife/cs_server_remove_nic.rb +101 -0
  27. data/lib/chef/knife/cs_service_list.rb +19 -59
  28. data/lib/chef/knife/cs_stack_create.rb +2 -0
  29. data/lib/chef/knife/cs_template_list.rb +26 -55
  30. data/lib/chef/knife/cs_template_register.rb +4 -4
  31. data/lib/chef/knife/cs_user_list.rb +19 -50
  32. data/lib/chef/knife/cs_volume_list.rb +17 -46
  33. data/lib/chef/knife/cs_zone_list.rb +13 -57
  34. data/lib/knife-cloudstack/connection.rb +60 -9
  35. metadata +5 -3
@@ -16,20 +16,14 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'chef/knife/cs_base'
19
+ require 'chef/knife'
20
20
  require 'chef/knife/cs_baselist'
21
21
 
22
22
  module KnifeCloudstack
23
23
  class CsDomainList < Chef::Knife
24
24
 
25
- include Chef::Knife::KnifeCloudstackBase
26
25
  include Chef::Knife::KnifeCloudstackBaseList
27
-
28
- deps do
29
- require 'knife-cloudstack/connection'
30
- Chef::Knife.load_deps
31
- end
32
-
26
+
33
27
  banner "knife cs domain list (options)"
34
28
 
35
29
  option :listall,
@@ -40,43 +34,19 @@ module KnifeCloudstack
40
34
  def run
41
35
  validate_base_options
42
36
 
43
- if locate_config_value(:fields)
44
- object_list = []
45
- locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
46
- else
47
- object_list = [
48
- ui.color('Name', :bold),
49
- ui.color('ID', :bold),
50
- ui.color('Level', :bold),
51
- ui.color('Path', :bold)
52
- ]
53
- end
54
-
55
- columns = object_list.count
56
- object_list = [] if locate_config_value(:noheader)
57
-
58
- connection_result = connection.list_object(
59
- "listDomains",
60
- "domain",
61
- locate_config_value(:filter),
62
- locate_config_value(:listall)
63
- )
64
-
65
- output_format(connection_result)
66
-
67
- connection_result.each do |r|
68
-
69
- if locate_config_value(:fields)
70
- locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
71
- else
72
- object_list << r['name'].to_s
73
- object_list << r['id'].to_s
74
- object_list << r['level'].to_s
75
- object_list << r['path'].to_s
76
- end
77
- end
78
- puts ui.list(object_list, :uneven_columns_across, columns)
79
- list_object_fields(connection_result) if locate_config_value(:fieldlist)
37
+ columns = [
38
+ 'Name :name',
39
+ 'ID :id',
40
+ 'Level :level',
41
+ 'Path :path'
42
+ ]
43
+
44
+ params = { 'command' => "listDomains" }
45
+ params['filter'] = locate_config_value(:filter) if locate_config_value(:filter)
46
+ params['listall'] = locate_config_value(:listall) if locate_config_value(:listall)
47
+
48
+ result = connection.list_object(params, "domain")
49
+ list_object(columns, result)
80
50
  end
81
51
 
82
52
  end
@@ -16,20 +16,14 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'chef/knife/cs_base'
19
+ require 'chef/knife'
20
20
  require 'chef/knife/cs_baselist'
21
21
 
22
22
  module KnifeCloudstack
23
23
  class CsFirewallruleList < Chef::Knife
24
24
 
25
- include Chef::Knife::KnifeCloudstackBase
26
25
  include Chef::Knife::KnifeCloudstackBaseList
27
26
 
28
- deps do
29
- require 'knife-cloudstack/connection'
30
- Chef::Knife.load_deps
31
- end
32
-
33
27
  banner "knife cs firewallrule list (options)"
34
28
 
35
29
  option :listall,
@@ -44,51 +38,24 @@ module KnifeCloudstack
44
38
  def run
45
39
  validate_base_options
46
40
 
47
- if locate_config_value(:fields)
48
- object_list = []
49
- locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
50
- else
51
- object_list = [
52
- ui.color('ID', :bold),
53
- ui.color('Protocol', :bold),
54
- ui.color('Start Port', :bold),
55
- ui.color('End Port', :bold),
56
- ui.color('IP AddressID', :bold),
57
- ui.color('IP Address', :bold),
58
- ui.color('State', :bold),
59
- ui.color('CIDR List', :bold)
60
- ]
61
- end
62
-
63
- columns = object_list.count
64
- object_list = [] if locate_config_value(:noheader)
65
-
66
- connection_result = connection.list_object(
67
- "listFirewallRules",
68
- "firewallrule",
69
- locate_config_value(:filter),
70
- locate_config_value(:listall),
71
- locate_config_value(:keyword)
72
- )
73
-
74
- output_format(connection_result)
75
-
76
- connection_result.each do |r|
77
- if locate_config_value(:fields)
78
- locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
79
- else
80
- object_list << r['id'].to_s
81
- object_list << r['protocol'].to_s
82
- object_list << r['startport'].to_s
83
- object_list << r['endport'].to_s
84
- object_list << r['ipaddressid'].to_s
85
- object_list << r['ipaddress'].to_s
86
- object_list << r['state'].to_s
87
- object_list << r['cidrlist'].to_s
88
- end
89
- end
90
- puts ui.list(object_list, :uneven_columns_across, columns)
91
- list_object_fields(connection_result) if locate_config_value(:fieldlist)
41
+ columns = [
42
+ 'ID :id',
43
+ 'Protocol :protocol',
44
+ 'Start Port :startport',
45
+ 'End Port :endport',
46
+ 'IP AddressID :ipaddressid',
47
+ 'IP Address :ipaddress',
48
+ 'State :state',
49
+ 'CIDR List :cidrlist'
50
+ ]
51
+
52
+ params = { 'command' => "listFirewallRules" }
53
+ params['filter'] = locate_config_value(:filter) if locate_config_value(:filter)
54
+ params['listall'] = locate_config_value(:listall) if locate_config_value(:listall)
55
+ params['keyword'] = locate_config_value(:keyword) if locate_config_value(:keyword)
56
+
57
+ result = connection.list_object(params, "firewallrule")
58
+ list_object(columns, result)
92
59
  end
93
60
 
94
61
  end
@@ -16,20 +16,14 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'chef/knife/cs_base'
19
+ require 'chef/knife'
20
20
  require 'chef/knife/cs_baselist'
21
21
 
22
22
  module KnifeCloudstack
23
23
  class CsHostList < Chef::Knife
24
24
 
25
- include Chef::Knife::KnifeCloudstackBase
26
25
  include Chef::Knife::KnifeCloudstackBaseList
27
26
 
28
- deps do
29
- require 'knife-cloudstack/connection'
30
- Chef::Knife.load_deps
31
- end
32
-
33
27
  banner "knife cs host list (options)"
34
28
 
35
29
  option :name,
@@ -43,52 +37,24 @@ module KnifeCloudstack
43
37
  def run
44
38
  validate_base_options
45
39
 
46
- if locate_config_value(:fields)
47
- object_list = []
48
- locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
49
- else
50
- object_list = [
51
- ui.color('Name', :bold),
52
- ui.color('Address', :bold),
53
- ui.color('State', :bold),
54
- ui.color('Type', :bold),
55
- ui.color('Cluster', :bold),
56
- ui.color('Pod', :bold),
57
- ui.color('Zone', :bold),
58
- ui.color('Resource', :bold)
59
- ]
60
- end
61
-
62
- columns = object_list.count
63
- object_list = [] if locate_config_value(:noheader)
64
-
65
- connection_result = connection.list_object(
66
- "listHosts",
67
- "host",
68
- locate_config_value(:filter),
69
- false,
70
- locate_config_value(:keyword),
71
- locate_config_value(:name)
72
- )
73
-
74
- output_format(connection_result)
75
-
76
- connection_result.each do |r|
77
- if locate_config_value(:fields)
78
- locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
79
- else
80
- object_list << r['name'].to_s
81
- object_list << r['ipaddress'].to_s
82
- object_list << r['state'].to_s
83
- object_list << r['type'].to_s
84
- object_list << r['clustername'].to_s
85
- object_list << r['podname'].to_s
86
- object_list << r['zonename'].to_s
87
- object_list << r['resourcestate'].to_s
88
- end
89
- end
90
- puts ui.list(object_list, :uneven_columns_across, columns)
91
- list_object_fields(connection_result) if locate_config_value(:fieldlist)
40
+ columns = [
41
+ 'Name :name',
42
+ 'Address :ipaddress',
43
+ 'State :state',
44
+ 'Type :type',
45
+ 'Cluster :clustername',
46
+ 'Pod :podname',
47
+ 'Zone :zonename',
48
+ 'Resource :resourcestate'
49
+ ]
50
+
51
+ params = { 'command' => "listHosts" }
52
+ params['filter'] = locate_config_value(:filter) if locate_config_value(:filter)
53
+ params['keyword'] = locate_config_value(:keyword) if locate_config_value(:keyword)
54
+ params['name'] = locate_config_value(:name) if locate_config_value(:name)
55
+
56
+ result = connection.list_object(params, "host")
57
+ list_object(columns, result)
92
58
  end
93
59
 
94
60
  end
@@ -16,20 +16,14 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'chef/knife/cs_base'
19
+ require 'chef/knife'
20
20
  require 'chef/knife/cs_baselist'
21
21
 
22
22
  module KnifeCloudstack
23
23
  class CsIsoList < Chef::Knife
24
24
 
25
- include Chef::Knife::KnifeCloudstackBase
26
25
  include Chef::Knife::KnifeCloudstackBaseList
27
26
 
28
- deps do
29
- require 'knife-cloudstack/connection'
30
- Chef::Knife.load_deps
31
- end
32
-
33
27
  banner "knife cs iso list (options)"
34
28
 
35
29
  option :listall,
@@ -45,49 +39,41 @@ module KnifeCloudstack
45
39
  :long => "--keyword KEY",
46
40
  :description => "List by keyword"
47
41
 
42
+ option :isofilter,
43
+ :long => "--isofilter FILTER",
44
+ :description => "Default: 'featured'. Options: 'self','selfexecutable','sharedexecutable','executable','community'",
45
+ :default => "featured"
46
+
48
47
  def run
49
48
  validate_base_options
50
49
 
51
- object_list = []
52
- if locate_config_value(:fields)
53
- locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
50
+ columns = [
51
+ 'Name :name',
52
+ 'Account :account',
53
+ 'Domain :domain',
54
+ 'Public :ispublic',
55
+ 'Size :size',
56
+ 'OS :ostypename'
57
+ ]
58
+
59
+ params = { 'command' => "listIsos" }
60
+ params['filter'] = locate_config_value(:filter) if locate_config_value(:filter)
61
+ params['keyword'] = locate_config_value(:keyword) if locate_config_value(:keyword)
62
+ params['listall'] = locate_config_value(:listall) if locate_config_value(:listall)
63
+ params['name'] = locate_config_value(:name) if locate_config_value(:name)
64
+
65
+ if ['all','featured','self','selfexecutable','sharedexecutable','executable','community'].include?(locate_config_value(:templatefilter))
66
+ params['isofilter'] = locate_config_value(:isofilter)
54
67
  else
55
- object_list << ui.color('Name', :bold)
56
- object_list << ui.color('Account', :bold) unless locate_config_value(:cloudstack_project)
57
- object_list << ui.color('Domain', :bold)
58
- object_list << ui.color('Public', :bold)
59
- object_list << ui.color('Size', :bold)
60
- object_list << ui.color('OS', :bold)
68
+ params['isofilter'] = 'featured'
61
69
  end
62
-
63
- columns = object_list.count
64
- object_list = [] if locate_config_value(:noheader)
65
-
66
- connection_result = connection.list_object(
67
- "listIsos",
68
- "iso",
69
- locate_config_value(:filter),
70
- locate_config_value(:listall),
71
- locate_config_value(:keyword),
72
- locate_config_value(:name)
73
- )
74
-
75
- output_format(connection_result)
76
-
77
- connection_result.each do |r|
78
- if locate_config_value(:fields)
79
- locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
80
- else
81
- object_list << r['name'].to_s
82
- object_list << (r['account'] ? r['account'].to_s : 'N/A') unless locate_config_value(:cloudstack_project)
83
- object_list << r['domain'].to_s
84
- object_list << r['ispublic'].to_s
85
- object_list << (r['size'] ? human_file_size(r['size']) : 'Unknown')
86
- object_list << (r['ostypename'] ? r['ostypename'].to_s : 'N/A')
87
- end
70
+
71
+ result = connection.list_object(params, "iso")
72
+ result.each do |r|
73
+ r['size'] = human_file_size(r['size']) if r['size']
88
74
  end
89
- puts ui.list(object_list, :uneven_columns_across, columns)
90
- connection.show_object_fields(connection_result) if locate_config_value(:fieldlist)
75
+
76
+ list_object(columns, result)
91
77
  end
92
78
 
93
79
  def human_file_size n
@@ -1,3 +1,4 @@
1
+ #
1
2
  # Copyright:: Copyright (c) 2011 Edmunds, Inc.
2
3
  # Copyright:: Copyright (c) 2013 Sander Botman.
3
4
  # License:: Apache License, Version 2.0
@@ -15,68 +16,28 @@
15
16
  # limitations under the License.
16
17
  #
17
18
 
18
- require 'chef/knife/cs_base'
19
+ require 'chef/knife'
19
20
  require 'chef/knife/cs_baselist'
20
21
 
21
22
  module KnifeCloudstack
22
23
  class CsKeypairList < Chef::Knife
23
24
 
24
- include Chef::Knife::KnifeCloudstackBase
25
25
  include Chef::Knife::KnifeCloudstackBaseList
26
26
 
27
- deps do
28
- require 'chef/knife'
29
- require 'knife-cloudstack/connection'
30
- Chef::Knife.load_deps
31
- end
32
-
33
27
  banner "knife cs keypair list (options)"
34
28
 
35
29
  def run
36
30
  validate_base_options
37
-
38
- object_list = []
39
- object_list << ui.color('Index', :bold) if locate_config_value(:index)
40
-
41
- if locate_config_value(:fields)
42
- locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
43
- else
44
- [
45
- ui.color('Name', :bold),
46
- ui.color('Fingerprint', :bold),
47
- ].each { |field| object_list << field }
48
- end
49
-
50
- columns = object_list.count
51
- object_list = [] if locate_config_value(:noheader)
52
-
53
- connection_result = connection.list_object(
54
- "listSSHKeyPairs",
55
- "sshkeypair",
56
- locate_config_value(:filter),
57
- false,
58
- locate_config_value(:keyword),
59
- locate_config_value(:name)
60
- )
61
-
62
- output_format(connection_result)
63
-
64
- index_num = 0
65
- connection_result.each do |r|
66
- if locate_config_value(:index)
67
- index_num += 1
68
- object_list << index_num.to_s
69
- end
70
-
71
- if locate_config_value(:fields)
72
- locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
73
- else
74
- object_list << r['name'].to_s
75
- object_list << r['fingerprint'].to_s
76
- end
77
- end
78
- puts ui.list(object_list, :uneven_columns_across, columns)
79
- list_object_fields(connection_result) if locate_config_value(:fieldlist)
31
+
32
+ columns = [
33
+ 'Name :name',
34
+ 'Fingerprint :fingerprint'
35
+ ]
36
+
37
+ params = { 'command' => "listSSHKeyPairs" }
38
+
39
+ result = connection.list_object(params, "sshkeypair")
40
+ list_object(columns, result)
80
41
  end
81
42
 
82
43
  end
@@ -17,21 +17,15 @@
17
17
  # See the License for the specific language governing permissions and
18
18
  # limitations under the License.
19
19
  #
20
- require 'chef/knife/cs_base'
20
+
21
+ require 'chef/knife'
21
22
  require 'chef/knife/cs_baselist'
22
23
 
23
24
  module KnifeCloudstack
24
25
  class CsNetworkList < Chef::Knife
25
26
 
26
- include Chef::Knife::KnifeCloudstackBase
27
27
  include Chef::Knife::KnifeCloudstackBaseList
28
28
 
29
- deps do
30
- require 'knife-cloudstack/connection'
31
- require 'chef/knife'
32
- Chef::Knife.load_deps
33
- end
34
-
35
29
  banner "knife cs network list (options)"
36
30
 
37
31
  option :listall,
@@ -46,49 +40,24 @@ module KnifeCloudstack
46
40
  def run
47
41
  validate_base_options
48
42
 
49
- object_list = []
50
- if locate_config_value(:fields)
51
- locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
52
- else
53
- object_list << ui.color('Name', :bold)
54
- object_list << ui.color('Type', :bold)
55
- object_list << ui.color('Default', :bold)
56
- object_list << ui.color('Shared', :bold)
57
- object_list << ui.color('Gateway', :bold)
58
- object_list << ui.color('Netmask', :bold)
59
- object_list << ui.color('Account', :bold) unless locate_config_value(:cloudstack_project)
60
- object_list << ui.color('Domain', :bold)
61
- end
62
-
63
- columns = object_list.count
64
- object_list = [] if locate_config_value(:noheader)
65
-
66
- connection_result = connection.list_object(
67
- "listNetworks",
68
- "network",
69
- locate_config_value(:filter),
70
- locate_config_value(:listall),
71
- locate_config_value(:keyword)
72
- )
73
-
74
- output_format(connection_result)
75
-
76
- connection_result.each do |r|
77
- if locate_config_value(:fields)
78
- locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
79
- else
80
- object_list << r['name'].to_s
81
- object_list << r['type'].to_s
82
- object_list << (r['isdefault'] ? r['isdefault'].to_s : 'false')
83
- object_list << (r['isshared'] ? r['isshared'].to_s : 'false')
84
- object_list << (r['gateway'] || '')
85
- object_list << (r['netmask'] || '')
86
- object_list << (r['account'] || '') unless locate_config_value(:cloudstack_project)
87
- object_list << (r['domain'] || '')
88
- end
89
- end
90
- puts ui.list(object_list, :uneven_columns_across, columns)
91
- list_object_fields(connection_result) if locate_config_value(:fieldlist)
43
+ columns = [
44
+ 'Name :name',
45
+ 'Type :type',
46
+ 'Default :default',
47
+ 'Shared :shared',
48
+ 'Gateway :gateway',
49
+ 'Netmask :netmask',
50
+ 'Account :account',
51
+ 'Domain :domain'
52
+ ]
53
+
54
+ params = { 'command' => "listNetworks" }
55
+ params['filter'] = locate_config_value(:filter) if locate_config_value(:filter)
56
+ params['listall'] = locate_config_value(:listall) if locate_config_value(:listall)
57
+ params['keyword'] = locate_config_value(:keyword) if locate_config_value(:keyword)
58
+
59
+ result = connection.list_object(params, "network")
60
+ list_object(columns, result)
92
61
  end
93
62
  end
94
63
  end
@@ -16,20 +16,14 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'chef/knife/cs_base'
19
+ require 'chef/knife'
20
20
  require 'chef/knife/cs_baselist'
21
21
 
22
22
  module KnifeCloudstack
23
23
  class CsOscategoryList < Chef::Knife
24
24
 
25
- include Chef::Knife::KnifeCloudstackBase
26
25
  include Chef::Knife::KnifeCloudstackBaseList
27
26
 
28
- deps do
29
- require 'knife-cloudstack/connection'
30
- Chef::Knife.load_deps
31
- end
32
-
33
27
  banner "knife cs oscategory list (options)"
34
28
 
35
29
  option :keyword,
@@ -39,39 +33,17 @@ module KnifeCloudstack
39
33
  def run
40
34
  validate_base_options
41
35
 
42
- if locate_config_value(:fields)
43
- object_list = []
44
- locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
45
- else
46
- object_list = [
47
- ui.color('Name', :bold),
48
- ui.color('ID', :bold),
49
- ]
50
- end
51
-
52
- columns = object_list.count
53
- object_list = [] if locate_config_value(:noheader)
54
-
55
- connection_result = connection.list_object(
56
- "listOsCategories",
57
- "oscategory",
58
- locate_config_value(:filter),
59
- false,
60
- locate_config_value(:keyword)
61
- )
62
-
63
- output_format(connection_result)
64
-
65
- connection_result.each do |r|
66
- if locate_config_value(:fields)
67
- locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
68
- else
69
- object_list << r['name'].to_s
70
- object_list << r['id'].to_s
71
- end
72
- end
73
- puts ui.list(object_list, :uneven_columns_across, columns)
74
- list_object_fields(connection_result) if locate_config_value(:fieldlist)
36
+ columns = [
37
+ 'Name :name',
38
+ 'ID :id'
39
+ ]
40
+
41
+ params = { 'command' => "listOsCategories" }
42
+ params['filter'] = locate_config_value(:filter) if locate_config_value(:filter)
43
+ params['keyword'] = locate_config_value(:keyword) if locate_config_value(:keyword)
44
+
45
+ result = connection.list_object(params, "oscategory")
46
+ list_object(columns, result)
75
47
  end
76
48
 
77
49
  end