bosh_openstack_cpi 0.0.4 → 0.0.5

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/spec/spec_helper.rb CHANGED
@@ -111,7 +111,12 @@ def mock_glance(options = nil)
111
111
  end
112
112
 
113
113
  def dynamic_network_spec
114
- { "type" => "dynamic" }
114
+ {
115
+ "type" => "dynamic",
116
+ "cloud_properties" => {
117
+ "security_groups" => %w[default]
118
+ }
119
+ }
115
120
  end
116
121
 
117
122
  def vip_network_spec
@@ -15,14 +15,17 @@ describe Bosh::OpenStackCloud::Cloud do
15
15
  attachment = double("attachment", :device => "/dev/vdc")
16
16
 
17
17
  cloud = mock_cloud do |openstack|
18
- openstack.servers.should_receive(:get).with("i-test").and_return(server)
19
- openstack.volumes.should_receive(:get).with("v-foobar").and_return(volume)
20
- openstack.should_receive(:get_server_volumes).and_return(volume_attachments)
18
+ openstack.servers.should_receive(:get).
19
+ with("i-test").and_return(server)
20
+ openstack.volumes.should_receive(:get).
21
+ with("v-foobar").and_return(volume)
22
+ openstack.should_receive(:get_server_volumes).
23
+ and_return(volume_attachments)
21
24
  end
22
25
 
23
- volume.should_receive(:attach).with(server.id, "/dev/vdc").and_return(attachment)
24
- volume.should_receive(:status).and_return(:available)
25
- cloud.should_receive(:wait_resource).with(volume, :available, :"in-use")
26
+ volume.should_receive(:attach).
27
+ with(server.id, "/dev/vdc").and_return(attachment)
28
+ cloud.should_receive(:wait_resource).with(volume, :"in-use")
26
29
 
27
30
  old_settings = { "foo" => "bar" }
28
31
  new_settings = {
@@ -34,8 +37,10 @@ describe Bosh::OpenStackCloud::Cloud do
34
37
  }
35
38
  }
36
39
 
37
- @registry.should_receive(:read_settings).with("i-test").and_return(old_settings)
38
- @registry.should_receive(:update_settings).with("i-test", new_settings)
40
+ @registry.should_receive(:read_settings).
41
+ with("i-test").and_return(old_settings)
42
+ @registry.should_receive(:update_settings).
43
+ with("i-test", new_settings)
39
44
 
40
45
  cloud.attach_disk("i-test", "v-foobar")
41
46
  end
@@ -43,18 +48,23 @@ describe Bosh::OpenStackCloud::Cloud do
43
48
  it "picks available device name" do
44
49
  server = double("server", :id => "i-test", :name => "i-test")
45
50
  volume = double("volume", :id => "v-foobar")
46
- volume_attachments = double("body", :body => {"volumeAttachments" => [{"device" => "/dev/vdc"}, {"device" => "/dev/vdd"}]})
51
+ volume_attachments = double("body", :body => {"volumeAttachments" =>
52
+ [{"device" => "/dev/vdc"},
53
+ {"device" => "/dev/vdd"}]})
47
54
  attachment = double("attachment", :device => "/dev/vdd")
48
55
 
49
56
  cloud = mock_cloud do |openstack|
50
- openstack.servers.should_receive(:get).with("i-test").and_return(server)
51
- openstack.volumes.should_receive(:get).with("v-foobar").and_return(volume)
52
- openstack.should_receive(:get_server_volumes).and_return(volume_attachments)
57
+ openstack.servers.should_receive(:get).
58
+ with("i-test").and_return(server)
59
+ openstack.volumes.should_receive(:get).
60
+ with("v-foobar").and_return(volume)
61
+ openstack.should_receive(:get_server_volumes).
62
+ and_return(volume_attachments)
53
63
  end
54
64
 
55
- volume.should_receive(:attach).with(server.id, "/dev/vde").and_return(attachment)
56
- volume.should_receive(:status).and_return(:available)
57
- cloud.should_receive(:wait_resource).with(volume, :available, :"in-use")
65
+ volume.should_receive(:attach).
66
+ with(server.id, "/dev/vde").and_return(attachment)
67
+ cloud.should_receive(:wait_resource).with(volume, :"in-use")
58
68
 
59
69
  old_settings = { "foo" => "bar" }
60
70
  new_settings = {
@@ -66,8 +76,10 @@ describe Bosh::OpenStackCloud::Cloud do
66
76
  }
67
77
  }
68
78
 
69
- @registry.should_receive(:read_settings).with("i-test").and_return(old_settings)
70
- @registry.should_receive(:update_settings).with("i-test", new_settings)
79
+ @registry.should_receive(:read_settings).
80
+ with("i-test").and_return(old_settings)
81
+ @registry.should_receive(:update_settings).
82
+ with("i-test", new_settings)
71
83
 
72
84
  cloud.attach_disk("i-test", "v-foobar")
73
85
  end
@@ -79,12 +91,16 @@ describe Bosh::OpenStackCloud::Cloud do
79
91
  array << {"device" => "/dev/vd#{char}"}
80
92
  array
81
93
  end
82
- volume_attachments = double("body", :body => {"volumeAttachments" => all_mappings})
94
+ volume_attachments = double("body", :body => {"volumeAttachments" =>
95
+ all_mappings})
83
96
 
84
97
  cloud = mock_cloud do |openstack|
85
- openstack.servers.should_receive(:get).with("i-test").and_return(server)
86
- openstack.volumes.should_receive(:get).with("v-foobar").and_return(volume)
87
- openstack.should_receive(:get_server_volumes).and_return(volume_attachments)
98
+ openstack.servers.should_receive(:get).
99
+ with("i-test").and_return(server)
100
+ openstack.volumes.should_receive(:get).
101
+ with("v-foobar").and_return(volume)
102
+ openstack.should_receive(:get_server_volumes).
103
+ and_return(volume_attachments)
88
104
  end
89
105
 
90
106
  expect {
@@ -8,13 +8,34 @@ describe Bosh::OpenStackCloud::Cloud do
8
8
  @registry = mock_registry
9
9
  end
10
10
 
11
+ it "forces recreation when security groups differ" do
12
+ server = double("server", :id => "i-test", :name => "i-test")
13
+ sec_grp = double("security_group",
14
+ :body => {"security_groups" => [{"name"=> "newgroup" }]})
15
+
16
+ cloud = mock_cloud do |openstack|
17
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
18
+ openstack.should_receive(:list_security_groups).
19
+ with("i-test").and_return(sec_grp)
20
+ end
21
+
22
+ expect {
23
+ cloud.configure_networks("i-test", combined_network_spec)
24
+ }.to raise_error Bosh::Clouds::NotSupported
25
+ end
26
+
11
27
  it "adds floating ip to the server for vip network" do
12
28
  server = double("server", :id => "i-test", :name => "i-test")
13
- address = double("address", :id => "a-test", :ip => "10.0.0.1", :instance_id => nil)
29
+ address = double("address", :id => "a-test", :ip => "10.0.0.1",
30
+ :instance_id => nil)
31
+ sec_grp = double("security_group",
32
+ :body => {"security_groups" => [{"name"=> "default" }]})
14
33
 
15
34
  cloud = mock_cloud do |openstack|
16
35
  openstack.servers.should_receive(:get).with("i-test").and_return(server)
17
- openstack.addresses.should_receive(:each).and_yield(address)
36
+ openstack.addresses.should_receive(:find).and_return(address)
37
+ openstack.should_receive(:list_security_groups).
38
+ with("i-test").and_return(sec_grp)
18
39
  end
19
40
 
20
41
  address.should_receive(:server=).with(server)
@@ -22,7 +43,8 @@ describe Bosh::OpenStackCloud::Cloud do
22
43
  old_settings = { "foo" => "bar", "networks" => "baz" }
23
44
  new_settings = { "foo" => "bar", "networks" => combined_network_spec }
24
45
 
25
- @registry.should_receive(:read_settings).with("i-test").and_return(old_settings)
46
+ @registry.should_receive(:read_settings).with("i-test").
47
+ and_return(old_settings)
26
48
  @registry.should_receive(:update_settings).with("i-test", new_settings)
27
49
 
28
50
  cloud.configure_networks("i-test", combined_network_spec)
@@ -30,53 +52,54 @@ describe Bosh::OpenStackCloud::Cloud do
30
52
 
31
53
  it "removes floating ip from the server if vip network is gone" do
32
54
  server = double("server", :id => "i-test", :name => "i-test")
33
- address = double("address", :id => "a-test", :ip => "10.0.0.1", :instance_id => "i-test")
55
+ address = double("address", :id => "a-test", :ip => "10.0.0.1",
56
+ :instance_id => "i-test")
57
+ sec_grp = double("security_group",
58
+ :body => {"security_groups" => [{"name"=> "default" }]})
34
59
 
35
60
  cloud = mock_cloud do |openstack|
36
61
  openstack.servers.should_receive(:get).with("i-test").and_return(server)
37
62
  openstack.addresses.should_receive(:each).and_yield(address)
63
+ openstack.should_receive(:list_security_groups).
64
+ with("i-test").and_return(sec_grp)
38
65
  end
39
66
 
40
67
  address.should_receive(:server=).with(nil)
41
68
 
42
- old_settings = { "foo" => "bar", "networks" => combined_network_spec }
43
- new_settings = { "foo" => "bar", "networks" => { "net_a" => dynamic_network_spec } }
69
+ old_settings = { "foo" => "bar",
70
+ "networks" => combined_network_spec }
71
+ new_settings = { "foo" => "bar",
72
+ "networks" => { "net_a" => dynamic_network_spec } }
44
73
 
45
- @registry.should_receive(:read_settings).with("i-test").and_return(old_settings)
74
+ @registry.should_receive(:read_settings).with("i-test").
75
+ and_return(old_settings)
46
76
  @registry.should_receive(:update_settings).with("i-test", new_settings)
47
77
 
48
78
  cloud.configure_networks("i-test", "net_a" => dynamic_network_spec)
49
79
  end
50
80
 
51
81
  it "performs network sanity check" do
52
- server = double("server", :id => "i-test", :name => "i-test")
53
-
54
82
  expect {
55
- cloud = mock_cloud do |openstack|
56
- openstack.servers.should_receive(:get).with("i-test").and_return(server)
57
- end
58
- cloud.configure_networks("i-test", "net_a" => vip_network_spec)
59
- }.to raise_error(Bosh::Clouds::CloudError, "At least one dynamic network should be defined")
83
+ mock_cloud.configure_networks("i-test",
84
+ "net_a" => vip_network_spec)
85
+ }.to raise_error(Bosh::Clouds::CloudError,
86
+ "At least one dynamic network should be defined")
60
87
 
61
88
  expect {
62
- cloud = mock_cloud do |openstack|
63
- openstack.servers.should_receive(:get).with("i-test").and_return(server)
64
- end
65
- cloud.configure_networks("i-test", "net_a" => vip_network_spec, "net_b" => vip_network_spec)
89
+ mock_cloud.configure_networks("i-test",
90
+ "net_a" => vip_network_spec,
91
+ "net_b" => vip_network_spec)
66
92
  }.to raise_error(Bosh::Clouds::CloudError, /More than one vip network/)
67
93
 
68
94
  expect {
69
- cloud = mock_cloud do |openstack|
70
- openstack.servers.should_receive(:get).with("i-test").and_return(server)
71
- end
72
- cloud.configure_networks("i-test", "net_a" => dynamic_network_spec, "net_b" => dynamic_network_spec)
95
+ mock_cloud.configure_networks("i-test",
96
+ "net_a" => dynamic_network_spec,
97
+ "net_b" => dynamic_network_spec)
73
98
  }.to raise_error(Bosh::Clouds::CloudError, /More than one dynamic network/)
74
99
 
75
100
  expect {
76
- cloud = mock_cloud do |openstack|
77
- openstack.servers.should_receive(:get).with("i-test").and_return(server)
78
- end
79
- cloud.configure_networks("i-test", "net_a" => { "type" => "foo" })
101
+ mock_cloud.configure_networks("i-test",
102
+ "net_a" => { "type" => "foo" })
80
103
  }.to raise_error(Bosh::Clouds::CloudError, /Invalid network type `foo'/)
81
104
  end
82
105
 
@@ -9,18 +9,17 @@ describe Bosh::OpenStackCloud::Cloud do
9
9
  disk_params = {
10
10
  :name => "volume-#{unique_name}",
11
11
  :description => "",
12
- :size => 2,
13
- :availability_zone => "nova"
12
+ :size => 2
14
13
  }
15
14
  volume = double("volume", :id => "v-foobar")
16
15
 
17
16
  cloud = mock_cloud do |openstack|
18
- openstack.volumes.should_receive(:create).with(disk_params).and_return(volume)
17
+ openstack.volumes.should_receive(:create).
18
+ with(disk_params).and_return(volume)
19
19
  end
20
20
 
21
21
  cloud.should_receive(:generate_unique_name).and_return(unique_name)
22
- volume.should_receive(:status).and_return(:creating)
23
- cloud.should_receive(:wait_resource).with(volume, :creating, :available)
22
+ cloud.should_receive(:wait_resource).with(volume, :available)
24
23
 
25
24
  cloud.create_disk(2048).should == "v-foobar"
26
25
  end
@@ -30,18 +29,17 @@ describe Bosh::OpenStackCloud::Cloud do
30
29
  disk_params = {
31
30
  :name => "volume-#{unique_name}",
32
31
  :description => "",
33
- :size => 3,
34
- :availability_zone => "nova"
32
+ :size => 3
35
33
  }
36
34
  volume = double("volume", :id => "v-foobar")
37
35
 
38
36
  cloud = mock_cloud do |openstack|
39
- openstack.volumes.should_receive(:create).with(disk_params).and_return(volume)
37
+ openstack.volumes.should_receive(:create).
38
+ with(disk_params).and_return(volume)
40
39
  end
41
40
 
42
41
  cloud.should_receive(:generate_unique_name).and_return(unique_name)
43
- volume.should_receive(:status).and_return(:creating)
44
- cloud.should_receive(:wait_resource).with(volume, :creating, :available)
42
+ cloud.should_receive(:wait_resource).with(volume, :available)
45
43
 
46
44
  cloud.create_disk(2049)
47
45
  end
@@ -49,11 +47,11 @@ describe Bosh::OpenStackCloud::Cloud do
49
47
  it "check min and max disk size" do
50
48
  expect {
51
49
  mock_cloud.create_disk(100)
52
- }.to raise_error(Bosh::Clouds::CloudError, /minimum disk size is 1 GiB/)
50
+ }.to raise_error(Bosh::Clouds::CloudError, /Minimum disk size is 1 GiB/)
53
51
 
54
52
  expect {
55
53
  mock_cloud.create_disk(2000 * 1024)
56
- }.to raise_error(Bosh::Clouds::CloudError, /maximum disk size is 1 TiB/)
54
+ }.to raise_error(Bosh::Clouds::CloudError, /Maximum disk size is 1 TiB/)
57
55
  end
58
56
 
59
57
  it "puts disk in the same AZ as a server" do
@@ -64,17 +62,19 @@ describe Bosh::OpenStackCloud::Cloud do
64
62
  :size => 1,
65
63
  :availability_zone => "foobar-land"
66
64
  }
67
- server = double("server", :id => "i-test", :availability_zone => "foobar-land")
65
+ server = double("server", :id => "i-test",
66
+ :availability_zone => "foobar-land")
68
67
  volume = double("volume", :id => "v-foobar")
69
68
 
70
69
  cloud = mock_cloud do |openstack|
71
- openstack.servers.should_receive(:get).with("i-test").and_return(server)
72
- openstack.volumes.should_receive(:create).with(disk_params).and_return(volume)
70
+ openstack.servers.should_receive(:get).
71
+ with("i-test").and_return(server)
72
+ openstack.volumes.should_receive(:create).
73
+ with(disk_params).and_return(volume)
73
74
  end
74
75
 
75
76
  cloud.should_receive(:generate_unique_name).and_return(unique_name)
76
- volume.should_receive(:status).and_return(:creating)
77
- cloud.should_receive(:wait_resource).with(volume, :creating, :available)
77
+ cloud.should_receive(:wait_resource).with(volume, :available)
78
78
 
79
79
  cloud.create_disk(1024, "i-test")
80
80
  end
@@ -10,7 +10,7 @@ describe Bosh::OpenStackCloud::Cloud do
10
10
 
11
11
  describe "Image upload based flow" do
12
12
 
13
- it "creates stemcell by uploading an image without kernel nor ramdisk" do
13
+ it "creates stemcell using an image without kernel nor ramdisk" do
14
14
  image = double("image", :id => "i-bar", :name => "i-bar")
15
15
  unique_name = UUIDTools::UUID.random_create.to_s
16
16
  image_params = {
@@ -22,14 +22,13 @@ describe Bosh::OpenStackCloud::Cloud do
22
22
  }
23
23
 
24
24
  cloud = mock_glance do |glance|
25
- glance.images.should_receive(:create).with(image_params).and_return(image)
25
+ glance.images.should_receive(:create).
26
+ with(image_params).and_return(image)
26
27
  end
27
28
 
28
29
  Dir.should_receive(:mktmpdir).and_yield(@tmp_dir)
29
30
  cloud.should_receive(:unpack_image).with(@tmp_dir, "/tmp/foo")
30
31
  cloud.should_receive(:generate_unique_name).and_return(unique_name)
31
- image.should_receive(:status).and_return(:queued)
32
- cloud.should_receive(:wait_resource).with(image, :queued, :active)
33
32
 
34
33
  sc_id = cloud.create_stemcell("/tmp/foo", {
35
34
  "container_format" => "ami",
@@ -39,7 +38,7 @@ describe Bosh::OpenStackCloud::Cloud do
39
38
  sc_id.should == "i-bar"
40
39
  end
41
40
 
42
- it "creates stemcell by uploading an image using kernel and ramdisk id's" do
41
+ it "creates stemcell using an image with kernel and ramdisk id's" do
43
42
  image = double("image", :id => "i-bar", :name => "i-bar")
44
43
  unique_name = UUIDTools::UUID.random_create.to_s
45
44
  image_params = {
@@ -55,14 +54,13 @@ describe Bosh::OpenStackCloud::Cloud do
55
54
  }
56
55
 
57
56
  cloud = mock_glance do |glance|
58
- glance.images.should_receive(:create).with(image_params).and_return(image)
57
+ glance.images.should_receive(:create).
58
+ with(image_params).and_return(image)
59
59
  end
60
60
 
61
61
  Dir.should_receive(:mktmpdir).and_yield(@tmp_dir)
62
62
  cloud.should_receive(:unpack_image).with(@tmp_dir, "/tmp/foo")
63
63
  cloud.should_receive(:generate_unique_name).and_return(unique_name)
64
- image.should_receive(:status).and_return(:queued)
65
- cloud.should_receive(:wait_resource).with(image, :queued, :active)
66
64
 
67
65
  sc_id = cloud.create_stemcell("/tmp/foo", {
68
66
  "container_format" => "ami",
@@ -76,13 +74,13 @@ describe Bosh::OpenStackCloud::Cloud do
76
74
  sc_id.should == "i-bar"
77
75
  end
78
76
 
79
- it "creates stemcell by uploading image, kernel and ramdisk" do
77
+ it "creates stemcell using an image with kernel and ramdisk files" do
80
78
  image = double("image", :id => "i-bar", :name => "i-bar")
81
79
  kernel = double("image", :id => "k-img-id", :name => "k-img-id")
82
80
  ramdisk = double("image", :id => "r-img-id", :name => "r-img-id")
83
81
  unique_name = UUIDTools::UUID.random_create.to_s
84
82
  kernel_params = {
85
- :name => "AKI-BOSH-#{unique_name}",
83
+ :name => "BOSH-#{unique_name}-AKI",
86
84
  :disk_format => "aki",
87
85
  :container_format => "aki",
88
86
  :location => "#{@tmp_dir}/kernel.img",
@@ -91,7 +89,7 @@ describe Bosh::OpenStackCloud::Cloud do
91
89
  }
92
90
  }
93
91
  ramdisk_params = {
94
- :name => "ARI-BOSH-#{unique_name}",
92
+ :name => "BOSH-#{unique_name}-ARI",
95
93
  :disk_format => "ari",
96
94
  :container_format => "ari",
97
95
  :location => "#{@tmp_dir}/initrd.img",
@@ -112,21 +110,18 @@ describe Bosh::OpenStackCloud::Cloud do
112
110
  }
113
111
 
114
112
  cloud = mock_glance do |glance|
115
- glance.images.should_receive(:create).with(kernel_params).and_return(kernel)
116
- glance.images.should_receive(:create).with(ramdisk_params).and_return(ramdisk)
117
- glance.images.should_receive(:create).with(image_params).and_return(image)
113
+ glance.images.should_receive(:create).
114
+ with(kernel_params).and_return(kernel)
115
+ glance.images.should_receive(:create).
116
+ with(ramdisk_params).and_return(ramdisk)
117
+ glance.images.should_receive(:create).
118
+ with(image_params).and_return(image)
118
119
  end
119
120
 
120
121
  Dir.should_receive(:mktmpdir).and_yield(@tmp_dir)
121
122
  cloud.should_receive(:unpack_image).with(@tmp_dir, "/tmp/foo")
122
123
  File.stub(:exists?).and_return(true)
123
124
  cloud.should_receive(:generate_unique_name).and_return(unique_name)
124
- kernel.should_receive(:status).and_return(:queued)
125
- cloud.should_receive(:wait_resource).with(kernel, :queued, :active)
126
- ramdisk.should_receive(:status).and_return(:queued)
127
- cloud.should_receive(:wait_resource).with(ramdisk, :queued, :active)
128
- image.should_receive(:status).and_return(:queued)
129
- cloud.should_receive(:wait_resource).with(image, :queued, :active)
130
125
 
131
126
  sc_id = cloud.create_stemcell("/tmp/foo", {
132
127
  "container_format" => "ami",
@@ -138,6 +133,69 @@ describe Bosh::OpenStackCloud::Cloud do
138
133
  sc_id.should == "i-bar"
139
134
  end
140
135
 
136
+ it "sets image properies from cloud_properties" do
137
+ image = double("image", :id => "i-bar", :name => "i-bar")
138
+ kernel = double("image", :id => "k-img-id", :name => "k-img-id")
139
+ ramdisk = double("image", :id => "r-img-id", :name => "r-img-id")
140
+ unique_name = UUIDTools::UUID.random_create.to_s
141
+ kernel_params = {
142
+ :name => "BOSH-#{unique_name}-AKI",
143
+ :disk_format => "aki",
144
+ :container_format => "aki",
145
+ :location => "#{@tmp_dir}/kernel.img",
146
+ :properties => {
147
+ :stemcell => "BOSH-#{unique_name}",
148
+ }
149
+ }
150
+ ramdisk_params = {
151
+ :name => "BOSH-#{unique_name}-ARI",
152
+ :disk_format => "ari",
153
+ :container_format => "ari",
154
+ :location => "#{@tmp_dir}/initrd.img",
155
+ :properties => {
156
+ :stemcell => "BOSH-#{unique_name}",
157
+ }
158
+ }
159
+ image_params = {
160
+ :name => "BOSH-#{unique_name}",
161
+ :disk_format => "ami",
162
+ :container_format => "ami",
163
+ :location => "#{@tmp_dir}/root.img",
164
+ :is_public => true,
165
+ :properties => {
166
+ :kernel_id => "k-img-id",
167
+ :ramdisk_id => "r-img-id",
168
+ :stemcell_name => "bosh-stemcell",
169
+ :stemcell_version => "x.y.z"
170
+ }
171
+ }
172
+
173
+ cloud = mock_glance do |glance|
174
+ glance.images.should_receive(:create).
175
+ with(kernel_params).and_return(kernel)
176
+ glance.images.should_receive(:create).
177
+ with(ramdisk_params).and_return(ramdisk)
178
+ glance.images.should_receive(:create).
179
+ with(image_params).and_return(image)
180
+ end
181
+
182
+ Dir.should_receive(:mktmpdir).and_yield(@tmp_dir)
183
+ cloud.should_receive(:unpack_image).with(@tmp_dir, "/tmp/foo")
184
+ File.stub(:exists?).and_return(true)
185
+ cloud.should_receive(:generate_unique_name).and_return(unique_name)
186
+
187
+ sc_id = cloud.create_stemcell("/tmp/foo", {
188
+ "name" => "bosh-stemcell",
189
+ "version" => "x.y.z",
190
+ "container_format" => "ami",
191
+ "disk_format" => "ami",
192
+ "kernel_file" => "kernel.img",
193
+ "ramdisk_file" => "initrd.img"
194
+ })
195
+
196
+ sc_id.should == "i-bar"
197
+ end
198
+
141
199
  end
142
200
 
143
201
  end