stackmate 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +7 -0
- data/README.md +25 -8
- data/bin/stackmate.rb +57 -53
- data/lib/stackmate/aws_attribs.rb +31 -31
- data/lib/stackmate/classmap.rb +33 -25
- data/lib/stackmate/client.rb +80 -0
- data/lib/stackmate/intrinsic_functions.rb +94 -38
- data/lib/stackmate/logging.rb +19 -19
- data/lib/stackmate/participants/cloudstack.rb +250 -165
- data/lib/stackmate/participants/cloudstack_affinitygroup.rb +122 -0
- data/lib/stackmate/participants/cloudstack_autoscalepolicy.rb +113 -0
- data/lib/stackmate/participants/cloudstack_autoscalevmgroup.rb +86 -0
- data/lib/stackmate/participants/cloudstack_autoscalevmprofile.rb +140 -0
- data/lib/stackmate/participants/cloudstack_condition.rb +122 -0
- data/lib/stackmate/participants/cloudstack_egressfirewallrule.rb +149 -0
- data/lib/stackmate/participants/cloudstack_firewallrule.rb +149 -0
- data/lib/stackmate/participants/cloudstack_globalloadbalancerrule.rb +158 -0
- data/lib/stackmate/participants/cloudstack_instancegroup.rb +113 -0
- data/lib/stackmate/participants/cloudstack_ipaddress.rb +149 -0
- data/lib/stackmate/participants/cloudstack_ipforwardingrule.rb +131 -0
- data/lib/stackmate/participants/cloudstack_iptonic.rb +95 -0
- data/lib/stackmate/participants/cloudstack_iso.rb +95 -0
- data/lib/stackmate/participants/cloudstack_lbhealthcheckpolicy.rb +140 -0
- data/lib/stackmate/participants/cloudstack_lbstickinesspolicy.rb +122 -0
- data/lib/stackmate/participants/cloudstack_loadbalancer.rb +158 -0
- data/lib/stackmate/participants/cloudstack_loadbalancerrule.rb +185 -0
- data/lib/stackmate/participants/cloudstack_network.rb +293 -0
- data/lib/stackmate/participants/cloudstack_networkacl.rb +176 -0
- data/lib/stackmate/participants/cloudstack_networkacllist.rb +104 -0
- data/lib/stackmate/participants/cloudstack_nictovirtualmachine.rb +104 -0
- data/lib/stackmate/participants/cloudstack_portforwardingrule.rb +176 -0
- data/lib/stackmate/participants/cloudstack_project.rb +113 -0
- data/lib/stackmate/participants/cloudstack_remoteaccessvpn.rb +122 -0
- data/lib/stackmate/participants/cloudstack_securitygroup.rb +122 -0
- data/lib/stackmate/participants/cloudstack_securitygroupegress.rb +185 -0
- data/lib/stackmate/participants/cloudstack_securitygroupingress.rb +185 -0
- data/lib/stackmate/participants/cloudstack_snapshot.rb +113 -0
- data/lib/stackmate/participants/cloudstack_snapshotpolicy.rb +122 -0
- data/lib/stackmate/participants/cloudstack_sshkeypair.rb +113 -0
- data/lib/stackmate/participants/cloudstack_staticnat.rb +113 -0
- data/lib/stackmate/participants/cloudstack_staticroute.rb +95 -0
- data/lib/stackmate/participants/cloudstack_tags.rb +113 -0
- data/lib/stackmate/participants/cloudstack_template.rb +212 -0
- data/lib/stackmate/participants/cloudstack_togloballoadbalancerrule.rb +104 -0
- data/lib/stackmate/participants/cloudstack_toloadbalancerrule.rb +95 -0
- data/lib/stackmate/participants/cloudstack_virtualmachine.rb +348 -0
- data/lib/stackmate/participants/cloudstack_virtualmachineops.rb +95 -0
- data/lib/stackmate/participants/cloudstack_vmsnapshot.rb +113 -0
- data/lib/stackmate/participants/cloudstack_volume.rb +176 -0
- data/lib/stackmate/participants/cloudstack_volumeops.rb +111 -0
- data/lib/stackmate/participants/cloudstack_vpc.rb +158 -0
- data/lib/stackmate/participants/cloudstack_vpnconnection.rb +95 -0
- data/lib/stackmate/participants/cloudstack_vpncustomergateway.rb +176 -0
- data/lib/stackmate/participants/cloudstack_vpngateway.rb +86 -0
- data/lib/stackmate/participants/cloudstack_vpnuser.rb +122 -0
- data/lib/stackmate/participants/common.rb +219 -70
- data/lib/stackmate/participants/stacknest.rb +6 -0
- data/lib/stackmate/resolver.rb +122 -0
- data/lib/stackmate/stack.rb +116 -60
- data/lib/stackmate/stack_executor.rb +99 -37
- data/lib/stackmate/stackpi.rb +46 -0
- data/lib/stackmate/version.rb +1 -1
- data/lib/stackmate/waitcondition_server.rb +15 -15
- data/lib/stackmate.rb +1 -1
- data/stackmate.gemspec +2 -4
- metadata +70 -19
data/CHANGELOG.txt
ADDED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
2
1
|
# Stackmate - CloudFormation for CloudStack
|
3
2
|
|
4
|
-
A library designed to read
|
3
|
+
A library designed to read CloudFormation templates
|
5
4
|
and execute them on a CloudStack deployment.
|
5
|
+
Supports AWS Cloudformation templates (namespace AWS::Cloudformation) as well as the CloudStack:: native namespace.
|
6
6
|
Uses the [ruote](http://ruote.rubyforge.org) workflow engine,
|
7
7
|
and embeds a modular [Sinatra](http://www.sinatrarb.com/) application for wait handles.
|
8
8
|
|
@@ -11,7 +11,8 @@ Instead it runs everything on the client side.
|
|
11
11
|
|
12
12
|
[Stacktician](https://github.com/chiradeep/stacktician) embeds stackmate to run it as a web application.
|
13
13
|
|
14
|
-
|
14
|
+
For the AWS::CloudFormation namespace, only Basic Zone (aka EC2-Classic) is supported for now.
|
15
|
+
For the CloudStack:: namespace, all virtual resources that can be created in CloudStack 4.2 are supported.
|
15
16
|
|
16
17
|
Follow:
|
17
18
|
* \#cloudstack-dev on Freenode
|
@@ -59,7 +60,9 @@ $ export SECKEY=9iSsuImdUxU0oumHu0p11li4IoUtwcvrSHcU63ZHS_y-4Iz3w5xPROzyjZTUXkhI
|
|
59
60
|
$ export URL="http://localhost:8080/client/api"
|
60
61
|
```
|
61
62
|
|
62
|
-
|
63
|
+
## Sample Templates
|
64
|
+
### AWS samples
|
65
|
+
AWS samples are templates that use the AWS::CloudFormation namespace.
|
63
66
|
|
64
67
|
You need a couple of mappings from AWS ids to your CloudStack implementation:
|
65
68
|
|
@@ -73,6 +76,11 @@ templates:
|
|
73
76
|
zoneid: b3409835-02b0-4d21-bba4-1f659402117e
|
74
77
|
```
|
75
78
|
|
79
|
+
### CloudStack Samples
|
80
|
+
CloudStack samples use the CloudStack namespace. These templates typically require the zone id, service offering id and template id as input parameters
|
81
|
+
|
82
|
+
|
83
|
+
## Usage Example
|
76
84
|
* Ensure you have a ssh keypair called 'Foo' (used in the template parameter below) for your account FIRST:
|
77
85
|
|
78
86
|
```bash
|
@@ -83,7 +91,7 @@ $ cloudmonkey
|
|
83
91
|
```
|
84
92
|
|
85
93
|
|
86
|
-
* Create a LAMP stack
|
94
|
+
* Create a LAMP stack (AWS template)
|
87
95
|
|
88
96
|
```bash
|
89
97
|
bin/stackmate MYSTACK01 --template-file=templates/LAMP_Single_Instance.template -p "DBName=cloud;DBUserName=cloud;SSHLocation=75.75.75.0/24;DBUsername=cloud;DBPassword=cloud;DBRootPassword=cloud;KeyName=Foo"
|
@@ -102,12 +110,22 @@ If you don't want the wait condition server to run, just use '-n'. Stackmate wil
|
|
102
110
|
|
103
111
|
If you want to validate your template, you can use the --dry-run option. This will parse and validate the template and create an execution schedule.
|
104
112
|
|
113
|
+
## Extending StackMate to other APIs
|
114
|
+
|
115
|
+
StackMate allows you to define your own workflow participants that will be called based on the template namespace. For example, you can define a class Foo::Bar
|
116
|
+
and use Foo::Bar as a "Type" in the template. The requirement is :
|
117
|
+
(1) It should contain a class variable @@stackmate_participant set to true so as to register with StackMate
|
118
|
+
(2) Foo::Bar should have Ruote::Participant as its ancestor
|
119
|
+
(3) Foo::Bar should have a method named "consume_workitem" that defines actions to be taken when called with workitem.
|
120
|
+
|
121
|
+
Use --plugins <Directories with ruby files> to add plugins. This has undergone limited testing
|
122
|
+
|
123
|
+
|
105
124
|
## TODO
|
106
125
|
* Parallelize (with ruote concurrence) where possible
|
107
|
-
* rollback on error
|
108
126
|
* timeouts
|
109
127
|
* embed in a web app ( [Stacktician](https://github.com/chiradeep/stacktician) )
|
110
|
-
|
128
|
+
|
111
129
|
|
112
130
|
## Feedback & bug reports
|
113
131
|
|
@@ -142,7 +160,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
142
160
|
|
143
161
|
- ruote, <http://ruote.rubyforge.org/>
|
144
162
|
- sinatra, <http://www.sinatrarb.com/>
|
145
|
-
- cloudstack_ruby_client, <https://github.com/chipchilders/cloudstack_ruby_client>
|
146
163
|
|
147
164
|
Many thanks to the authors
|
148
165
|
|
data/bin/stackmate.rb
CHANGED
@@ -9,66 +9,70 @@ require 'stackmate/waitcondition_server'
|
|
9
9
|
options = {}
|
10
10
|
stack_name = ''
|
11
11
|
opt_parser = OptionParser.new do |opts|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
12
|
+
opts.banner = "Usage: stackmate.rb STACK_NAME [options]"
|
13
|
+
opts.separator ""
|
14
|
+
opts.separator "Specific options:"
|
15
|
+
opts.on(:REQUIRED, "--template-file FILE", String, "Path to the file that contains the template") do |f|
|
16
|
+
options[:file] = f
|
17
|
+
end
|
18
|
+
opts.on("-p", "--parameters [KEY1=VALUE1 KEY2=VALUE2..]", "Parameter values used to create the stack.") do |p|
|
19
|
+
options[:params] = p
|
20
|
+
puts p
|
21
|
+
end
|
22
|
+
options[:wait_conditions] = true
|
23
|
+
opts.on("-n", "--no-wait-conditions", "Do not create any wait conditions") do
|
24
|
+
options[:wait_conditions] = false
|
25
|
+
end
|
26
|
+
options[:dry_run] = false
|
27
|
+
opts.on("-r", "--dry-run", "Parse and pretend to execute but not actually do anything. Useful for validating the template") do
|
28
|
+
options[:dry_run] = true
|
29
|
+
end
|
30
|
+
opts.on("-h", "--help", "Show this message") do
|
31
|
+
puts opts
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
opts.on("--plugins DIRS",String, "Comman separated plugins directory") do |plugins|
|
35
|
+
options[:plugins] = plugins
|
36
|
+
end
|
34
37
|
end
|
35
38
|
|
36
39
|
begin
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
opt_parser.parse!(ARGV)
|
41
|
+
if ARGV.size == 1
|
42
|
+
stack_name = ARGV[0]
|
43
|
+
end
|
41
44
|
rescue => e
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
+
puts e.message.capitalize
|
46
|
+
puts opt_parser.help()
|
47
|
+
exit 1
|
45
48
|
end
|
46
49
|
|
47
50
|
if options[:file] && stack_name != ''
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
51
|
+
if options[:wait_conditions]
|
52
|
+
Thread.new do
|
53
|
+
StackMate::WaitConditionServer.run!
|
52
54
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
55
|
+
end
|
56
|
+
engine = Ruote::Dashboard.new(
|
57
|
+
Ruote::Worker.new(
|
58
|
+
Ruote::HashStorage.new))
|
59
|
+
engine.noisy = ENV['NOISY'] == 'true'
|
60
|
+
engine.configure('wait_logger_timeout', 600)
|
57
61
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
nil
|
62
|
+
unknown = nil
|
63
|
+
unresolved = catch(:unresolved) do
|
64
|
+
unknown = catch(:unknown) do
|
65
|
+
StackMate.configure('NOOP') if options[:dry_run]
|
66
|
+
opts = {}
|
67
|
+
api_opts = {:APIKEY => "#{ENV['APIKEY']}", :SECKEY => "#{ENV['SECKEY']}", :URL => "#{ENV['URL']}" }
|
68
|
+
p = StackMate::StackExecutor.new(options[:file], stack_name, options[:params], engine, options[:wait_conditions], api_opts, options[:plugins])
|
69
|
+
p.launch()
|
70
|
+
nil
|
69
71
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
puts 'Failed to resolve parameters ' + unresolved.to_s if unresolved
|
75
|
+
print "Sorry, I don't know how to create resources of type: ", unknown, "\n" if unknown
|
76
|
+
else
|
77
|
+
puts opt_parser.help()
|
78
|
+
end
|
@@ -1,33 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
1
|
+
AWS_ATTRIBS = {'AWS::CloudFormation::WaitCondition' => [ 'Data'],
|
2
|
+
'AWS::CloudFormation::Stack' => ['Outputs.EmbeddedStackOutputName'],
|
3
|
+
'AWS::CloudFront::Distribution' => ['DomainName'],
|
4
|
+
'AWS::EC2::EIP' => ['AllocationId'],
|
5
|
+
'AWS::EC2::Instance' => ['AvailabilityZone', 'PrivateDnsName', 'PublicDnsName', 'PrivateIp', 'PublicIp'],
|
6
|
+
'AWS::EC2::AWS::EC2::SubnetNetworkAclAssociation' => ['AssociationId'],
|
7
|
+
'AWS::ElasticBeanstalk::Environment' => ['EndpointURL'],
|
8
|
+
'AWS::ElasticLoadBalancing::LoadBalancer' => ['CanonicalHostedZoneName', 'CanonicalHostedZoneNameID',
|
9
|
+
'DNSName', 'SourceSecurityGroup.GroupName', 'SourceSecurityGroup.OwnerAlias'],
|
10
|
+
'AWS::IAM::AccessKey' => ['SecretAccessKey'],
|
11
|
+
'AWS::IAM::Group' => ['Arn'],
|
12
|
+
'AWS::IAM::User' => ['Arn'],
|
13
|
+
'AWS::RDS::DBInstance' => ['Endpoint.Address', 'Endpoint.Port'],
|
14
|
+
'AWS::S3::Bucket' => ['DomainName', 'WebsiteURL', 'Arn'],
|
15
|
+
'AWS::SQS::Queue' => ['Arn', 'QueueName']
|
16
|
+
}
|
17
17
|
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
19
|
+
AWS_FAKE_ATTRIB_VALUES = {'AWS::CloudFormation::WaitCondition' => {'Data' => '{ "Signal1" : "Step 1 complete." , "Signal2" : "Step 2 complete." } '},
|
20
|
+
'AWS::CloudFormation::Stack' => {'Outputs.EmbeddedStackOutputName' => 'FakeEmbeddedStackOutputName'},
|
21
|
+
'AWS::CloudFront::Distribution' => {'DomainName' => 'd2fadu0nynjpfn.cloudfront.net'},
|
22
|
+
'AWS::EC2::EIP' => {'AllocationId' => 'eipalloc-5723d13e'},
|
23
|
+
'AWS::EC2::Instance' => {'AvailabilityZone' => 'us-east-1b', 'PrivateDnsName' => 'ip-10-24-34-0.cs.internal', 'PublicDnsName' => 'cs-17-10-90-145.compute.acs.org.', 'PrivateIp' => '10.24.34.0', 'PublicIp' => '75.75.75.111'},
|
24
|
+
'AWS::EC2::AWS::EC2::SubnetNetworkAclAssociation' => {'AssociationId' => 'aclassoc-e5b95c8c'},
|
25
|
+
'AWS::ElasticBeanstalk::Environment' => {'EndpointURL' => 'eb-myst-myen-132MQC4KRLAMD-1371280482.us-east-1.elb.amazonaws.com'},
|
26
|
+
'AWS::ElasticLoadBalancing::LoadBalancer' => {'CanonicalHostedZoneName' => 'mystack-myelb-15HMABG9ZCN57-1013119603.us-east-1.elb.amazonaws.com', 'CanonicalHostedZoneNameID' => 'Z3DZXE0Q79N41H', 'DNSName' => 'mystack-myelb-15HMABG9ZCN57-1013119603.us-east-1.elb.amazonaws.com', 'SourceSecurityGroup.GroupName' => 'elb-ssg', 'SourceSecurityGroup.OwnerAlias' => 'elb-ssg-owner'},
|
27
|
+
'AWS::IAM::AccessKey' => {'SecretAccessKey' => 'c8alrXUtnYEMI/K7MDAZQ/bPxRfiCYzEXAMPLEKEY'},
|
28
|
+
'AWS::IAM::Group' => {'Arn' => 'arn:aws:iam::123456789012:group/mystack-mygroup-1DZETITOWEKVO'},
|
29
|
+
'AWS::IAM::User' => {'Arn' => 'mystack-myuser-1CCXAFG2H2U4D'},
|
30
|
+
'AWS::RDS::DBInstance' => {'Endpoint.Address' => 'mystack-mydb-1apw1j4phylrk.cg034hpkmmjt.us-east-1.rds.amazonaws.com', 'Endpoint.Port' => '3306'},
|
31
|
+
'AWS::S3::Bucket' => {'DomainName' => 'mystack-mybucket-kdwwxmddtr2g.s3.amazonaws.com', 'WebsiteURL' => 'http://mystack-mybucket-kdwwxmddtr2g.s3-website-us-east-1.amazonaws.com/', 'Arn' => 'arn:aws:s3::12345678901::root'},
|
32
|
+
'AWS::SQS::Queue' => {'Arn' => 'arn:aws:sqs:us-east-1:123456789012:mystack-myqueue-15PG5C2FC1CW8', 'QueueName' => 'mystack-myqueue-1VF9BKQH5BJVI'}
|
33
|
+
}
|
data/lib/stackmate/classmap.rb
CHANGED
@@ -1,30 +1,38 @@
|
|
1
1
|
module StackMate
|
2
|
-
|
3
|
-
|
2
|
+
PROFILES = ['CLOUDSTACK', 'NOOP']
|
3
|
+
@profile = 'CLOUDSTACK'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
CS_CLASS_MAP = {
|
6
|
+
'AWS::CloudFormation::WaitConditionHandle' => 'StackMate::WaitConditionHandle',
|
7
|
+
'AWS::CloudFormation::WaitCondition' => 'StackMate::WaitCondition',
|
8
|
+
'AWS::EC2::Instance' => 'StackMate::CloudStackInstance',
|
9
|
+
'AWS::EC2::SecurityGroup' => 'StackMate::CloudStackSecurityGroupAWS',
|
10
|
+
'Outputs' => 'StackMate::CloudStackOutput',
|
11
|
+
}
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
13
|
+
def StackMate.class_for(cf_resource)
|
14
|
+
#return cf_resource
|
15
|
+
case @profile
|
16
|
+
when 'CLOUDSTACK'
|
17
|
+
if(cf_resource.start_with?("CloudStack::"))
|
18
|
+
c = cf_resource.split('::')[1]
|
19
|
+
"StackMate::CloudStack"+c
|
20
|
+
elsif(CS_CLASS_MAP.has_key?(cf_resource))
|
21
|
+
CS_CLASS_MAP[cf_resource]
|
22
|
+
else
|
23
|
+
cf_resource
|
24
|
+
end
|
25
|
+
when 'NOOP'
|
26
|
+
if cf_resource == 'Outputs'
|
27
|
+
'StackMate::Output'
|
28
|
+
else
|
29
|
+
'StackMate::NoOpResource'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
25
33
|
|
26
|
-
|
27
|
-
|
28
|
-
|
34
|
+
def StackMate.configure(profile)
|
35
|
+
@profile = profile
|
36
|
+
end
|
29
37
|
|
30
|
-
end
|
38
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
%w[ base64 cgi openssl uri digest/sha1 net/https net/http json ].each { |f| require f }
|
2
|
+
|
3
|
+
|
4
|
+
module StackMate
|
5
|
+
class CloudStackClient
|
6
|
+
#
|
7
|
+
# The following is malformed response title in ACS, should be fixed
|
8
|
+
#
|
9
|
+
MALFORMED_RESPONSES = {
|
10
|
+
/(create|list)counter/i => 'counterresponse',
|
11
|
+
/createcondition/i => 'conditionresponse',
|
12
|
+
/createautoscalepolicy/i => 'autoscalepolicyresponse',
|
13
|
+
/createautoscalevmprofile/i => 'autoscalevmprofileresponse',
|
14
|
+
/createautoscalevmgroup/i => 'autoscalevmgroupresponse',
|
15
|
+
/enableautoscalevmgroup/i => 'enableautoscalevmGroupresponse',
|
16
|
+
/disableautoscalevmgroup/i => 'disableautoscalevmGroupresponse',
|
17
|
+
/assignvirtualmachine/i => 'moveuservmresponse',
|
18
|
+
/resetsshkeyforvirtualmachine/i => 'resetSSHKeyforvirtualmachineresponse',
|
19
|
+
/restorevirtualmachine/i => 'restorevmresponse',
|
20
|
+
/activateproject/i => 'activaterojectresponse',
|
21
|
+
/listnetworkdevice/i => 'listnetworkdevice',
|
22
|
+
/listniciranvpdevicenetworks/i => 'listniciranvpdevicenetworks',
|
23
|
+
/cancelstoragemaintenance/i => 'cancelprimarystoragemaintenanceresponse',
|
24
|
+
/enablestoragemaintenance/i => 'prepareprimarystorageformaintenanceresponse',
|
25
|
+
/copyiso/i => 'copytemplateresponse',
|
26
|
+
/deleteiso/i => 'deleteisosresponse',
|
27
|
+
/listisopermissions/i => 'listtemplatepermissionsresponse'
|
28
|
+
}
|
29
|
+
def initialize(api_url, api_key, secret_key, use_ssl=nil)
|
30
|
+
@api_url = api_url
|
31
|
+
@api_key = api_key
|
32
|
+
@secret_key = secret_key
|
33
|
+
@use_ssl = use_ssl
|
34
|
+
end
|
35
|
+
|
36
|
+
def request(params)
|
37
|
+
params['response'] = 'json'
|
38
|
+
params['apiKey'] = @api_key
|
39
|
+
|
40
|
+
data = params.map{ |k,v| "#{k.to_s}=#{CGI.escape(v.to_s).gsub(/\+|\ /, "%20")}" }.sort.join('&')
|
41
|
+
|
42
|
+
signature = OpenSSL::HMAC.digest 'sha1', @secret_key, data.downcase
|
43
|
+
signature = Base64.encode64(signature).chomp
|
44
|
+
signature = CGI.escape(signature)
|
45
|
+
|
46
|
+
url = "#{@api_url}?#{data}&signature=#{signature}"
|
47
|
+
uri = URI.parse(url)
|
48
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
49
|
+
# http.use_ssl = @use_ssl
|
50
|
+
# http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
51
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
52
|
+
|
53
|
+
http.request(request)
|
54
|
+
end
|
55
|
+
|
56
|
+
def api_call(command,params)
|
57
|
+
#params = {'command' => command}
|
58
|
+
#params.merge!(args) unless args.empty?
|
59
|
+
params['command'] = command
|
60
|
+
response = request(params)
|
61
|
+
json = JSON.parse(response.body)
|
62
|
+
resp_title = command.downcase + "response"
|
63
|
+
MALFORMED_RESPONSES.each do |k, v|
|
64
|
+
if k =~ command
|
65
|
+
resp_title = v
|
66
|
+
end
|
67
|
+
end
|
68
|
+
if !response.is_a?(Net::HTTPOK)
|
69
|
+
if ((["431","530"].include?(response.code.to_s)) && (["9999","4350"].include?(json[resp_title]['cserrorcode'].to_s)))
|
70
|
+
raise ArgumentError, json[resp_title]['errortext']
|
71
|
+
end
|
72
|
+
|
73
|
+
raise RuntimeError, json['errorresponse']['errortext'] if response.code == "432"
|
74
|
+
raise RuntimeError, "Unable to make request from client due to :" + response.to_s
|
75
|
+
#raise CloudstackRubyClient::RequestError.new(response, json)
|
76
|
+
end
|
77
|
+
json[resp_title]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -1,58 +1,114 @@
|
|
1
|
+
class Hash
|
2
|
+
def downcase_key
|
3
|
+
keys.each do |k|
|
4
|
+
store(k.downcase, Array === (v = delete(k)) ? v.map(&:downcase_key) : v)
|
5
|
+
end
|
6
|
+
self
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
1
10
|
module StackMate
|
2
11
|
module Intrinsic
|
3
12
|
|
4
|
-
def intrinsic (hash, workitem)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
def intrinsic (hash, workitem)
|
14
|
+
key = hash.keys[0]
|
15
|
+
value = hash[key]
|
16
|
+
#logger.debug "Intrinsic: key = #{key}, value = #{value}"
|
17
|
+
case key
|
18
|
+
when 'Fn::Join'
|
19
|
+
fn_join(value, workitem)
|
20
|
+
when 'Fn::GetAtt'
|
21
|
+
fn_getatt(value, workitem)
|
22
|
+
when 'Fn::Select'
|
23
|
+
fn_select(value)
|
24
|
+
when 'Ref'
|
25
|
+
fn_ref(value, workitem)
|
26
|
+
when 'Fn::Lookup'
|
27
|
+
fn_lookup(value, workitem)
|
28
|
+
when 'Fn::FindInMap'
|
29
|
+
fn_map(value, workitem)
|
30
|
+
when 'Fn::Base64'
|
31
|
+
fn_base64(value, workitem)
|
32
|
+
end
|
18
33
|
end
|
19
34
|
|
20
35
|
def fn_join(value, workitem)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
first_ = false
|
36
|
+
delim = value[0]
|
37
|
+
v = value[1]
|
38
|
+
#logger.debug "Intrinsic: fn_join value = #{v}"
|
39
|
+
result = ''
|
40
|
+
first_ = true
|
41
|
+
v.each do |token|
|
42
|
+
case token
|
43
|
+
when String
|
44
|
+
result = result + (first_ ? "" : delim) + token
|
45
|
+
when Hash
|
46
|
+
result = result + (first_ ? "" : delim) + intrinsic(token, workitem)
|
34
47
|
end
|
35
|
-
|
48
|
+
first_ = false
|
49
|
+
end
|
50
|
+
result
|
36
51
|
end
|
37
52
|
|
38
53
|
def fn_getatt(array_value, workitem)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
54
|
+
resource = array_value[0]
|
55
|
+
attribute = array_value[1]
|
56
|
+
#logger.debug "Intrinsic: fn_getatt resource= #{resource} attrib = #{attribute} wi[Resource] = #{workitem[resource]}"
|
57
|
+
workitem[resource][attribute]
|
58
|
+
# attributes = array_value[1..-2]
|
59
|
+
# current_resource = workitem[resource]
|
60
|
+
# attributes.each do |a|
|
61
|
+
# current_resource = current_resource[a]
|
62
|
+
# end
|
63
|
+
# current_resource[array_value[-1]]
|
43
64
|
end
|
44
65
|
|
45
66
|
def fn_select(array_value)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
67
|
+
index = array_value[0].to_i #TODO unsafe
|
68
|
+
values = array_value[1]
|
69
|
+
#logger.debug "Intrinsic: fn_select index= #{index} values = #{values.inspect}"
|
70
|
+
values[index]
|
50
71
|
end
|
51
72
|
|
52
73
|
def fn_ref(value, workitem)
|
53
|
-
|
74
|
+
#logger.debug "Intrinsic: fn_ref value = #{value}"
|
75
|
+
if workitem[value]
|
54
76
|
workitem[value]['physical_id'] #TODO only works with Resources not Params
|
77
|
+
else
|
78
|
+
workitem['ResolvedNames'][value]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def fn_lookup(value, workitem)
|
83
|
+
case value
|
84
|
+
when String
|
85
|
+
workitem['ResolvedNames'][value]
|
86
|
+
when Hash
|
87
|
+
workitem['ResolvedNames'][intrinsic(value, workitem)]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def fn_map(value, workitem)
|
92
|
+
#logger.debug "Intrinsic: fn_ref value = #{value}"
|
93
|
+
resolved_keys = []
|
94
|
+
value.each do |k|
|
95
|
+
case k
|
96
|
+
when String
|
97
|
+
resolved_keys.push(k)
|
98
|
+
when Hash
|
99
|
+
resolved_keys.push(intrinsic(k,workitem))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
workitem['Mappings'][resolved_keys[0]][resolved_keys[1]][resolved_keys[2]]
|
55
103
|
end
|
56
104
|
|
105
|
+
def fn_base64(value, workitem)
|
106
|
+
case value
|
107
|
+
when String
|
108
|
+
Base64.urlsafe_encode64(value)
|
109
|
+
when Hash
|
110
|
+
Base64.urlsafe_encode64(intrinsic(value, workitem))
|
111
|
+
end
|
112
|
+
end
|
57
113
|
end
|
58
|
-
end
|
114
|
+
end
|
data/lib/stackmate/logging.rb
CHANGED
@@ -1,28 +1,28 @@
|
|
1
1
|
require 'logger'
|
2
2
|
|
3
3
|
module StackMate
|
4
|
-
module Logging
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module Logging
|
5
|
+
def logger
|
6
|
+
@logger ||= Logging.logger_for(self.class.name)
|
7
|
+
end
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
# Use a hash class-ivar to cache a unique Logger per class:
|
10
|
+
@loggers = {}
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
class << self
|
13
|
+
def logger_for(classname)
|
14
|
+
@loggers[classname] ||= configure_logger_for(classname)
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
def configure_logger_for(classname)
|
18
|
+
logger = Logger.new(STDOUT)
|
19
|
+
logger.progname = classname
|
20
|
+
logger.datetime_format= '%F %T'
|
21
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
22
|
+
"[#{datetime}] #{severity} #{progname} #{msg}\n"
|
23
|
+
end
|
24
|
+
logger
|
23
25
|
end
|
24
|
-
logger
|
25
26
|
end
|
26
27
|
end
|
27
|
-
end
|
28
|
-
end
|
28
|
+
end
|