fog-openstack 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -28
  3. data/docs/workflow.md +129 -0
  4. data/examples/workflow/workflow-examples.rb +387 -0
  5. data/gemfiles/Gemfile-1.9 +1 -0
  6. data/lib/fog/openstack.rb +5 -1
  7. data/lib/fog/openstack/baremetal.rb +14 -38
  8. data/lib/fog/openstack/compute.rb +4 -33
  9. data/lib/fog/openstack/core.rb +104 -50
  10. data/lib/fog/openstack/identity.rb +2 -1
  11. data/lib/fog/openstack/identity_v2.rb +0 -2
  12. data/lib/fog/openstack/identity_v3.rb +0 -2
  13. data/lib/fog/openstack/image_v1.rb +6 -6
  14. data/lib/fog/openstack/image_v2.rb +7 -6
  15. data/lib/fog/openstack/introspection.rb +10 -35
  16. data/lib/fog/openstack/metering.rb +16 -35
  17. data/lib/fog/openstack/monitoring.rb +5 -31
  18. data/lib/fog/openstack/network.rb +5 -34
  19. data/lib/fog/openstack/nfv.rb +10 -35
  20. data/lib/fog/openstack/orchestration.rb +5 -40
  21. data/lib/fog/openstack/planning.rb +11 -36
  22. data/lib/fog/openstack/requests/compute/get_limits.rb +8 -7
  23. data/lib/fog/openstack/requests/network/update_router.rb +1 -1
  24. data/lib/fog/openstack/requests/workflow_v2/create_action.rb +28 -0
  25. data/lib/fog/openstack/requests/workflow_v2/create_action_execution.rb +31 -0
  26. data/lib/fog/openstack/requests/workflow_v2/create_cron_trigger.rb +55 -0
  27. data/lib/fog/openstack/requests/workflow_v2/create_environment.rb +31 -0
  28. data/lib/fog/openstack/requests/workflow_v2/create_execution.rb +30 -0
  29. data/lib/fog/openstack/requests/workflow_v2/create_workbook.rb +28 -0
  30. data/lib/fog/openstack/requests/workflow_v2/create_workflow.rb +28 -0
  31. data/lib/fog/openstack/requests/workflow_v2/delete_action.rb +25 -0
  32. data/lib/fog/openstack/requests/workflow_v2/delete_action_execution.rb +25 -0
  33. data/lib/fog/openstack/requests/workflow_v2/delete_cron_trigger.rb +25 -0
  34. data/lib/fog/openstack/requests/workflow_v2/delete_environment.rb +25 -0
  35. data/lib/fog/openstack/requests/workflow_v2/delete_execution.rb +25 -0
  36. data/lib/fog/openstack/requests/workflow_v2/delete_workbook.rb +25 -0
  37. data/lib/fog/openstack/requests/workflow_v2/delete_workflow.rb +25 -0
  38. data/lib/fog/openstack/requests/workflow_v2/get_action.rb +27 -0
  39. data/lib/fog/openstack/requests/workflow_v2/get_action_execution.rb +26 -0
  40. data/lib/fog/openstack/requests/workflow_v2/get_cron_trigger.rb +28 -0
  41. data/lib/fog/openstack/requests/workflow_v2/get_environment.rb +28 -0
  42. data/lib/fog/openstack/requests/workflow_v2/get_execution.rb +26 -0
  43. data/lib/fog/openstack/requests/workflow_v2/get_task.rb +27 -0
  44. data/lib/fog/openstack/requests/workflow_v2/get_workbook.rb +28 -0
  45. data/lib/fog/openstack/requests/workflow_v2/get_workflow.rb +28 -0
  46. data/lib/fog/openstack/requests/workflow_v2/list_action_executions.rb +28 -0
  47. data/lib/fog/openstack/requests/workflow_v2/list_actions.rb +30 -0
  48. data/lib/fog/openstack/requests/workflow_v2/list_cron_triggers.rb +28 -0
  49. data/lib/fog/openstack/requests/workflow_v2/list_environments.rb +28 -0
  50. data/lib/fog/openstack/requests/workflow_v2/list_executions.rb +28 -0
  51. data/lib/fog/openstack/requests/workflow_v2/list_services.rb +28 -0
  52. data/lib/fog/openstack/requests/workflow_v2/list_tasks.rb +28 -0
  53. data/lib/fog/openstack/requests/workflow_v2/list_workbooks.rb +28 -0
  54. data/lib/fog/openstack/requests/workflow_v2/list_workflows.rb +30 -0
  55. data/lib/fog/openstack/requests/workflow_v2/rerun_task.rb +33 -0
  56. data/lib/fog/openstack/requests/workflow_v2/update_action.rb +28 -0
  57. data/lib/fog/openstack/requests/workflow_v2/update_action_execution.rb +33 -0
  58. data/lib/fog/openstack/requests/workflow_v2/update_environment.rb +31 -0
  59. data/lib/fog/openstack/requests/workflow_v2/update_execution.rb +33 -0
  60. data/lib/fog/openstack/requests/workflow_v2/update_workbook.rb +28 -0
  61. data/lib/fog/openstack/requests/workflow_v2/update_workflow.rb +28 -0
  62. data/lib/fog/openstack/requests/workflow_v2/validate_action.rb +28 -0
  63. data/lib/fog/openstack/requests/workflow_v2/validate_workbook.rb +28 -0
  64. data/lib/fog/openstack/requests/workflow_v2/validate_workflow.rb +28 -0
  65. data/lib/fog/openstack/storage.rb +4 -35
  66. data/lib/fog/openstack/version.rb +1 -1
  67. data/lib/fog/openstack/volume_v1.rb +7 -5
  68. data/lib/fog/openstack/volume_v2.rb +7 -5
  69. data/lib/fog/openstack/workflow.rb +12 -0
  70. data/lib/fog/openstack/workflow_v2.rb +160 -0
  71. metadata +48 -95
  72. data/lib/fog/openstack/common.rb +0 -44
  73. data/tests/openstack/models/compute/images_tests.rb +0 -13
  74. data/tests/openstack/models/compute/security_group_tests.rb +0 -54
  75. data/tests/openstack/models/compute/server_tests.rb +0 -220
  76. data/tests/openstack/models/compute/service_tests.rb +0 -17
  77. data/tests/openstack/models/image/image_tests.rb +0 -36
  78. data/tests/openstack/models/image/images_tests.rb +0 -19
  79. data/tests/openstack/models/network/floating_ip_tests.rb +0 -35
  80. data/tests/openstack/models/network/floating_ips_tests.rb +0 -19
  81. data/tests/openstack/models/network/ike_policies_tests.rb +0 -28
  82. data/tests/openstack/models/network/ike_policy_tests.rb +0 -36
  83. data/tests/openstack/models/network/ipsec_policies_tests.rb +0 -26
  84. data/tests/openstack/models/network/ipsec_policy_tests.rb +0 -36
  85. data/tests/openstack/models/network/ipsec_site_connection_tests.rb +0 -50
  86. data/tests/openstack/models/network/ipsec_site_connections_tests.rb +0 -32
  87. data/tests/openstack/models/network/lb_health_monitor_tests.rb +0 -52
  88. data/tests/openstack/models/network/lb_health_monitors_tests.rb +0 -21
  89. data/tests/openstack/models/network/lb_member_tests.rb +0 -28
  90. data/tests/openstack/models/network/lb_members_tests.rb +0 -21
  91. data/tests/openstack/models/network/lb_pool_tests.rb +0 -53
  92. data/tests/openstack/models/network/lb_pools_tests.rb +0 -20
  93. data/tests/openstack/models/network/lb_vip_tests.rb +0 -39
  94. data/tests/openstack/models/network/lb_vips_tests.rb +0 -21
  95. data/tests/openstack/models/network/network_tests.rb +0 -61
  96. data/tests/openstack/models/network/networks_tests.rb +0 -21
  97. data/tests/openstack/models/network/port_tests.rb +0 -28
  98. data/tests/openstack/models/network/ports_tests.rb +0 -25
  99. data/tests/openstack/models/network/router_tests.rb +0 -39
  100. data/tests/openstack/models/network/routers_tests.rb +0 -21
  101. data/tests/openstack/models/network/security_group_rule_tests.rb +0 -27
  102. data/tests/openstack/models/network/security_group_rules_tests.rb +0 -25
  103. data/tests/openstack/models/network/security_group_tests.rb +0 -17
  104. data/tests/openstack/models/network/security_groups_tests.rb +0 -16
  105. data/tests/openstack/models/network/subnet_tests.rb +0 -30
  106. data/tests/openstack/models/network/subnets_tests.rb +0 -27
  107. data/tests/openstack/models/network/vpn_service_tests.rb +0 -27
  108. data/tests/openstack/models/network/vpn_services_tests.rb +0 -22
  109. data/tests/openstack/models/nfv/vnf_tests.rb +0 -35
  110. data/tests/openstack/models/nfv/vnfd_tests.rb +0 -23
  111. data/tests/openstack/models/nfv/vnfds_tests.rb +0 -31
  112. data/tests/openstack/models/nfv/vnfs_tests.rb +0 -38
  113. data/tests/openstack/models/planning/.gitkeep +0 -0
  114. data/tests/openstack/models/planning/plan_tests.rb +0 -51
  115. data/tests/openstack/models/planning/plans_tests.rb +0 -18
  116. data/tests/openstack/models/planning/role_tests.rb +0 -14
  117. data/tests/openstack/models/storage/file_tests.rb +0 -251
  118. data/tests/openstack/requests/compute/address_tests.rb +0 -58
  119. data/tests/openstack/requests/compute/aggregate_tests.rb +0 -59
  120. data/tests/openstack/requests/compute/availability_zone_tests.rb +0 -18
  121. data/tests/openstack/requests/compute/flavor_tests.rb +0 -95
  122. data/tests/openstack/requests/compute/helper.rb +0 -41
  123. data/tests/openstack/requests/compute/image_tests.rb +0 -58
  124. data/tests/openstack/requests/compute/keypair_tests.rb +0 -30
  125. data/tests/openstack/requests/compute/limit_tests.rb +0 -59
  126. data/tests/openstack/requests/compute/quota_tests.rb +0 -60
  127. data/tests/openstack/requests/compute/security_group_tests.rb +0 -61
  128. data/tests/openstack/requests/compute/server_tests.rb +0 -289
  129. data/tests/openstack/requests/compute/service_tests.rb +0 -33
  130. data/tests/openstack/requests/compute/tenant_tests.rb +0 -19
  131. data/tests/openstack/requests/compute/volume_tests.rb +0 -38
  132. data/tests/openstack/requests/image/image_tests.rb +0 -135
  133. data/tests/openstack/requests/metering/event_tests.rb +0 -17
  134. data/tests/openstack/requests/metering/meter_tests.rb +0 -52
  135. data/tests/openstack/requests/metering/resource_tests.rb +0 -19
  136. data/tests/openstack/requests/network/ike_policy_tests.rb +0 -65
  137. data/tests/openstack/requests/network/ipsec_policy_tests.rb +0 -65
  138. data/tests/openstack/requests/network/ipsec_site_connection_tests.rb +0 -82
  139. data/tests/openstack/requests/network/lb_health_monitor_tests.rb +0 -93
  140. data/tests/openstack/requests/network/lb_member_tests.rb +0 -60
  141. data/tests/openstack/requests/network/lb_pool_tests.rb +0 -80
  142. data/tests/openstack/requests/network/lb_vip_tests.rb +0 -71
  143. data/tests/openstack/requests/network/network_tests.rb +0 -107
  144. data/tests/openstack/requests/network/port_tests.rb +0 -66
  145. data/tests/openstack/requests/network/quota_tests.rb +0 -65
  146. data/tests/openstack/requests/network/router_tests.rb +0 -73
  147. data/tests/openstack/requests/network/security_group_rule_tests.rb +0 -58
  148. data/tests/openstack/requests/network/security_group_tests.rb +0 -43
  149. data/tests/openstack/requests/network/subnet_tests.rb +0 -66
  150. data/tests/openstack/requests/network/vpn_service_tests.rb +0 -61
  151. data/tests/openstack/requests/nfv/vnf_tests.rb +0 -70
  152. data/tests/openstack/requests/nfv/vnfd_tests.rb +0 -44
  153. data/tests/openstack/requests/orchestration/stack_tests.rb +0 -64
  154. data/tests/openstack/requests/planning/.gitkeep +0 -0
  155. data/tests/openstack/requests/planning/plan_tests.rb +0 -65
  156. data/tests/openstack/requests/planning/role_tests.rb +0 -16
  157. data/tests/openstack/requests/storage/container_tests.rb +0 -64
  158. data/tests/openstack/requests/storage/large_object_tests.rb +0 -372
  159. data/tests/openstack/requests/storage/object_tests.rb +0 -208
  160. data/tests/openstack/requests/volume/availability_zone_tests.rb +0 -13
  161. data/tests/openstack/requests/volume/quota_tests.rb +0 -50
  162. data/tests/openstack/requests/volume/volume_type_tests.rb +0 -32
@@ -1,20 +1,21 @@
1
1
  module Fog
2
2
  module Compute
3
3
  class OpenStack
4
- # http://docs.openstack.org/api/openstack-compute/2/content/ProgramaticLimits.html
5
- #
4
+ # http://developer.openstack.org/api-ref-compute-v2.1.html#showlimits
5
+
6
6
  class Real
7
- def get_limits
7
+ def get_limits(options = {})
8
8
  request(
9
- :expects => 200,
10
- :method => 'GET',
11
- :path => '/limits.json'
9
+ :expects => 200,
10
+ :method => 'GET',
11
+ :path => '/limits',
12
+ :query => options
12
13
  )
13
14
  end
14
15
  end
15
16
 
16
17
  class Mock
17
- def get_limits
18
+ def get_limits(_options = {})
18
19
  rate_limits = [
19
20
  { 'regex' => '.*',
20
21
  'limit' => [
@@ -22,7 +22,7 @@ module Fog
22
22
  Fog::Logger.deprecation "Passing a model objects into options[:external_gateway_info] is deprecated. \
23
23
  Please pass external external gateway as follows options[:external_gateway_info] = { :network_id => NETWORK_ID }]"
24
24
  data['router'][:external_gateway_info] = { :network_id => egi.id }
25
- elsif egi.is_a?(Hash) and egi[:network_id]
25
+ elsif egi.is_a?(Hash)
26
26
  data['router'][:external_gateway_info] = egi
27
27
  else
28
28
  raise ArgumentError.new('Invalid external_gateway_info attribute')
@@ -0,0 +1,28 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def create_action(definition)
7
+ body = Fog::JSON.encode(definition)
8
+ request(
9
+ :body => body,
10
+ :expects => 201,
11
+ :method => "POST",
12
+ :path => "actions"
13
+ )
14
+ end
15
+ end
16
+
17
+ class Mock
18
+ def create_action(_definition)
19
+ response = Excon::Response.new
20
+ response.status = 201
21
+ response.body = ""
22
+ response
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def create_action_execution(action, input = {}, params = {})
7
+ data = { :name => action }
8
+ data[:input] = Fog::JSON.encode(input) unless input.empty?
9
+ data[:params] = Fog::JSON.encode(params) unless params.empty?
10
+ body = Fog::JSON.encode(data)
11
+ request(
12
+ :body => body,
13
+ :expects => 201,
14
+ :method => "POST",
15
+ :path => "action_executions"
16
+ )
17
+ end
18
+ end
19
+
20
+ class Mock
21
+ def create_action_execution(_action, _input = {}, _params = {})
22
+ response = Excon::Response.new
23
+ response.status = 201
24
+ response.body = ""
25
+ response
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,55 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def create_cron_trigger(name,
7
+ workflow_identifier,
8
+ workflow_input = nil,
9
+ workflow_params = nil,
10
+ pattern = "* * * * *",
11
+ first_time = nil,
12
+ count = nil)
13
+ data = {
14
+ :name => name,
15
+ :pattern => pattern,
16
+ :first_execution_time => first_time,
17
+ :remaining_executions => count
18
+ }
19
+ if workflow_identifier
20
+ data[:workflow_id] = workflow_identifier
21
+ end
22
+ if workflow_input
23
+ data[:workflow_input] = Fog::JSON.encode(workflow_input)
24
+ end
25
+ if workflow_params
26
+ data[:workflow_params] = Fog::JSON.encode(workflow_params)
27
+ end
28
+ body = Fog::JSON.encode(data)
29
+ request(
30
+ :body => body,
31
+ :expects => 201,
32
+ :method => "POST",
33
+ :path => "cron_triggers"
34
+ )
35
+ end
36
+ end
37
+
38
+ class Mock
39
+ def create_cron_trigger(_name,
40
+ _workflow_identifier,
41
+ _workflow_input = nil,
42
+ _workflow_params = nil,
43
+ _pattern = nil,
44
+ _first_time = nil,
45
+ _count = nil)
46
+ response = Excon::Response.new
47
+ response.status = 201
48
+ response.body = ""
49
+ response
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,31 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def create_environment(definition)
7
+ unless definition["variables"].nil?
8
+ definition["variables"] = Fog::JSON.encode(definition["variables"])
9
+ end
10
+ body = Fog::JSON.encode(definition)
11
+ request(
12
+ :body => body,
13
+ :expects => 201,
14
+ :method => "POST",
15
+ :path => "environments"
16
+ )
17
+ end
18
+ end
19
+
20
+ class Mock
21
+ def create_environment(_definition)
22
+ response = Excon::Response.new
23
+ response.status = 201
24
+ response.body = ""
25
+ response
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def create_execution(workflow, input = {})
7
+ data = { :workflow_name => workflow }
8
+ data[:input] = Fog::JSON.encode(input) unless input.empty?
9
+ body = Fog::JSON.encode(data)
10
+ request(
11
+ :body => body,
12
+ :expects => 201,
13
+ :method => "POST",
14
+ :path => "executions"
15
+ )
16
+ end
17
+ end
18
+
19
+ class Mock
20
+ def create_execution(_workflow, _input = {})
21
+ response = Excon::Response.new
22
+ response.status = 201
23
+ response.body = ""
24
+ response
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def create_workbook(definition)
7
+ body = Fog::JSON.encode(definition)
8
+ request(
9
+ :body => body,
10
+ :expects => 201,
11
+ :method => "POST",
12
+ :path => "workbooks"
13
+ )
14
+ end
15
+ end
16
+
17
+ class Mock
18
+ def create_workbook(_definition)
19
+ response = Excon::Response.new
20
+ response.status = 201
21
+ response.body = ""
22
+ response
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def create_workflow(definition)
7
+ body = Fog::JSON.encode(definition)
8
+ request(
9
+ :body => body,
10
+ :expects => 201,
11
+ :method => "POST",
12
+ :path => "workflows"
13
+ )
14
+ end
15
+ end
16
+
17
+ class Mock
18
+ def create_workflow(_definition)
19
+ response = Excon::Response.new
20
+ response.status = 201
21
+ response.body = ""
22
+ response
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def delete_action(name)
7
+ request(
8
+ :expects => 204,
9
+ :method => "DELETE",
10
+ :path => "actions/#{URI.encode(name)}"
11
+ )
12
+ end
13
+ end
14
+
15
+ class Mock
16
+ def delete_action(_name)
17
+ response = Excon::Response.new
18
+ response.status = 204
19
+ response
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def delete_action_execution(id)
7
+ request(
8
+ :expects => 204,
9
+ :method => "DELETE",
10
+ :path => "action_executions/#{id}"
11
+ )
12
+ end
13
+ end
14
+
15
+ class Mock
16
+ def delete_action_execution(_id)
17
+ response = Excon::Response.new
18
+ response.status = 204
19
+ response
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def delete_cron_trigger(name)
7
+ request(
8
+ :expects => 204,
9
+ :method => "DELETE",
10
+ :path => "cron_triggers/#{URI.encode(name)}"
11
+ )
12
+ end
13
+ end
14
+
15
+ class Mock
16
+ def delete_cron_trigger(_name)
17
+ response = Excon::Response.new
18
+ response.status = 204
19
+ response
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def delete_environment(name)
7
+ request(
8
+ :expects => 204,
9
+ :method => "DELETE",
10
+ :path => "environments/#{URI.encode(name)}"
11
+ )
12
+ end
13
+ end
14
+
15
+ class Mock
16
+ def delete_environment(_name)
17
+ response = Excon::Response.new
18
+ response.status = 204
19
+ response
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def delete_execution(id)
7
+ request(
8
+ :expects => 204,
9
+ :method => "DELETE",
10
+ :path => "executions/#{id}"
11
+ )
12
+ end
13
+ end
14
+
15
+ class Mock
16
+ def delete_execution(_id)
17
+ response = Excon::Response.new
18
+ response.status = 204
19
+ response
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def delete_workbook(name)
7
+ request(
8
+ :expects => 204,
9
+ :method => "DELETE",
10
+ :path => "workbooks/#{URI.encode(name)}"
11
+ )
12
+ end
13
+ end
14
+
15
+ class Mock
16
+ def delete_workbook(_name)
17
+ response = Excon::Response.new
18
+ response.status = 204
19
+ response
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Fog
2
+ module Workflow
3
+ class OpenStack
4
+ class V2
5
+ class Real
6
+ def delete_workflow(identifier)
7
+ request(
8
+ :expects => 204,
9
+ :method => "DELETE",
10
+ :path => "workflows/#{identifier}"
11
+ )
12
+ end
13
+ end
14
+
15
+ class Mock
16
+ def delete_workflow(_identifier)
17
+ response = Excon::Response.new
18
+ response.status = 204
19
+ response
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end