saklient 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.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +43 -0
- data/Rakefile +1 -0
- data/config.sh.sample +5 -0
- data/example.rb +83 -0
- data/example.sh +6 -0
- data/lib/saklient/cloud/api.rb +382 -0
- data/lib/saklient/cloud/client.rb +111 -0
- data/lib/saklient/cloud/enums/eappliance_class.rb +39 -0
- data/lib/saklient/cloud/enums/eavailability.rb +79 -0
- data/lib/saklient/cloud/enums/edisk_connection.rb +39 -0
- data/lib/saklient/cloud/enums/escope.rb +39 -0
- data/lib/saklient/cloud/enums/escript_class.rb +39 -0
- data/lib/saklient/cloud/enums/eserver_instance_status.rb +44 -0
- data/lib/saklient/cloud/enums/estorage_class.rb +34 -0
- data/lib/saklient/cloud/errors/access_api_key_disabled_exception.rb +23 -0
- data/lib/saklient/cloud/errors/access_sakura_exception.rb +23 -0
- data/lib/saklient/cloud/errors/access_staff_exception.rb +23 -0
- data/lib/saklient/cloud/errors/access_token_exception.rb +23 -0
- data/lib/saklient/cloud/errors/access_xhr_or_api_key_exception.rb +23 -0
- data/lib/saklient/cloud/errors/account_not_found_exception.rb +23 -0
- data/lib/saklient/cloud/errors/account_not_specified_exception.rb +23 -0
- data/lib/saklient/cloud/errors/ambiguous_identifier_exception.rb +23 -0
- data/lib/saklient/cloud/errors/ambiguous_zone_exception.rb +23 -0
- data/lib/saklient/cloud/errors/api_proxy_timeout_exception.rb +23 -0
- data/lib/saklient/cloud/errors/api_proxy_timeout_non_get_exception.rb +23 -0
- data/lib/saklient/cloud/errors/archive_is_incomplete_exception.rb +23 -0
- data/lib/saklient/cloud/errors/boot_failure_by_lock_exception.rb +23 -0
- data/lib/saklient/cloud/errors/boot_failure_in_group_exception.rb +23 -0
- data/lib/saklient/cloud/errors/busy_exception.rb +23 -0
- data/lib/saklient/cloud/errors/cant_resize_smaller_exception.rb +23 -0
- data/lib/saklient/cloud/errors/cdrom_device_locked_exception.rb +23 -0
- data/lib/saklient/cloud/errors/cdrom_disabled_exception.rb +23 -0
- data/lib/saklient/cloud/errors/cdrom_in_use_exception.rb +23 -0
- data/lib/saklient/cloud/errors/cdrom_is_incomplete_exception.rb +23 -0
- data/lib/saklient/cloud/errors/connect_to_same_switch_exception.rb +23 -0
- data/lib/saklient/cloud/errors/contract_creation_exception.rb +23 -0
- data/lib/saklient/cloud/errors/copy_to_itself_exception.rb +23 -0
- data/lib/saklient/cloud/errors/delete_disk_b4_template_exception.rb +23 -0
- data/lib/saklient/cloud/errors/delete_ip_v6_nets_first_exception.rb +23 -0
- data/lib/saklient/cloud/errors/delete_res_b4_account_exception.rb +23 -0
- data/lib/saklient/cloud/errors/delete_router_b4_switch_exception.rb +23 -0
- data/lib/saklient/cloud/errors/delete_static_route_first_exception.rb +23 -0
- data/lib/saklient/cloud/errors/disabled_in_sandbox_exception.rb +23 -0
- data/lib/saklient/cloud/errors/disconnect_b4_delete_exception.rb +23 -0
- data/lib/saklient/cloud/errors/disconnect_b4_update_exception.rb +23 -0
- data/lib/saklient/cloud/errors/disk_connection_limit_exception.rb +23 -0
- data/lib/saklient/cloud/errors/disk_is_copying_exception.rb +23 -0
- data/lib/saklient/cloud/errors/disk_is_not_available_exception.rb +23 -0
- data/lib/saklient/cloud/errors/disk_license_mismatch_exception.rb +23 -0
- data/lib/saklient/cloud/errors/disk_or_ss_in_migration_exception.rb +23 -0
- data/lib/saklient/cloud/errors/disk_stock_run_out_exception.rb +23 -0
- data/lib/saklient/cloud/errors/dns_aaaa_record_not_found_exception.rb +23 -0
- data/lib/saklient/cloud/errors/dns_arecord_not_found_exception.rb +23 -0
- data/lib/saklient/cloud/errors/dns_ptr_update_failure_exception.rb +23 -0
- data/lib/saklient/cloud/errors/dont_create_in_sandbox_exception.rb +23 -0
- data/lib/saklient/cloud/errors/duplicate_account_code_exception.rb +23 -0
- data/lib/saklient/cloud/errors/duplicate_entry_exception.rb +23 -0
- data/lib/saklient/cloud/errors/duplicate_user_code_exception.rb +23 -0
- data/lib/saklient/cloud/errors/file_not_uploaded_exception.rb +23 -0
- data/lib/saklient/cloud/errors/filter_array_comparison_exception.rb +23 -0
- data/lib/saklient/cloud/errors/filter_bad_operator_exception.rb +23 -0
- data/lib/saklient/cloud/errors/filter_null_comparison_exception.rb +23 -0
- data/lib/saklient/cloud/errors/filter_unknown_operator_exception.rb +23 -0
- data/lib/saklient/cloud/errors/ftp_cannot_close_exception.rb +23 -0
- data/lib/saklient/cloud/errors/ftp_is_already_close_exception.rb +23 -0
- data/lib/saklient/cloud/errors/ftp_is_already_open_exception.rb +23 -0
- data/lib/saklient/cloud/errors/ftp_must_be_closed_exception.rb +23 -0
- data/lib/saklient/cloud/errors/host_operation_failure_exception.rb +23 -0
- data/lib/saklient/cloud/errors/illegal_das_usage_exception.rb +23 -0
- data/lib/saklient/cloud/errors/in_migration_exception.rb +23 -0
- data/lib/saklient/cloud/errors/invalid_format_exception.rb +23 -0
- data/lib/saklient/cloud/errors/invalid_param_comb_exception.rb +23 -0
- data/lib/saklient/cloud/errors/invalid_range_exception.rb +23 -0
- data/lib/saklient/cloud/errors/invalid_uri_argument_exception.rb +23 -0
- data/lib/saklient/cloud/errors/ip_v6_net_already_attached_exception.rb +23 -0
- data/lib/saklient/cloud/errors/limit_count_in_account_exception.rb +23 -0
- data/lib/saklient/cloud/errors/limit_count_in_member_exception.rb +23 -0
- data/lib/saklient/cloud/errors/limit_count_in_network_exception.rb +23 -0
- data/lib/saklient/cloud/errors/limit_count_in_router_exception.rb +23 -0
- data/lib/saklient/cloud/errors/limit_count_in_zone_exception.rb +23 -0
- data/lib/saklient/cloud/errors/limit_memory_in_account_exception.rb +23 -0
- data/lib/saklient/cloud/errors/limit_size_in_account_exception.rb +23 -0
- data/lib/saklient/cloud/errors/missing_iso_image_exception.rb +23 -0
- data/lib/saklient/cloud/errors/missing_param_exception.rb +23 -0
- data/lib/saklient/cloud/errors/must_be_of_same_zone_exception.rb +23 -0
- data/lib/saklient/cloud/errors/no_display_response_exception.rb +23 -0
- data/lib/saklient/cloud/errors/not_for_router_exception.rb +23 -0
- data/lib/saklient/cloud/errors/not_replicating_exception.rb +23 -0
- data/lib/saklient/cloud/errors/not_with_hybridconn_exception.rb +23 -0
- data/lib/saklient/cloud/errors/old_storage_plan_exception.rb +23 -0
- data/lib/saklient/cloud/errors/operation_failure_exception.rb +23 -0
- data/lib/saklient/cloud/errors/operation_timeout_exception.rb +23 -0
- data/lib/saklient/cloud/errors/original_hash_mismatch_exception.rb +23 -0
- data/lib/saklient/cloud/errors/packet_filter_applying_exception.rb +23 -0
- data/lib/saklient/cloud/errors/packet_filter_version_mismatch_exception.rb +23 -0
- data/lib/saklient/cloud/errors/param_ip_not_found_exception.rb +23 -0
- data/lib/saklient/cloud/errors/param_res_not_found_exception.rb +23 -0
- data/lib/saklient/cloud/errors/payment_credit_card_exception.rb +23 -0
- data/lib/saklient/cloud/errors/payment_payment_exception.rb +23 -0
- data/lib/saklient/cloud/errors/payment_registration_exception.rb +23 -0
- data/lib/saklient/cloud/errors/payment_tel_certification_exception.rb +23 -0
- data/lib/saklient/cloud/errors/payment_unpayable_exception.rb +23 -0
- data/lib/saklient/cloud/errors/penalty_operation_exception.rb +23 -0
- data/lib/saklient/cloud/errors/replica_already_exists_exception.rb +23 -0
- data/lib/saklient/cloud/errors/replica_not_found_exception.rb +23 -0
- data/lib/saklient/cloud/errors/res_already_connected_exception.rb +23 -0
- data/lib/saklient/cloud/errors/res_already_disconnected_exception.rb +23 -0
- data/lib/saklient/cloud/errors/res_already_exists_exception.rb +23 -0
- data/lib/saklient/cloud/errors/res_used_in_zone_exception.rb +23 -0
- data/lib/saklient/cloud/errors/resource_path_not_found_exception.rb +23 -0
- data/lib/saklient/cloud/errors/run_out_of_ip_address_exception.rb +23 -0
- data/lib/saklient/cloud/errors/same_license_required_exception.rb +23 -0
- data/lib/saklient/cloud/errors/server_could_not_stop_exception.rb +23 -0
- data/lib/saklient/cloud/errors/server_is_cleaning_exception.rb +23 -0
- data/lib/saklient/cloud/errors/server_operation_failure_exception.rb +23 -0
- data/lib/saklient/cloud/errors/server_power_must_be_down_exception.rb +23 -0
- data/lib/saklient/cloud/errors/server_power_must_be_up_exception.rb +23 -0
- data/lib/saklient/cloud/errors/service_temporarily_unavailable_exception.rb +23 -0
- data/lib/saklient/cloud/errors/size_mismatch_exception.rb +23 -0
- data/lib/saklient/cloud/errors/snapshot_in_migration_exception.rb +23 -0
- data/lib/saklient/cloud/errors/still_creating_exception.rb +23 -0
- data/lib/saklient/cloud/errors/storage_abnormal_exception.rb +23 -0
- data/lib/saklient/cloud/errors/storage_operation_failure_exception.rb +23 -0
- data/lib/saklient/cloud/errors/switch_hybrid_connected_exception.rb +23 -0
- data/lib/saklient/cloud/errors/template_ftp_is_open_exception.rb +23 -0
- data/lib/saklient/cloud/errors/template_is_incomplete_exception.rb +23 -0
- data/lib/saklient/cloud/errors/too_many_request_exception.rb +23 -0
- data/lib/saklient/cloud/errors/unknown_exception.rb +23 -0
- data/lib/saklient/cloud/errors/unknown_os_type_exception.rb +23 -0
- data/lib/saklient/cloud/errors/unsupported_res_class_exception.rb +23 -0
- data/lib/saklient/cloud/errors/user_not_specified_exception.rb +23 -0
- data/lib/saklient/cloud/errors/vnc_proxy_request_failure_exception.rb +23 -0
- data/lib/saklient/cloud/models/model.rb +348 -0
- data/lib/saklient/cloud/models/model_appliance.rb +161 -0
- data/lib/saklient/cloud/models/model_archive.rb +207 -0
- data/lib/saklient/cloud/models/model_disk.rb +200 -0
- data/lib/saklient/cloud/models/model_disk_plan.rb +156 -0
- data/lib/saklient/cloud/models/model_icon.rb +178 -0
- data/lib/saklient/cloud/models/model_iface.rb +115 -0
- data/lib/saklient/cloud/models/model_ipv6_net.rb +106 -0
- data/lib/saklient/cloud/models/model_iso_image.rb +207 -0
- data/lib/saklient/cloud/models/model_router.rb +148 -0
- data/lib/saklient/cloud/models/model_router_plan.rb +106 -0
- data/lib/saklient/cloud/models/model_script.rb +178 -0
- data/lib/saklient/cloud/models/model_server.rb +237 -0
- data/lib/saklient/cloud/models/model_server_plan.rb +119 -0
- data/lib/saklient/cloud/models/model_swytch.rb +138 -0
- data/lib/saklient/cloud/models/query_params.rb +44 -0
- data/lib/saklient/cloud/product.rb +95 -0
- data/lib/saklient/cloud/resources/appliance.rb +551 -0
- data/lib/saklient/cloud/resources/archive.rb +861 -0
- data/lib/saklient/cloud/resources/disk.rb +802 -0
- data/lib/saklient/cloud/resources/disk_config.rb +353 -0
- data/lib/saklient/cloud/resources/disk_plan.rb +194 -0
- data/lib/saklient/cloud/resources/ftp_info.rb +93 -0
- data/lib/saklient/cloud/resources/icon.rb +268 -0
- data/lib/saklient/cloud/resources/iface.rb +329 -0
- data/lib/saklient/cloud/resources/ipv4_net.rb +275 -0
- data/lib/saklient/cloud/resources/ipv6_net.rb +238 -0
- data/lib/saklient/cloud/resources/iso_image.rb +666 -0
- data/lib/saklient/cloud/resources/resource.rb +287 -0
- data/lib/saklient/cloud/resources/router.rb +508 -0
- data/lib/saklient/cloud/resources/router_plan.rb +230 -0
- data/lib/saklient/cloud/resources/script.rb +554 -0
- data/lib/saklient/cloud/resources/server.rb +730 -0
- data/lib/saklient/cloud/resources/server_instance.rb +217 -0
- data/lib/saklient/cloud/resources/server_plan.rb +285 -0
- data/lib/saklient/cloud/resources/swytch.rb +521 -0
- data/lib/saklient/errors/exception_factory.rb +532 -0
- data/lib/saklient/errors/http_bad_gateway_exception.rb +21 -0
- data/lib/saklient/errors/http_bad_request_exception.rb +21 -0
- data/lib/saklient/errors/http_conflict_exception.rb +21 -0
- data/lib/saklient/errors/http_exception.rb +27 -0
- data/lib/saklient/errors/http_expectation_failed_exception.rb +21 -0
- data/lib/saklient/errors/http_failed_dependency_exception.rb +21 -0
- data/lib/saklient/errors/http_forbidden_exception.rb +21 -0
- data/lib/saklient/errors/http_gateway_timeout_exception.rb +21 -0
- data/lib/saklient/errors/http_gone_exception.rb +21 -0
- data/lib/saklient/errors/http_http_version_not_supported_exception.rb +21 -0
- data/lib/saklient/errors/http_insufficient_storage_exception.rb +21 -0
- data/lib/saklient/errors/http_internal_server_error_exception.rb +21 -0
- data/lib/saklient/errors/http_length_required_exception.rb +21 -0
- data/lib/saklient/errors/http_locked_exception.rb +21 -0
- data/lib/saklient/errors/http_method_not_allowed_exception.rb +21 -0
- data/lib/saklient/errors/http_not_acceptable_exception.rb +21 -0
- data/lib/saklient/errors/http_not_extended_exception.rb +21 -0
- data/lib/saklient/errors/http_not_found_exception.rb +21 -0
- data/lib/saklient/errors/http_not_implemented_exception.rb +21 -0
- data/lib/saklient/errors/http_payment_required_exception.rb +21 -0
- data/lib/saklient/errors/http_precondition_failed_exception.rb +21 -0
- data/lib/saklient/errors/http_proxy_authentication_required_exception.rb +21 -0
- data/lib/saklient/errors/http_request_entity_too_large_exception.rb +21 -0
- data/lib/saklient/errors/http_request_timeout_exception.rb +21 -0
- data/lib/saklient/errors/http_request_uri_too_long_exception.rb +21 -0
- data/lib/saklient/errors/http_requested_range_not_satisfiable_exception.rb +21 -0
- data/lib/saklient/errors/http_service_unavailable_exception.rb +21 -0
- data/lib/saklient/errors/http_unauthorized_exception.rb +21 -0
- data/lib/saklient/errors/http_unprocessable_entity_exception.rb +21 -0
- data/lib/saklient/errors/http_unsupported_media_type_exception.rb +21 -0
- data/lib/saklient/errors/http_upgrade_required_exception.rb +21 -0
- data/lib/saklient/errors/http_variant_also_negotiates_exception.rb +21 -0
- data/lib/saklient/errors/saklient_exception.rb +22 -0
- data/lib/saklient/util.rb +121 -0
- data/lib/saklient/version.rb +5 -0
- data/saklient.gemspec +24 -0
- data/spec/archive_spec.rb +141 -0
- data/spec/enum_spec.rb +24 -0
- data/spec/exception_spec.rb +25 -0
- data/spec/iso_image_spec.rb +172 -0
- data/spec/router_spec.rb +124 -0
- data/spec/server_spec.rb +261 -0
- data/spec/util_spec.rb +72 -0
- data/test-sshkey.txt +27 -0
- metadata +281 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
# -*- encoding: UTF-8 -*-
|
2
|
+
|
3
|
+
require_relative 'errors/saklient_exception'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Saklient
|
7
|
+
|
8
|
+
# @private
|
9
|
+
class Util
|
10
|
+
|
11
|
+
# @param [any] obj
|
12
|
+
# @param [String] path
|
13
|
+
# @return [any]
|
14
|
+
def self.exists_path(obj, path)
|
15
|
+
aPath = path.split('.')
|
16
|
+
for seg in aPath
|
17
|
+
return false if (obj).nil?
|
18
|
+
return false if !obj.is_a?(Hash)
|
19
|
+
next if seg == ''
|
20
|
+
return false if !(!obj.nil? && obj.key?(seg.to_sym))
|
21
|
+
obj = obj[seg.to_sym]
|
22
|
+
end
|
23
|
+
return true
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [any] obj
|
27
|
+
# @param [String] path
|
28
|
+
# @return [any]
|
29
|
+
def self.get_by_path(obj, path)
|
30
|
+
aPath = path.split('.')
|
31
|
+
for seg in aPath
|
32
|
+
return nil if (obj).nil?
|
33
|
+
return nil if !obj.is_a?(Hash)
|
34
|
+
next if seg == ''
|
35
|
+
return nil if !(!obj.nil? && obj.key?(seg.to_sym))
|
36
|
+
obj = obj[seg.to_sym]
|
37
|
+
end
|
38
|
+
return obj
|
39
|
+
end
|
40
|
+
|
41
|
+
# @todo array support
|
42
|
+
# @todo overwriting
|
43
|
+
# @todo writing into children of non-object
|
44
|
+
# @param [any] obj
|
45
|
+
# @param [any] value
|
46
|
+
# @param [String] path
|
47
|
+
# @return [void]
|
48
|
+
def self.set_by_path(obj, path, value)
|
49
|
+
aPath = path.split('.')
|
50
|
+
key = aPath.pop
|
51
|
+
for seg in aPath
|
52
|
+
next if seg == ''
|
53
|
+
obj[seg.to_sym] = {} if !(!obj.nil? && obj.key?(seg.to_sym))
|
54
|
+
obj = obj[seg.to_sym]
|
55
|
+
end
|
56
|
+
obj[key.to_sym] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param [String] classPath
|
60
|
+
# @param [Array] args
|
61
|
+
# @return [any]
|
62
|
+
def self.create_class_instance(classPath, args)
|
63
|
+
ret = nil
|
64
|
+
if !(classPath).nil?
|
65
|
+
apath = classPath.split('.')
|
66
|
+
ret = apath.inject(Object) { |obj, name| obj.const_get(name[0].upcase + name[1..-1]) }.new(*args)
|
67
|
+
end
|
68
|
+
raise Exception.new('Could not create class instance of ' + classPath) if (ret).nil?
|
69
|
+
return ret
|
70
|
+
end
|
71
|
+
|
72
|
+
# @param [String] s
|
73
|
+
# @return [NativeDate]
|
74
|
+
def self.str2date(s)
|
75
|
+
return nil if (s).nil?
|
76
|
+
return DateTime.parse(s)
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param [NativeDate] d
|
80
|
+
# @return [String]
|
81
|
+
def self.date2str(d)
|
82
|
+
return nil if (d).nil?
|
83
|
+
return d.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
# @param [String] s
|
87
|
+
# @return [String]
|
88
|
+
def self.url_encode(s)
|
89
|
+
return URI.encode(s)
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param [Integer] sec
|
93
|
+
# @return [void]
|
94
|
+
def self.sleep(sec)
|
95
|
+
super self
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param [any] value
|
99
|
+
# @param [String] typeName
|
100
|
+
# @return [void]
|
101
|
+
def self.validate_type(value, typeName, force=false)
|
102
|
+
isOk = false
|
103
|
+
if !force then
|
104
|
+
return if typeName=="any" || typeName=="void" || value.nil?
|
105
|
+
clazz = value.class.to_s
|
106
|
+
if typeName=="bool"
|
107
|
+
isOk = clazz=="TrueClass" || clazz=="FalseClass"
|
108
|
+
elsif typeName=="Float"
|
109
|
+
isOk = clazz=="Fixnum" || clazz=="Float"
|
110
|
+
elsif typeName=="String"
|
111
|
+
isOk = clazz=="Fixnum" || clazz=="Float" || clazz=="String"
|
112
|
+
else
|
113
|
+
isOk = value.is_a?(Object.const_get(typeName))
|
114
|
+
end
|
115
|
+
end
|
116
|
+
raise Saklient::Errors::SaklientException.new('argument_type_mismatch', 'Argument type mismatch (expected '+typeName+' but got '+clazz+')') unless isOk
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
data/saklient.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'saklient/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
|
8
|
+
gem.name = 'saklient'
|
9
|
+
gem.version = Saklient::VERSION
|
10
|
+
gem.licenses = ['MIT']
|
11
|
+
gem.authors = ['townewgokgok']
|
12
|
+
gem.email = ['dev-support-ml@sakura.ad.jp']
|
13
|
+
gem.description = %q{An easy interface to control your resources on SAKURA Cloud.}
|
14
|
+
gem.summary = %q{SAKURA Internet API Client Library for Ruby}
|
15
|
+
gem.homepage = 'http://sakura-internet.github.io/saklient.doc/'
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ['lib']
|
21
|
+
|
22
|
+
gem.add_development_dependency 'rspec'
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
$: << File.dirname(__dir__) + '/lib'
|
2
|
+
require 'saklient/cloud/api'
|
3
|
+
require 'date'
|
4
|
+
require 'SecureRandom'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
describe 'Archive' do
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
before do
|
12
|
+
|
13
|
+
# load config file
|
14
|
+
root = File.dirname(__dir__)
|
15
|
+
test_ok_file = root + '/testok'
|
16
|
+
expect(File).to exist(test_ok_file)
|
17
|
+
config_file = root + '/config.sh'
|
18
|
+
expect(File).to exist(config_file)
|
19
|
+
@config = {}
|
20
|
+
fh = open(config_file)
|
21
|
+
fh.each {|line|
|
22
|
+
if /^\s*export\s+(\w+)\s*=\s*(.+?)\s*$/.match(line) then
|
23
|
+
key = $1
|
24
|
+
value = $2
|
25
|
+
@config[key.to_sym] = value.gsub(/'([^']*)'|"([^"]*)"|\\(.)|(.)/) {|m|
|
26
|
+
$1 || $2 || $3 || $4
|
27
|
+
}
|
28
|
+
end
|
29
|
+
}
|
30
|
+
fh.close
|
31
|
+
expect(@config[:SACLOUD_TOKEN]).not_to be_empty #'SACLOUD_TOKEN must be defined in config.sh'
|
32
|
+
expect(@config[:SACLOUD_SECRET]).not_to be_empty #'SACLOUD_SECRET must be defined in config.sh'
|
33
|
+
#expect(@config[:SACLOUD_ZONE]).not_to be_empty #'SACLOUD_ZONE must be defined in config.sh'
|
34
|
+
|
35
|
+
# authorize
|
36
|
+
@api = Saklient::Cloud::API::authorize(@config[:SACLOUD_TOKEN], @config[:SACLOUD_SECRET])
|
37
|
+
@api = @api.in_zone(@config[:SACLOUD_ZONE]) if @config[:SACLOUD_ZONE]
|
38
|
+
expect(@api).to be_an_instance_of Saklient::Cloud::API
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
it 'should be CRUDed' do
|
45
|
+
name = '!ruby_rspec-' + DateTime.now.strftime('%Y%m%d_%H%M%S') + '-' + SecureRandom.uuid[0, 8]
|
46
|
+
description = 'This instance was created by saklient.ruby rspec'
|
47
|
+
tag = 'saklient-test'
|
48
|
+
|
49
|
+
archive = @api.archive.create
|
50
|
+
expect(archive).to be_an_instance_of Saklient::Cloud::Resources::Archive
|
51
|
+
archive.name = name
|
52
|
+
archive.description = description
|
53
|
+
archive.tags = [tag]
|
54
|
+
archive.size_gib = 20
|
55
|
+
archive.save
|
56
|
+
|
57
|
+
#
|
58
|
+
ftp = archive.ftp_info
|
59
|
+
expect(ftp).to be_an_instance_of Saklient::Cloud::Resources::FtpInfo
|
60
|
+
expect(ftp.host_name).not_to be_nil
|
61
|
+
expect(ftp.user).not_to be_nil
|
62
|
+
expect(ftp.password).not_to be_nil
|
63
|
+
ftp2 = archive.open_ftp(true).ftp_info
|
64
|
+
expect(ftp2).to be_an_instance_of Saklient::Cloud::Resources::FtpInfo
|
65
|
+
expect(ftp2.host_name).not_to be_nil
|
66
|
+
expect(ftp2.user).not_to be_nil
|
67
|
+
expect(ftp2.password).not_to be_nil
|
68
|
+
expect(ftp2.password).not_to eq ftp.password
|
69
|
+
|
70
|
+
#
|
71
|
+
temp = Tempfile.new('saklient-')
|
72
|
+
path = temp.path
|
73
|
+
temp.close!
|
74
|
+
cmd = "dd if=/dev/urandom bs=4096 count=64 > #{path}; ls -l #{path}"
|
75
|
+
puts cmd
|
76
|
+
puts `( #{cmd} ) 2>&1`
|
77
|
+
cmd = "set ftp:ssl-allow true;"
|
78
|
+
cmd += "set ftp:ssl-force true;"
|
79
|
+
cmd += "set ftp:ssl-protect-data true;"
|
80
|
+
cmd += "set ftp:ssl-protect-list true;"
|
81
|
+
cmd += "put #{path};"
|
82
|
+
cmd += "exit"
|
83
|
+
cmd = "lftp -u #{ftp2.user},#{ftp2.password} -p 21 -e '#{cmd}' #{ftp2.host_name}"
|
84
|
+
puts cmd
|
85
|
+
puts `( #{cmd} ) 2>&1`
|
86
|
+
cmd = "rm -f #{path}"
|
87
|
+
puts cmd
|
88
|
+
puts `( #{cmd} ) 2>&1`
|
89
|
+
|
90
|
+
archive.close_ftp
|
91
|
+
|
92
|
+
#
|
93
|
+
archive.destroy
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
it 'should be copied' do
|
100
|
+
name = '!ruby_rspec-' + DateTime.now.strftime('%Y%m%d_%H%M%S') + '-' + SecureRandom.uuid[0, 8]
|
101
|
+
description = 'This instance was created by saklient.ruby rspec'
|
102
|
+
tag = 'saklient-test'
|
103
|
+
|
104
|
+
disk = @api.disk.create
|
105
|
+
disk.name = name
|
106
|
+
disk.description = description
|
107
|
+
disk.tags = [tag]
|
108
|
+
disk.size_gib = 20
|
109
|
+
disk.save
|
110
|
+
|
111
|
+
archive = @api.archive.create
|
112
|
+
archive.name = name
|
113
|
+
archive.description = description
|
114
|
+
archive.tags = [tag]
|
115
|
+
archive.source = disk
|
116
|
+
archive.save
|
117
|
+
|
118
|
+
fail 'ディスクからアーカイブへのコピーがタイムアウトまたは失敗しました' unless archive.sleep_while_copying
|
119
|
+
|
120
|
+
disk.destroy
|
121
|
+
|
122
|
+
ftp = archive.open_ftp.ftp_info
|
123
|
+
expect(ftp).to be_an_instance_of Saklient::Cloud::Resources::FtpInfo
|
124
|
+
expect(ftp.host_name).not_to be_nil
|
125
|
+
expect(ftp.user).not_to be_nil
|
126
|
+
expect(ftp.password).not_to be_nil
|
127
|
+
|
128
|
+
archive.close_ftp
|
129
|
+
archive.destroy
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
after do
|
135
|
+
@config = nil
|
136
|
+
@api = nil
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
end
|
data/spec/enum_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$: << File.dirname(__dir__) + '/lib'
|
2
|
+
require 'saklient/cloud/enums/eserver_instance_status'
|
3
|
+
|
4
|
+
EServerInstanceStatus = Saklient::Cloud::Enums::EServerInstanceStatus
|
5
|
+
|
6
|
+
describe 'Enum' do
|
7
|
+
|
8
|
+
it 'should be defined' do
|
9
|
+
expect(EServerInstanceStatus::up).to eq 'up'
|
10
|
+
expect(EServerInstanceStatus::down).to eq 'down'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should be compared' do
|
14
|
+
expect(EServerInstanceStatus::compare('up', 'up')).to eq 0
|
15
|
+
expect(EServerInstanceStatus::compare('up', 'down')).to eq 1
|
16
|
+
expect(EServerInstanceStatus::compare('down', 'up')).to eq -1
|
17
|
+
expect(EServerInstanceStatus::compare('UNDEFINED-SYMBOL', 'up')).to be_nil
|
18
|
+
expect(EServerInstanceStatus::compare('up', 'UNDEFINED-SYMBOL')).to be_nil
|
19
|
+
expect(EServerInstanceStatus::compare(nil, 'up')).to be_nil
|
20
|
+
expect(EServerInstanceStatus::compare('up', nil)).to be_nil
|
21
|
+
expect(EServerInstanceStatus::compare(nil, nil)).to be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
$: << File.dirname(__dir__) + '/lib'
|
2
|
+
require 'saklient/cloud/errors/exception_factory'
|
3
|
+
|
4
|
+
ExceptionFactory = Saklient::Errors::ExceptionFactory
|
5
|
+
HttpException = Saklient::Errors::HttpException
|
6
|
+
HttpNotFoundException = Saklient::Errors::HttpNotFoundException
|
7
|
+
ServerPowerMustBeUpException = Saklient::Cloud::Errors::ServerPowerMustBeUpException
|
8
|
+
|
9
|
+
describe 'Exception' do
|
10
|
+
|
11
|
+
it 'should be created' do
|
12
|
+
|
13
|
+
x = ExceptionFactory.create(404)
|
14
|
+
expect(x).to be_an_instance_of HttpNotFoundException
|
15
|
+
|
16
|
+
x = ExceptionFactory.create(409, 'server_power_must_be_up')
|
17
|
+
expect(x).to be_an_instance_of ServerPowerMustBeUpException
|
18
|
+
|
19
|
+
x = ExceptionFactory.create(666, 'nameless_http_error', 'Ia! Cthulhu Fthagn!')
|
20
|
+
expect(x).to be_an_instance_of HttpException
|
21
|
+
expect(x.message).to eq 'Ia! Cthulhu Fthagn!'
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
$: << File.dirname(__dir__) + '/lib'
|
2
|
+
require 'saklient/cloud/api'
|
3
|
+
require 'date'
|
4
|
+
require 'SecureRandom'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
describe 'IsoImage' do
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
before do
|
12
|
+
|
13
|
+
# load config file
|
14
|
+
root = File.dirname(__dir__)
|
15
|
+
test_ok_file = root + '/testok'
|
16
|
+
expect(File).to exist(test_ok_file)
|
17
|
+
config_file = root + '/config.sh'
|
18
|
+
expect(File).to exist(config_file)
|
19
|
+
@config = {}
|
20
|
+
fh = open(config_file)
|
21
|
+
fh.each {|line|
|
22
|
+
if /^\s*export\s+(\w+)\s*=\s*(.+?)\s*$/.match(line) then
|
23
|
+
key = $1
|
24
|
+
value = $2
|
25
|
+
@config[key.to_sym] = value.gsub(/'([^']*)'|"([^"]*)"|\\(.)|(.)/) {|m|
|
26
|
+
$1 || $2 || $3 || $4
|
27
|
+
}
|
28
|
+
end
|
29
|
+
}
|
30
|
+
fh.close
|
31
|
+
expect(@config[:SACLOUD_TOKEN]).not_to be_empty #'SACLOUD_TOKEN must be defined in config.sh'
|
32
|
+
expect(@config[:SACLOUD_SECRET]).not_to be_empty #'SACLOUD_SECRET must be defined in config.sh'
|
33
|
+
#expect(@config[:SACLOUD_ZONE]).not_to be_empty #'SACLOUD_ZONE must be defined in config.sh'
|
34
|
+
|
35
|
+
# authorize
|
36
|
+
@api = Saklient::Cloud::API::authorize(@config[:SACLOUD_TOKEN], @config[:SACLOUD_SECRET])
|
37
|
+
@api = @api.in_zone(@config[:SACLOUD_ZONE]) if @config[:SACLOUD_ZONE]
|
38
|
+
expect(@api).to be_an_instance_of Saklient::Cloud::API
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
it 'should be CRUDed' do
|
45
|
+
name = '!ruby_rspec-' + DateTime.now.strftime('%Y%m%d_%H%M%S') + '-' + SecureRandom.uuid[0, 8]
|
46
|
+
description = 'This instance was created by saklient.ruby rspec'
|
47
|
+
tag = 'saklient-test'
|
48
|
+
|
49
|
+
iso = @api.iso_image.create
|
50
|
+
expect(iso).to be_an_instance_of Saklient::Cloud::Resources::IsoImage
|
51
|
+
iso.name = name
|
52
|
+
iso.description = description
|
53
|
+
iso.tags = [tag]
|
54
|
+
iso.size_mib = 5120
|
55
|
+
iso.save
|
56
|
+
|
57
|
+
#
|
58
|
+
ftp = iso.ftp_info
|
59
|
+
expect(ftp).to be_an_instance_of Saklient::Cloud::Resources::FtpInfo
|
60
|
+
expect(ftp.host_name).not_to be_nil
|
61
|
+
expect(ftp.user).not_to be_nil
|
62
|
+
expect(ftp.password).not_to be_nil
|
63
|
+
ftp2 = iso.open_ftp(true).ftp_info
|
64
|
+
expect(ftp2).to be_an_instance_of Saklient::Cloud::Resources::FtpInfo
|
65
|
+
expect(ftp2.host_name).not_to be_nil
|
66
|
+
expect(ftp2.user).not_to be_nil
|
67
|
+
expect(ftp2.password).not_to be_nil
|
68
|
+
expect(ftp2.password).not_to eq ftp.password
|
69
|
+
|
70
|
+
#
|
71
|
+
temp = Tempfile.new('saklient-')
|
72
|
+
path = temp.path
|
73
|
+
temp.close!
|
74
|
+
cmd = "dd if=/dev/urandom bs=4096 count=64 > #{path}; ls -l #{path}"
|
75
|
+
puts cmd
|
76
|
+
puts `( #{cmd} ) 2>&1`
|
77
|
+
cmd = "set ftp:ssl-allow true;"
|
78
|
+
cmd += "set ftp:ssl-force true;"
|
79
|
+
cmd += "set ftp:ssl-protect-data true;"
|
80
|
+
cmd += "set ftp:ssl-protect-list true;"
|
81
|
+
cmd += "put #{path};"
|
82
|
+
cmd += "exit"
|
83
|
+
cmd = "lftp -u #{ftp2.user},#{ftp2.password} -p 21 -e '#{cmd}' #{ftp2.host_name}"
|
84
|
+
puts cmd
|
85
|
+
puts `( #{cmd} ) 2>&1`
|
86
|
+
cmd = "rm -f #{path}"
|
87
|
+
puts cmd
|
88
|
+
puts `( #{cmd} ) 2>&1`
|
89
|
+
|
90
|
+
iso.close_ftp
|
91
|
+
|
92
|
+
#
|
93
|
+
iso.destroy
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
it 'should be inserted and ejected' do
|
100
|
+
name = '!ruby_rspec-' + DateTime.now.strftime('%Y%m%d_%H%M%S') + '-' + SecureRandom.uuid[0, 8]
|
101
|
+
description = 'This instance was created by saklient.ruby rspec'
|
102
|
+
tag = 'saklient-test'
|
103
|
+
|
104
|
+
# search iso images
|
105
|
+
puts 'searching iso images...'
|
106
|
+
isos = @api.iso_image.
|
107
|
+
with_name_like('CentOS 6.5 64bit').
|
108
|
+
with_shared_scope.
|
109
|
+
limit(1).
|
110
|
+
find
|
111
|
+
expect(isos.length).to be > 0
|
112
|
+
iso = isos[0]
|
113
|
+
|
114
|
+
# create a server
|
115
|
+
puts 'creating a server...'
|
116
|
+
server = @api.server.create
|
117
|
+
expect(server).to be_an_instance_of Saklient::Cloud::Resources::Server
|
118
|
+
server.name = name
|
119
|
+
server.description = description
|
120
|
+
server.tags = [tag]
|
121
|
+
server.plan = @api.product.server.get_by_spec(1, 1)
|
122
|
+
server.save
|
123
|
+
|
124
|
+
# insert iso image while the server is down
|
125
|
+
puts 'inserting an ISO image to the server...'
|
126
|
+
server.insert_iso_image(iso)
|
127
|
+
expect(server.instance.iso_image).to be_an_instance_of Saklient::Cloud::Resources::IsoImage
|
128
|
+
expect(server.instance.iso_image.id).to eq iso.id
|
129
|
+
|
130
|
+
# eject iso image while the server is down
|
131
|
+
puts 'ejecting the ISO image from the server...'
|
132
|
+
server.eject_iso_image
|
133
|
+
expect(server.instance.iso_image).to be_nil
|
134
|
+
|
135
|
+
# boot
|
136
|
+
puts 'booting the server...'
|
137
|
+
server.boot
|
138
|
+
sleep 3
|
139
|
+
|
140
|
+
# insert iso image while the server is up
|
141
|
+
puts 'inserting an ISO image to the server...'
|
142
|
+
server.insert_iso_image(iso)
|
143
|
+
expect(server.instance.iso_image).to be_an_instance_of Saklient::Cloud::Resources::IsoImage
|
144
|
+
expect(server.instance.iso_image.id).to eq iso.id
|
145
|
+
|
146
|
+
# eject iso image while the server is up
|
147
|
+
puts 'ejecting the ISO image from the server...'
|
148
|
+
server.eject_iso_image
|
149
|
+
expect(server.instance.iso_image).to be_nil
|
150
|
+
|
151
|
+
# stop
|
152
|
+
sleep 1
|
153
|
+
puts 'stopping the server...'
|
154
|
+
server.stop
|
155
|
+
fail 'サーバが正常に停止しません' unless server.sleep_until_down
|
156
|
+
|
157
|
+
# delete the server
|
158
|
+
puts 'deleting the server...'
|
159
|
+
server.destroy
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
after do
|
166
|
+
@config = nil
|
167
|
+
@api = nil
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
|
172
|
+
end
|
data/spec/router_spec.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
$: << File.dirname(__dir__) + '/lib'
|
2
|
+
require 'saklient/cloud/api'
|
3
|
+
require 'date'
|
4
|
+
require 'SecureRandom'
|
5
|
+
require 'ipaddr'
|
6
|
+
|
7
|
+
describe 'Router' do
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
before do
|
12
|
+
|
13
|
+
# load config file
|
14
|
+
root = File.dirname(__dir__)
|
15
|
+
test_ok_file = root + '/testok'
|
16
|
+
expect(File).to exist(test_ok_file)
|
17
|
+
config_file = root + '/config.sh'
|
18
|
+
expect(File).to exist(config_file)
|
19
|
+
@config = {}
|
20
|
+
fh = open(config_file)
|
21
|
+
fh.each {|line|
|
22
|
+
if /^\s*export\s+(\w+)\s*=\s*(.+?)\s*$/.match(line) then
|
23
|
+
key = $1
|
24
|
+
value = $2
|
25
|
+
@config[key.to_sym] = value.gsub(/'([^']*)'|"([^"]*)"|\\(.)|(.)/) {|m|
|
26
|
+
$1 || $2 || $3 || $4
|
27
|
+
}
|
28
|
+
end
|
29
|
+
}
|
30
|
+
fh.close
|
31
|
+
expect(@config[:SACLOUD_TOKEN]).not_to be_empty #'SACLOUD_TOKEN must be defined in config.sh'
|
32
|
+
expect(@config[:SACLOUD_SECRET]).not_to be_empty #'SACLOUD_SECRET must be defined in config.sh'
|
33
|
+
#expect(@config[:SACLOUD_ZONE]).not_to be_empty #'SACLOUD_ZONE must be defined in config.sh'
|
34
|
+
|
35
|
+
# authorize
|
36
|
+
@api = Saklient::Cloud::API::authorize(@config[:SACLOUD_TOKEN], @config[:SACLOUD_SECRET])
|
37
|
+
@api = @api.in_zone(@config[:SACLOUD_ZONE]) if @config[:SACLOUD_ZONE]
|
38
|
+
expect(@api).to be_an_instance_of Saklient::Cloud::API
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
it 'should be CRUDed' do
|
45
|
+
name = '!ruby_rspec-' + DateTime.now.strftime('%Y%m%d_%H%M%S') + '-' + SecureRandom.uuid[0, 8]
|
46
|
+
description = 'This instance was created by saklient.ruby rspec'
|
47
|
+
mask_len = 28
|
48
|
+
|
49
|
+
#
|
50
|
+
swytch = nil
|
51
|
+
if false then
|
52
|
+
puts 'ルータ+スイッチの帯域プランを検索しています...'
|
53
|
+
plans = @api.product.router.find
|
54
|
+
min_mbps = 0x7FFFFFFF
|
55
|
+
for plan in plans do
|
56
|
+
expect(plan).to be_an_instance_of Saklient::Cloud::Resources::RouterPlan
|
57
|
+
expect(plan.band_width_mbps).to be > 0
|
58
|
+
min_mbps = [plan.band_width_mbps, min_mbps].min
|
59
|
+
end
|
60
|
+
|
61
|
+
puts 'ルータ+スイッチを作成しています...'
|
62
|
+
router = @api.router.create
|
63
|
+
router.name = name
|
64
|
+
router.description = description
|
65
|
+
router.band_width_mbps = min_mbps
|
66
|
+
router.network_mask_len = mask_len
|
67
|
+
router.save
|
68
|
+
|
69
|
+
puts 'ルータ+スイッチの作成完了を待機しています...'
|
70
|
+
fail 'ルータが正常に作成されません' unless router.sleep_while_creating
|
71
|
+
swytch = router.get_swytch
|
72
|
+
else
|
73
|
+
puts '既存のルータ+スイッチを取得しています...'
|
74
|
+
swytches = @api.swytch.with_name_like('saklient-static-1').limit(1).find
|
75
|
+
expect(swytches.length).to eq 1
|
76
|
+
swytch = swytches[0]
|
77
|
+
end
|
78
|
+
|
79
|
+
expect(swytch).to be_an_instance_of Saklient::Cloud::Resources::Swytch
|
80
|
+
expect(swytch.ipv4_nets.length).to be > 0
|
81
|
+
expect(swytch.ipv4_nets[0]).to be_an_instance_of Saklient::Cloud::Resources::Ipv4Net
|
82
|
+
|
83
|
+
#
|
84
|
+
puts 'ルータ+スイッチの帯域プランを変更しています...'
|
85
|
+
router_id_before = swytch.router.id
|
86
|
+
swytch.change_plan(swytch.router.band_width_mbps==100 ? 500 : 100)
|
87
|
+
expect(swytch.router.id).not_to eq router_id_before
|
88
|
+
|
89
|
+
#
|
90
|
+
if 0 < swytch.ipv6_nets.length then
|
91
|
+
puts 'ルータ+スイッチからIPv6ネットワークの割当を解除しています...'
|
92
|
+
swytch.remove_ipv6_net
|
93
|
+
end
|
94
|
+
puts 'ルータ+スイッチにIPv6ネットワークを割り当てています...'
|
95
|
+
v6net = swytch.add_ipv6_net
|
96
|
+
expect(v6net).to be_an_instance_of Saklient::Cloud::Resources::Ipv6Net
|
97
|
+
expect(swytch.ipv6_nets.length).to eq 1
|
98
|
+
|
99
|
+
#
|
100
|
+
(swytch.ipv4_nets.length - 1).downto(1) do |i|
|
101
|
+
puts 'ルータ+スイッチからスタティックルートの割当を解除しています...'
|
102
|
+
net = swytch.ipv4_nets[i]
|
103
|
+
swytch.remove_static_route(net)
|
104
|
+
end
|
105
|
+
|
106
|
+
puts 'ルータ+スイッチにスタティックルートを割り当てています...'
|
107
|
+
net0 = swytch.ipv4_nets[0]
|
108
|
+
next_hop = IPAddr.new(IPAddr.new(net0.address).to_i + 4, Socket::AF_INET).to_s
|
109
|
+
sroute = swytch.add_static_route(28, next_hop)
|
110
|
+
expect(sroute).to be_an_instance_of Saklient::Cloud::Resources::Ipv4Net
|
111
|
+
expect(swytch.ipv4_nets.length).to eq 2
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
after do
|
118
|
+
@config = nil
|
119
|
+
@api = nil
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
end
|