qc.rb 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.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +4 -0
  4. data/bin/qc +49 -0
  5. data/lib/qc.rb +189 -0
  6. data/lib/qingcloud.com.cert.pem +31 -0
  7. metadata +50 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4580a07ef6b2a0ce936e3044b00dc5d1d6d1b777
4
+ data.tar.gz: 5eb71554537db1e81fef56c5e337dc7a72859c06
5
+ SHA512:
6
+ metadata.gz: 673d374b1847215fea439935597aadde90e9b25b7ac29b347dbc889203752f1a12d22712ef443ca3651c400210859ff17177fa6257a10841afc7ba962bfe1c8c
7
+ data.tar.gz: fa0e2d2b7bdb3dd6133745e72517451a6b73c177d95ea3e8145f88d5ccda80e14a0babd3bcb49928d5b261bd4d2dd961a441af08dc7e2fba31631dfc6e6d1681
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Daniel Bovensiepen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ qc.rb
2
+ =====
3
+
4
+ QingCloud API Library to handle instances, networks, etc. on QingCloud.com
data/bin/qc ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'qc'
4
+
5
+ def help_header cmd, options
6
+ <<HEADER
7
+ qc.rb (Version #{QC::VERSION}) is a QingCloud API CLI
8
+
9
+ qc #{cmd}
10
+
11
+ Options:
12
+ #{options}
13
+ Website:
14
+ https://github.com/qc.rb
15
+ HEADER
16
+ end
17
+
18
+ case ARGV[0]
19
+ when 'ssh'
20
+ case ARGV[1]
21
+ when 'list'
22
+ QC::SSH.each {|s| puts s}
23
+ else
24
+ puts help_header('ssh [option]', <<HELP)
25
+ list List all public keys
26
+ HELP
27
+ end
28
+ when 'ins'
29
+ case ARGV[1]
30
+ when 'list'
31
+ QC::Instance.each {|s| puts s}
32
+ else
33
+ puts help_header('ins [option]', <<HELP)
34
+ list List all machines
35
+ HELP
36
+ end
37
+ else
38
+ puts help_header('[option]', <<HELP)
39
+ ssh SSH Key Management
40
+ img Image Managements (available Operator Systems)
41
+ sec Security Group Management (Firewall rules)
42
+ ip IP Management (Internet Bandwidth)
43
+ route Router Managemen
44
+ net Software Defined Network Management
45
+ vol Volume Management (add, change and remove HDDs)
46
+ ins Instance Management (manipulate machines)
47
+ HELP
48
+ end
49
+
data/lib/qc.rb ADDED
@@ -0,0 +1,189 @@
1
+ require 'openssl'
2
+ require 'Base64'
3
+ require 'cgi'
4
+ require 'json'
5
+ require 'net/http'
6
+ require 'yaml'
7
+ require 'fileutils'
8
+
9
+ module QC
10
+ VERSION = '0.0.1'
11
+
12
+ def QC.load_config key
13
+ f = File.expand_path('~/.qingcloud/config.yaml')
14
+ if File.exists? f
15
+ config = YAML.load(File.open(f))
16
+ if config.has_key? key
17
+ config[key]
18
+ else
19
+ raise "'#{key}' is missing in configuration file"
20
+ end
21
+ else
22
+ puts "'#{f}' doesn't exist!"
23
+ print "Do you want to create it? (Y/n)"
24
+ a = $stdin.gets.strip
25
+ if a == 'n'
26
+ puts "No configuration file!"
27
+ exit
28
+ elsif a.downcase == 'y' or a == ''
29
+ h = {}
30
+ print 'Secret Key:'
31
+ h['qy_secret_access_key'] = $stdin.gets.strip
32
+ print 'Access Key ID:'
33
+ h['qy_access_key_id'] = $stdin.gets.strip
34
+ print 'Zone:'
35
+ h['zone'] = $stdin.gets.strip
36
+ begin
37
+ FileUtils.mkdir_p(File.dirname(f))
38
+ File.new(f, 'w+').puts YAML.dump(h)
39
+ puts "Configuration file was created!"
40
+ rescue Exception => e
41
+ raise "Configuration file couldn't be created! (#{e.class}: #{e.message})"
42
+ end
43
+ exit
44
+ end
45
+ end
46
+ end
47
+
48
+ QC::CERT_FILE = File.open(File.join(File.dirname(__FILE__), "qingcloud.com.cert.pem")).readlines.join
49
+ QC::Key = QC.load_config('qy_secret_access_key')
50
+ QC::AccessKeyId = QC.load_config('qy_access_key_id')
51
+ QC::Zone = QC.load_config('zone')
52
+
53
+ def QC.hmac key, data
54
+ hmac = OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, data)
55
+ b64_hmac = Base64.encode64(hmac).strip
56
+ url_b64_hmac = CGI.escape(b64_hmac)
57
+ end
58
+
59
+ class SSH
60
+ def initialize s
61
+ @id = s['keypair_id']
62
+ @name = s['keypair_name']
63
+ @date = s['create_time']
64
+ @e_method = s['encrypt_method']
65
+ @desc = s['description']
66
+ @key = s['pub_key']
67
+ end
68
+
69
+ def to_s
70
+ <<STR
71
+ ID: "#{@id}"
72
+ Name: "#{@name}"
73
+ Creation Date: "#{@date}"
74
+ Encryption Method: "#{@e_method}"
75
+ Description: "#{@desc}"
76
+ Public Key:
77
+ "#{@key}"
78
+ STR
79
+ end
80
+
81
+ def SSH.each &block
82
+ r = QC::API::Request.new 'DescribeKeyPairs'
83
+ r.execute!(QC::Key)['keypair_set'].to_a.each {|s| block.call(SSH.new(s))}
84
+ end
85
+ end
86
+
87
+ class Instance
88
+ def initialize s
89
+ @id = s['instance_id']
90
+ @name = s['instance_name']
91
+ @type = s['instance_type']
92
+ @vcpu = s['vcpu_current']
93
+ @desc = s['description']
94
+ @status = s['status']
95
+ end
96
+
97
+ def to_s
98
+ <<STR
99
+ ID: "#{@id}"
100
+ Name: "#{@name}"
101
+ Type: "#{@type}"
102
+ VCPU: "#{@vcpu}"
103
+ Description: "#{@desc}"
104
+ Status: "#{@status}"
105
+ STR
106
+ end
107
+
108
+ def Instance.each &block
109
+ r = QC::API::Request.new 'DescribeInstances'
110
+ r.execute!(QC::Key)['instance_set'].to_a.each {|s| block.call(Instance.new(s))}
111
+ end
112
+ end
113
+
114
+ module API
115
+ class Request
116
+ attr_reader :response
117
+
118
+ def initialize action, extra_params = []
119
+ @response = :not_requested
120
+
121
+ @params = []
122
+ @params << ['action', action]
123
+ @params << ['access_key_id', QC::AccessKeyId]
124
+ @params << ['signature_method', 'HmacSHA256']
125
+ @params << ['signature_version', 1]
126
+ @params << ['zone', QC::Zone]
127
+ extra_params.each {|i| @params << i}
128
+ end
129
+
130
+ def execute!(key)
131
+ _p = @params.dup
132
+ _p << ['time_stamp', Time.now.utc.strftime("%FT%TZ")]
133
+ @uri = URI.parse(API.json2url(key, _p.to_json))
134
+
135
+ # Establish a SSL connection
136
+ Net::HTTP.start(@uri.host, 443,
137
+ :use_ssl => true,
138
+ :verify_mode => OpenSSL::SSL::VERIFY_PEER) do |https|
139
+
140
+ # Verify additional the host name in the certificate to avoid MITM
141
+ unless OpenSSL::SSL.verify_certificate_identity(https.peer_cert, 'qingcloud.com')
142
+ raise 'Hostname in certifcate does NOT match! (MITM?)'
143
+ end
144
+
145
+ # Verify the individual certificate
146
+ unless https.peer_cert.to_s == QC::CERT_FILE
147
+ raise "Certificate is NOT trustworthy!"
148
+ end
149
+
150
+ ####################################################################
151
+ # Starting from this point I consider the SSL connection as safe!
152
+
153
+ @response = https.request(Net::HTTP::Get.new(@uri.request_uri))
154
+
155
+ # After this point we close the SSL connection.
156
+ ####################################################################
157
+ end
158
+
159
+ JSON.parse(@response.body)
160
+ end
161
+ end
162
+
163
+ def API.sort_json json
164
+ JSON.parse(json).to_a.sort.to_s
165
+ end
166
+
167
+ def API.json2reqstr json
168
+ "GET\n/iaas/\n" + json2params(json)
169
+ end
170
+
171
+ def API.json2sign key, json
172
+ QC.hmac(key, json2reqstr(sort_json(json)))
173
+ end
174
+
175
+ def API.json2url key, json
176
+ sign = json2sign(key, json)
177
+ params = json2params(json)
178
+ "https://api.qingcloud.com/iaas/?#{params}&signature=#{sign}"
179
+ end
180
+
181
+ private
182
+
183
+ def API.json2params json
184
+ JSON.parse(json).to_a.map do |i|
185
+ "#{CGI.escape(i[0])}=#{CGI.escape(i[1].to_s)}"
186
+ end.join('&')
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,31 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIFRzCCBC+gAwIBAgIHSyW9yrLTdzANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
3
+ BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAY
4
+ BgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydGlm
5
+ aWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkxMDAuBgNVBAMTJ0dvIERhZGR5
6
+ IFNlY3VyZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTERMA8GA1UEBRMIMDc5Njky
7
+ ODcwHhcNMTMwMzE5MDUxMTIxWhcNMTUwMzE5MDUxMTIxWjA9MSEwHwYDVQQLExhE
8
+ b21haW4gQ29udHJvbCBWYWxpZGF0ZWQxGDAWBgNVBAMMDyoucWluZ2Nsb3VkLmNv
9
+ bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALwYkwKcaVEk//LDRyNo
10
+ OZUNJbSFRmtaa+gVbKu1nGHhhmmyKaToV6n3yAUY4sth+js03FARcV72hkFhB4Pw
11
+ ZEyXJyHkumtJu0mQWDzo+U2nqjQJFpEbWyPC/H+KW/8XJVmBZiufW6Sde7+ypiOg
12
+ Pwv8Ch47tgZo/7Z7apHX9UzZt8O8Ml58xSmEucZzKxy60SK+hjtuFHWL5T/WzBQn
13
+ hB+geK9OSCv5Woi/hLvQaWan9xWF1NQjz9k5OzbcB04SNhRlm5Y8y3ypHxgdHSzm
14
+ AI9vabPSTbjNG084Llbe3VGdwx3gdbMD4wHhTbhFPhVV2UzGR3zRUPwcatq2wxep
15
+ mXsCAwEAAaOCAbwwggG4MA8GA1UdEwEB/wQFMAMBAQAwHQYDVR0lBBYwFAYIKwYB
16
+ BQUHAwEGCCsGAQUFBwMCMA4GA1UdDwEB/wQEAwIFoDAzBgNVHR8ELDAqMCigJqAk
17
+ hiJodHRwOi8vY3JsLmdvZGFkZHkuY29tL2dkczEtODcuY3JsMFMGA1UdIARMMEow
18
+ SAYLYIZIAYb9bQEHFwEwOTA3BggrBgEFBQcCARYraHR0cDovL2NlcnRpZmljYXRl
19
+ cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzCBgAYIKwYBBQUHAQEEdDByMCQGCCsG
20
+ AQUFBzABhhhodHRwOi8vb2NzcC5nb2RhZGR5LmNvbS8wSgYIKwYBBQUHMAKGPmh0
21
+ dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeS9nZF9pbnRl
22
+ cm1lZGlhdGUuY3J0MB8GA1UdIwQYMBaAFP2sYTKTbEXW4u6FX5q653aZaMznMCkG
23
+ A1UdEQQiMCCCDyoucWluZ2Nsb3VkLmNvbYINcWluZ2Nsb3VkLmNvbTAdBgNVHQ4E
24
+ FgQUnyjKq4LLVT955jb3wyzLNgFETewwDQYJKoZIhvcNAQEFBQADggEBAB5FSJjq
25
+ Zue7+jt/1rYf6pbGhpZEhVRXNq662/d5KRlSVyd0Z0LK8OUdt8JR/cQt8XAyMYii
26
+ AUXAqklQcK0fphkcPznLIMUab9QWkaUSajOf/rUh23sxNZrgKYW8Z0H8MevCWISO
27
+ +O6g9S/fzjtG7xkrDh1gBxFHCfai3st3kphyirbTkoMZN/tXEiZzYsK3nh+c7F6E
28
+ HgjZmdVMeSodJpdj7bOULntqJIau1NrTzQxGGJDLtqf8lTMiHotZ8VfN9wDPBqfs
29
+ 0jFV1vvczYwIQaEr+1iZXxgZC2itR4hbX3D3JN7WnPWJp+8LJpwkZeCXDL4t2GK8
30
+ G4/6MpCRBK936qU=
31
+ -----END CERTIFICATE-----
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: qc.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Bovensiepen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-15 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: QingCloud API Library to handle instances, networks, internetconnections,
14
+ etc. on QingCloud.com
15
+ email: daniel@bovensiepen.net
16
+ executables:
17
+ - qc
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - bin/qc
22
+ - lib/qc.rb
23
+ - lib/qingcloud.com.cert.pem
24
+ - LICENSE
25
+ - README.md
26
+ homepage: https://github.com/bovi/qc.rb
27
+ licenses:
28
+ - MIT
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 2.0.3
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: QingCloud API Library
50
+ test_files: []