engineyard-api 0.0.1
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.
- data/README.rdoc +34 -0
- data/lib/engineyard-api.rb +9 -0
- data/lib/engineyard-api/account.rb +18 -0
- data/lib/engineyard-api/app.rb +56 -0
- data/lib/engineyard-api/engineyard.rb +66 -0
- data/lib/engineyard-api/environment.rb +151 -0
- data/lib/engineyard-api/ey_api.rb +27 -0
- data/lib/engineyard-api/instance.rb +59 -0
- data/lib/engineyard-api/version.rb +3 -0
- metadata +133 -0
data/README.rdoc
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
= Engineyard-API
|
2
|
+
* {rubyforge rdoc}[http://tbd]
|
3
|
+
* {rubyforge project}[http://tbd]
|
4
|
+
* {github project}[http://github.com/tbd]
|
5
|
+
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
... will usually be as simple as:
|
10
|
+
|
11
|
+
gem install engineyard-api
|
12
|
+
|
13
|
+
Or, if you downloaded the archive:
|
14
|
+
|
15
|
+
gem build engineyard-api
|
16
|
+
gem install engineyard-api-<version>
|
17
|
+
|
18
|
+
=== Usage & examples
|
19
|
+
|
20
|
+
==== Listing instances
|
21
|
+
require 'engineyard-api'
|
22
|
+
ey = Engineyard.new("API_KEY")
|
23
|
+
environment = ey.environment_by_name("Production")
|
24
|
+
environment.instances.each do |instance|
|
25
|
+
puts instance.id
|
26
|
+
end
|
27
|
+
|
28
|
+
===== Multile Accounts
|
29
|
+
require 'engineyard-api'
|
30
|
+
ey = Engineyard.new("API_KEY")
|
31
|
+
account1_environment = ey.account_by_name("Account1").environments.first
|
32
|
+
account2_environment = ey.account_by_id(1234).environment_by_name("production")
|
33
|
+
last_account_environment = ey.accounts.last.environment_by_id(4567)
|
34
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require_relative 'engineyard-api/environment.rb'
|
5
|
+
require_relative 'engineyard-api/ey_api.rb'
|
6
|
+
require_relative 'engineyard-api/instance.rb'
|
7
|
+
require_relative 'engineyard-api/app.rb'
|
8
|
+
require_relative 'engineyard-api/account.rb'
|
9
|
+
require_relative 'engineyard-api/engineyard.rb'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module EngineyardAPI
|
2
|
+
# Users (and API keys) can have access to multiple accounts.
|
3
|
+
class Account
|
4
|
+
# Returns account id as an Integer
|
5
|
+
attr_reader :id
|
6
|
+
# Returns Account name as a String
|
7
|
+
attr_reader :name
|
8
|
+
# Returns a list of environments in an Array
|
9
|
+
attr_reader :environments
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(account) # :nodoc:
|
13
|
+
@id = account['id'] if account.has_key? 'id'
|
14
|
+
@name = account['name'] if account.has_key? 'name'
|
15
|
+
@environments = account.has_key?(environments) ? account['environments'] : []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
module EngineyardAPI
|
3
|
+
# Class to store application information
|
4
|
+
class App
|
5
|
+
|
6
|
+
def initialize(app) # :nodoc:
|
7
|
+
@app=app
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns application id in form of an integer
|
11
|
+
def app_id
|
12
|
+
@app['id']
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return account name as string
|
16
|
+
def name
|
17
|
+
@app['name']
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return repository associated to application
|
21
|
+
def repository
|
22
|
+
@app['repository_uri']
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns type of application
|
26
|
+
def app_type
|
27
|
+
@app['app_type_id']
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return account id as integer
|
31
|
+
def account_id
|
32
|
+
@app['account']['id']
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return account nam eas string
|
36
|
+
def account_name
|
37
|
+
@app['account']['name']
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](key) # :nodoc:
|
41
|
+
@app['key']
|
42
|
+
end
|
43
|
+
|
44
|
+
def zone # :nodoc:
|
45
|
+
@app['availability_zone']
|
46
|
+
end
|
47
|
+
|
48
|
+
def last_deploy=(deployment) # :nodoc:
|
49
|
+
@last_deploy=deployment
|
50
|
+
end
|
51
|
+
|
52
|
+
def last_deploy # :nodoc:
|
53
|
+
@last_deploy
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
module EngineyardAPI
|
3
|
+
# Create new Engineyard object bassed off supplied options
|
4
|
+
# Example:
|
5
|
+
# @ey = Engineyard.new(:key => "myapikey")
|
6
|
+
def EngineyardAPI.new(*args)
|
7
|
+
Engineyard.new(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Main class returned by EngineyardAPI.new(:key => "key")
|
11
|
+
class Engineyard
|
12
|
+
attr_accessor :accounts
|
13
|
+
|
14
|
+
|
15
|
+
def initialize(options) #:nodoc
|
16
|
+
@accounts = []
|
17
|
+
raise ArgumentError, "API key not specified. #{self.class}.new(:key => '<api_key>')" unless options.has_key? :key
|
18
|
+
@options=options
|
19
|
+
# @path = "/environments/#{id}"
|
20
|
+
@api = EngineyardAPI::EyAPI.new(@options[:key])
|
21
|
+
EngineyardAPI.const_set("API", @api)
|
22
|
+
reload
|
23
|
+
end
|
24
|
+
|
25
|
+
## Call environment information from API. Information is cached in class until reload is called again. Prevents calling API when not needed.
|
26
|
+
def reload
|
27
|
+
account_list=@api.get("/accounts")['accounts']
|
28
|
+
environment_list=@api.get("/environments")['environments']
|
29
|
+
account_list.each {|a| @accounts << Account.new(a) }
|
30
|
+
environment_list.each {|e|
|
31
|
+
account_by_id(e['account']['id']).environments << Environment.new(e['id'],e)
|
32
|
+
}
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns array of environments
|
37
|
+
def environments
|
38
|
+
return @accounts.map { |account| account.environments }.flatten
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# Find an environment based on name. Returns EngineyardAPI::Environment
|
43
|
+
def environment_by_name(name)
|
44
|
+
return environments.select {|e| e.name == name }.first
|
45
|
+
end
|
46
|
+
|
47
|
+
# Find an environment based on name. Returns EngineyardAPI::Environment
|
48
|
+
def environment_by_id(id)
|
49
|
+
return environments.select {|e| e.environment_id == id }.first
|
50
|
+
end
|
51
|
+
|
52
|
+
# Find an account based on name. Returns EngineyardAPI::Account
|
53
|
+
def account_by_id(id)
|
54
|
+
ac = @accounts.select { |a| a.id == id }
|
55
|
+
raise "No such account" unless ac.count == 1
|
56
|
+
return ac[0]
|
57
|
+
end
|
58
|
+
|
59
|
+
# Locate an account by name. Returns EngineyardAPI::Account
|
60
|
+
def account_by_name(name)
|
61
|
+
ac = @accounts.select { |a| a.name == name }
|
62
|
+
raise "No such account" unless ac.count == 1
|
63
|
+
return ac[0]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
|
2
|
+
module EngineyardAPI
|
3
|
+
# Environment class stores all environment data, and crawls over other parts of the api to grab data for Instance and App classes.
|
4
|
+
class Environment
|
5
|
+
def initialize(id,environment = nil) # :nodoc:
|
6
|
+
@environment = environment if environment
|
7
|
+
@id = id
|
8
|
+
@path = "/environments/#{id}"
|
9
|
+
#EngineyardAPI::API = EngineyardAPI::API
|
10
|
+
reload(environment)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns integer containing environment ID
|
14
|
+
def environment_id
|
15
|
+
@environment['id']
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns String containing th eenvironment name
|
19
|
+
def name
|
20
|
+
@environment['name']
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns number of instances in environment
|
24
|
+
def instance_count
|
25
|
+
@environment['instances_count']
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns array full of EngineyardAPI::Instnaces
|
29
|
+
def instances
|
30
|
+
@instances
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns current status
|
34
|
+
def instance_status
|
35
|
+
@environment['instance_status']
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return load balancer IP address, if any.
|
39
|
+
def load_balancer_ip_address
|
40
|
+
@environment['load_balancer_ip_address']
|
41
|
+
end
|
42
|
+
|
43
|
+
def account # :nodoc:
|
44
|
+
@environment['account']
|
45
|
+
end
|
46
|
+
|
47
|
+
# Name of current stack associated with environment
|
48
|
+
def stack_name
|
49
|
+
@environment['stack_name']
|
50
|
+
end
|
51
|
+
|
52
|
+
# Default user used for deployments (usually 'deploy')
|
53
|
+
def ssh_username
|
54
|
+
@environment['ssh_username']
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns current stack name
|
58
|
+
def app_server_stack_name
|
59
|
+
@environment['app_server_stack_name']
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns framework environment (i.e. "production", or "staging")
|
63
|
+
def framework_env
|
64
|
+
@environment['framework_env']
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns array of EngineyardAPI::APP's
|
68
|
+
def apps
|
69
|
+
@applications
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def deployment_configurations
|
74
|
+
@environment['deployment_configurations']
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return EngineyardAPI::Instance of current app_master
|
78
|
+
def app_master
|
79
|
+
@app_master
|
80
|
+
end
|
81
|
+
|
82
|
+
# This was mostly used for testing api. not used in script. keeping for future development reasons.
|
83
|
+
def keys(k=nil) # :nodoc:
|
84
|
+
return @environment.keys unless k
|
85
|
+
return @environment[k]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Create a new instance. first param can take any arguments in hash that API would accept (i.e. :instance_size, :volume_size, etc)
|
89
|
+
def add_instance(body)
|
90
|
+
request = {:body => {:request => body}}
|
91
|
+
EngineyardAPI::API.post("#{@path}/add_instances", request)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Checks to see how many add instance actions are working on environment. returns array.
|
95
|
+
def add_status
|
96
|
+
EngineyardAPI::API.get("#{@path}/add_instances")
|
97
|
+
end
|
98
|
+
|
99
|
+
# Remove instance. Provide role (:app, or :util), and :amazon_id if needed.
|
100
|
+
def remove_instance(remove_instance)
|
101
|
+
request = {:body => { :request => {:role => role } } } if [:app,:util].include? remove_instance
|
102
|
+
request = {:body => { :request => { :provisioned_id => remove_instance.amazon_id } }} unless [:app,:util].include? remove_instance
|
103
|
+
EngineyardAPI::API.post("#{@path}/remove_instances",request)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Same as add_status, except checks for instances being removed. retruns array.
|
107
|
+
def remove_status
|
108
|
+
EngineyardAPI::API.get("#{@path}/remove_instances")
|
109
|
+
end
|
110
|
+
|
111
|
+
# Used for development. same as keys method, but does not return a list of keys if no params.
|
112
|
+
def [](key)
|
113
|
+
@enviornment[key]
|
114
|
+
end
|
115
|
+
|
116
|
+
# Checks if environment is busy. returns true and hash of actions if instances are being added, instances are being removed, or if deploy is ongoing.
|
117
|
+
def busy?
|
118
|
+
return true, {:add_status => add_status["requests"] , :remove_status => remove_status["requests"] } if add_status["requests"].count > 0 or remove_status["requests"].count > 0
|
119
|
+
return false
|
120
|
+
end
|
121
|
+
|
122
|
+
# Checks if environment is deploying (works for standard deploy only (i.e. via dashboard or ey_cli utillity)
|
123
|
+
def deploying?
|
124
|
+
return true if apps.select {|app| app.last_deploy['finished_at'] == nil }.count > 0
|
125
|
+
return false
|
126
|
+
end
|
127
|
+
|
128
|
+
# Like pressing 'apply' on dashboard
|
129
|
+
def rebuild
|
130
|
+
EngineyardAPI::API.put("#{@path}/update_instances",{:body => ""})
|
131
|
+
end
|
132
|
+
|
133
|
+
## Call environment information from API. Information is cached in class until reload is called again. Prevents calling API when not needed.
|
134
|
+
def reload(e=nil)
|
135
|
+
@environment = EngineyardAPI::API.get(@path)['environment'] unless e
|
136
|
+
@instances = Array.new
|
137
|
+
@applications = Array.new
|
138
|
+
# Populate instances
|
139
|
+
@environment['instances'].each {|instance|
|
140
|
+
@instances << EngineyardAPI::Instance.new(instance)
|
141
|
+
}
|
142
|
+
# Populate Apps
|
143
|
+
@environment['apps'].each {|app|
|
144
|
+
@applications << EngineyardAPI::App.new(app)
|
145
|
+
}
|
146
|
+
@app_master=App.new(@environment['app_master'])
|
147
|
+
@applications.each { |app| app.last_deploy=EngineyardAPI::API.get("/apps/#{app.app_id}/environments/#{environment_id}/deployments/last")['deployment'] }
|
148
|
+
return true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
module EngineyardAPI
|
3
|
+
# Actually communicates with API.
|
4
|
+
class EyAPI # :nodoc:
|
5
|
+
include HTTParty
|
6
|
+
base_uri 'https://cloud.engineyard.com:443/api/v2/'
|
7
|
+
format :json
|
8
|
+
headers 'Accept' => 'application/json'
|
9
|
+
headers 'Content-Type' => 'application/json'
|
10
|
+
|
11
|
+
def initialize(key = $options[:key]) # :nodoc:
|
12
|
+
@api_key = key
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(path) # :nodoc:
|
17
|
+
self.class.get(path, :headers => { 'X-EY-Cloud-Token' => @api_key })
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(path,data) # :nodoc:
|
21
|
+
self.class.post(path,data.merge(:headers=> { 'X-EY-Cloud-Token' => @api_key}))
|
22
|
+
end
|
23
|
+
def put(path,data={}) # :nodoc:
|
24
|
+
self.class.put(path,data.merge(:headers => { 'X-EY-Cloud-Token' => @api_key}))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module EngineyardAPI
|
2
|
+
# Store information on instances
|
3
|
+
class Instance
|
4
|
+
def initialize(instance) # :nodoc:
|
5
|
+
@instance = instance
|
6
|
+
@logs = EngineyardAPI::API.get "/instances/#{@instance['id']}/logs"
|
7
|
+
@alerts = EngineyardAPI::API.get "/instances/#{@instance['id']}/alerts"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Return internal ID
|
11
|
+
def instance_id
|
12
|
+
@instance['id']
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return instance status (i.e. :running, :error, etc)
|
16
|
+
def status
|
17
|
+
@instance['status']
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return instance role (i.e., app, utility, db, db_master, app_master
|
21
|
+
def role
|
22
|
+
@instance['role']
|
23
|
+
end
|
24
|
+
|
25
|
+
# Retruns instance nam
|
26
|
+
def name
|
27
|
+
@instance['name']
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return amazon ID
|
31
|
+
def amazon_id
|
32
|
+
@instance['amazon_id']
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return instance public hostname
|
36
|
+
def public_hostname
|
37
|
+
@instance['public_hostname']
|
38
|
+
end
|
39
|
+
|
40
|
+
# Retruns instance zone
|
41
|
+
def zone
|
42
|
+
@instance['availability_zone']
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns list of log entries as array
|
46
|
+
def logs
|
47
|
+
return @logs['logs']
|
48
|
+
end
|
49
|
+
def [](key) # :nodoc:
|
50
|
+
@instance['key']
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns list of alerts as array
|
54
|
+
def alerts
|
55
|
+
return @alerts['alerts']
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: engineyard-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- James Paterni
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: httparty
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: webmock
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: Ruby Wrapper for Egnine Yard API
|
95
|
+
email: jpaterni@engineyard.com
|
96
|
+
executables: []
|
97
|
+
extensions: []
|
98
|
+
extra_rdoc_files: []
|
99
|
+
files:
|
100
|
+
- README.rdoc
|
101
|
+
- lib/engineyard-api/account.rb
|
102
|
+
- lib/engineyard-api/app.rb
|
103
|
+
- lib/engineyard-api/engineyard.rb
|
104
|
+
- lib/engineyard-api/environment.rb
|
105
|
+
- lib/engineyard-api/ey_api.rb
|
106
|
+
- lib/engineyard-api/instance.rb
|
107
|
+
- lib/engineyard-api/version.rb
|
108
|
+
- lib/engineyard-api.rb
|
109
|
+
homepage: ''
|
110
|
+
licenses: []
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 1.8.24
|
130
|
+
signing_key:
|
131
|
+
specification_version: 3
|
132
|
+
summary: Ruby Wrapper for Engine Yard API
|
133
|
+
test_files: []
|