Committed by
Gerrit Code Review
[ONOS-3618] Implement REST API for Meter query, insert, delete
* Implement encode & decode method for MeterBandCodec & MeterCodec * Implement MetersWebResource * Add unit test for MeterBandCodec & MeterCodec * Add unit test for MetersWebResource * Add meter insertion json example * Add Swagger doc Change-Id: I07284c6678c08b3cb9e109e86ffb2cf28bf36447
Showing
16 changed files
with
1660 additions
and
4 deletions
... | @@ -51,7 +51,7 @@ public interface Band { | ... | @@ -51,7 +51,7 @@ public interface Band { |
51 | * | 51 | * |
52 | * @return the long value of the size | 52 | * @return the long value of the size |
53 | */ | 53 | */ |
54 | - long burst(); | 54 | + Long burst(); |
55 | 55 | ||
56 | /** | 56 | /** |
57 | * Only meaningful in the case of a REMARK band type. | 57 | * Only meaningful in the case of a REMARK band type. |
... | @@ -60,7 +60,7 @@ public interface Band { | ... | @@ -60,7 +60,7 @@ public interface Band { |
60 | * | 60 | * |
61 | * @return a short value | 61 | * @return a short value |
62 | */ | 62 | */ |
63 | - short dropPrecedence(); | 63 | + Short dropPrecedence(); |
64 | 64 | ||
65 | /** | 65 | /** |
66 | * Signals the type of band to create. | 66 | * Signals the type of band to create. | ... | ... |
... | @@ -45,12 +45,12 @@ public final class DefaultBand implements Band, BandEntry { | ... | @@ -45,12 +45,12 @@ public final class DefaultBand implements Band, BandEntry { |
45 | } | 45 | } |
46 | 46 | ||
47 | @Override | 47 | @Override |
48 | - public long burst() { | 48 | + public Long burst() { |
49 | return burstSize; | 49 | return burstSize; |
50 | } | 50 | } |
51 | 51 | ||
52 | @Override | 52 | @Override |
53 | - public short dropPrecedence() { | 53 | + public Short dropPrecedence() { |
54 | return prec; | 54 | return prec; |
55 | } | 55 | } |
56 | 56 | ... | ... |
... | @@ -50,6 +50,8 @@ import org.onosproject.net.intent.Constraint; | ... | @@ -50,6 +50,8 @@ import org.onosproject.net.intent.Constraint; |
50 | import org.onosproject.net.intent.HostToHostIntent; | 50 | import org.onosproject.net.intent.HostToHostIntent; |
51 | import org.onosproject.net.intent.Intent; | 51 | import org.onosproject.net.intent.Intent; |
52 | import org.onosproject.net.intent.PointToPointIntent; | 52 | import org.onosproject.net.intent.PointToPointIntent; |
53 | +import org.onosproject.net.meter.Band; | ||
54 | +import org.onosproject.net.meter.Meter; | ||
53 | import org.onosproject.net.statistic.Load; | 55 | import org.onosproject.net.statistic.Load; |
54 | import org.onosproject.net.topology.Topology; | 56 | import org.onosproject.net.topology.Topology; |
55 | import org.onosproject.net.topology.TopologyCluster; | 57 | import org.onosproject.net.topology.TopologyCluster; |
... | @@ -102,6 +104,8 @@ public class CodecManager implements CodecService { | ... | @@ -102,6 +104,8 @@ public class CodecManager implements CodecService { |
102 | registerCodec(Driver.class, new DriverCodec()); | 104 | registerCodec(Driver.class, new DriverCodec()); |
103 | registerCodec(GroupBucket.class, new GroupBucketCodec()); | 105 | registerCodec(GroupBucket.class, new GroupBucketCodec()); |
104 | registerCodec(Load.class, new LoadCodec()); | 106 | registerCodec(Load.class, new LoadCodec()); |
107 | + registerCodec(Meter.class, new MeterCodec()); | ||
108 | + registerCodec(Band.class, new MeterBandCodec()); | ||
105 | registerCodec(TableStatisticsEntry.class, new TableStatisticsEntryCodec()); | 109 | registerCodec(TableStatisticsEntry.class, new TableStatisticsEntryCodec()); |
106 | registerCodec(PortStatistics.class, new PortStatisticsCodec()); | 110 | registerCodec(PortStatistics.class, new PortStatisticsCodec()); |
107 | registerCodec(Metric.class, new MetricCodec()); | 111 | registerCodec(Metric.class, new MetricCodec()); | ... | ... |
1 | +/* | ||
2 | + * Copyright 2014-2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.codec.impl; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
19 | +import org.onosproject.codec.CodecContext; | ||
20 | +import org.onosproject.codec.JsonCodec; | ||
21 | +import org.onosproject.net.meter.Band; | ||
22 | +import org.onosproject.net.meter.DefaultBand; | ||
23 | +import org.slf4j.Logger; | ||
24 | + | ||
25 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
26 | +import static org.onlab.util.Tools.nullIsIllegal; | ||
27 | +import static org.slf4j.LoggerFactory.getLogger; | ||
28 | + | ||
29 | +/** | ||
30 | + * Meter band JSON codec. | ||
31 | + */ | ||
32 | +public final class MeterBandCodec extends JsonCodec<Band> { | ||
33 | + private final Logger log = getLogger(getClass()); | ||
34 | + | ||
35 | + // JSON field names | ||
36 | + private static final String TYPE = "type"; | ||
37 | + private static final String RATE = "rate"; | ||
38 | + private static final String BURST_SIZE = "burstSize"; | ||
39 | + private static final String PREC = "prec"; | ||
40 | + private static final String PACKETS = "packets"; | ||
41 | + private static final String BYTES = "bytes"; | ||
42 | + private static final String MISSING_MEMBER_MESSAGE = " member is required in Band"; | ||
43 | + | ||
44 | + @Override | ||
45 | + public ObjectNode encode(Band band, CodecContext context) { | ||
46 | + checkNotNull(band, "Band cannot be null"); | ||
47 | + | ||
48 | + ObjectNode result = context.mapper().createObjectNode() | ||
49 | + .put(TYPE, band.type().toString()) | ||
50 | + .put(RATE, band.rate()) | ||
51 | + .put(PACKETS, band.packets()) | ||
52 | + .put(BYTES, band.bytes()) | ||
53 | + .put(BURST_SIZE, band.burst()); | ||
54 | + | ||
55 | + if (band.dropPrecedence() != null) { | ||
56 | + result.put(PREC, band.dropPrecedence()); | ||
57 | + } | ||
58 | + | ||
59 | + return result; | ||
60 | + } | ||
61 | + | ||
62 | + @Override | ||
63 | + public Band decode(ObjectNode json, CodecContext context) { | ||
64 | + if (json == null || !json.isObject()) { | ||
65 | + return null; | ||
66 | + } | ||
67 | + | ||
68 | + // parse rate | ||
69 | + long rate = nullIsIllegal(json.get(RATE), RATE + MISSING_MEMBER_MESSAGE).asLong(); | ||
70 | + | ||
71 | + // parse burst size | ||
72 | + long burstSize = nullIsIllegal(json.get(BURST_SIZE), BURST_SIZE + MISSING_MEMBER_MESSAGE).asLong(); | ||
73 | + | ||
74 | + // parse precedence | ||
75 | + Short precedence = null; | ||
76 | + | ||
77 | + // parse band type | ||
78 | + String typeStr = nullIsIllegal(json.get(TYPE), TYPE + MISSING_MEMBER_MESSAGE).asText(); | ||
79 | + Band.Type type; | ||
80 | + switch (typeStr) { | ||
81 | + case "DROP": | ||
82 | + type = Band.Type.DROP; | ||
83 | + break; | ||
84 | + case "REMARK": | ||
85 | + type = Band.Type.REMARK; | ||
86 | + precedence = (short) nullIsIllegal(json.get(PREC), PREC + MISSING_MEMBER_MESSAGE).asInt(); | ||
87 | + break; | ||
88 | + default: | ||
89 | + log.warn("The requested type {} is not defined for band.", typeStr); | ||
90 | + return null; | ||
91 | + } | ||
92 | + | ||
93 | + Band band = DefaultBand.builder() | ||
94 | + .ofType(type) | ||
95 | + .burstSize(burstSize) | ||
96 | + .withRate(rate) | ||
97 | + .dropPrecedence(precedence) | ||
98 | + .build(); | ||
99 | + | ||
100 | + return band; | ||
101 | + } | ||
102 | +} |
1 | +/* | ||
2 | + * Copyright 2014-2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.codec.impl; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import com.fasterxml.jackson.databind.node.ArrayNode; | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import org.onosproject.codec.CodecContext; | ||
22 | +import org.onosproject.codec.JsonCodec; | ||
23 | +import org.onosproject.core.ApplicationId; | ||
24 | +import org.onosproject.core.CoreService; | ||
25 | +import org.onosproject.net.DeviceId; | ||
26 | +import org.onosproject.net.meter.Band; | ||
27 | +import org.onosproject.net.meter.DefaultMeter; | ||
28 | +import org.onosproject.net.meter.Meter; | ||
29 | +import org.onosproject.net.meter.MeterId; | ||
30 | +import org.slf4j.Logger; | ||
31 | + | ||
32 | +import java.util.ArrayList; | ||
33 | +import java.util.List; | ||
34 | +import java.util.stream.IntStream; | ||
35 | + | ||
36 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
37 | +import static org.onlab.util.Tools.nullIsIllegal; | ||
38 | +import static org.slf4j.LoggerFactory.getLogger; | ||
39 | + | ||
40 | + | ||
41 | +/** | ||
42 | + * Meter JSON codec. | ||
43 | + */ | ||
44 | +public final class MeterCodec extends JsonCodec<Meter> { | ||
45 | + private final Logger log = getLogger(getClass()); | ||
46 | + | ||
47 | + // JSON field names | ||
48 | + private static final String ID = "id"; | ||
49 | + private static final String STATE = "state"; | ||
50 | + private static final String LIFE = "life"; | ||
51 | + private static final String PACKETS = "packets"; | ||
52 | + private static final String BYTES = "bytes"; | ||
53 | + private static final String REFERENCE_COUNT = "referenceCount"; | ||
54 | + private static final String APP_ID = "appId"; | ||
55 | + private static final String BURST = "burst"; | ||
56 | + private static final String DEVICE_ID = "deviceId"; | ||
57 | + private static final String UNIT = "unit"; | ||
58 | + private static final String BANDS = "bands"; | ||
59 | + public static final String REST_APP_ID = "org.onosproject.rest"; | ||
60 | + private static final String MISSING_MEMBER_MESSAGE = " member is required in Meter"; | ||
61 | + | ||
62 | + @Override | ||
63 | + public ObjectNode encode(Meter meter, CodecContext context) { | ||
64 | + checkNotNull(meter, "Meter cannot be null"); | ||
65 | + ObjectNode result = context.mapper().createObjectNode() | ||
66 | + .put(ID, meter.id().toString()) | ||
67 | + .put(LIFE, meter.life()) | ||
68 | + .put(PACKETS, meter.packetsSeen()) | ||
69 | + .put(BYTES, meter.bytesSeen()) | ||
70 | + .put(REFERENCE_COUNT, meter.referenceCount()) | ||
71 | + .put(UNIT, meter.unit().toString()) | ||
72 | + .put(BURST, meter.isBurst()) | ||
73 | + .put(DEVICE_ID, meter.deviceId().toString()); | ||
74 | + | ||
75 | + if (meter.appId() != null) { | ||
76 | + result.put(APP_ID, meter.appId().toString()); | ||
77 | + } | ||
78 | + | ||
79 | + if (meter.state() != null) { | ||
80 | + result.put(STATE, meter.state().toString()); | ||
81 | + } | ||
82 | + | ||
83 | + ArrayNode bands = context.mapper().createArrayNode(); | ||
84 | + meter.bands().forEach(band -> { | ||
85 | + ObjectNode bandJson = context.codec(Band.class).encode(band, context); | ||
86 | + bands.add(bandJson); | ||
87 | + }); | ||
88 | + result.set(BANDS, bands); | ||
89 | + return result; | ||
90 | + } | ||
91 | + | ||
92 | + @Override | ||
93 | + public Meter decode(ObjectNode json, CodecContext context) { | ||
94 | + if (json == null || !json.isObject()) { | ||
95 | + return null; | ||
96 | + } | ||
97 | + | ||
98 | + final JsonCodec<Band> meterBandCodec = context.codec(Band.class); | ||
99 | + CoreService coreService = context.getService(CoreService.class); | ||
100 | + | ||
101 | + // parse meter id | ||
102 | + int meterIdInt = nullIsIllegal(json.get(ID), ID + MISSING_MEMBER_MESSAGE).asInt(); | ||
103 | + MeterId meterId = MeterId.meterId(meterIdInt); | ||
104 | + | ||
105 | + // parse device id | ||
106 | + DeviceId deviceId = DeviceId.deviceId(nullIsIllegal(json.get(DEVICE_ID), | ||
107 | + DEVICE_ID + MISSING_MEMBER_MESSAGE).asText()); | ||
108 | + | ||
109 | + // application id | ||
110 | + ApplicationId appId = coreService.registerApplication(REST_APP_ID); | ||
111 | + | ||
112 | + // parse burst | ||
113 | + boolean burst = false; | ||
114 | + JsonNode burstJson = json.get("burst"); | ||
115 | + if (burstJson != null) { | ||
116 | + burst = burstJson.asBoolean(); | ||
117 | + } | ||
118 | + | ||
119 | + // parse unit type | ||
120 | + String unit = nullIsIllegal(json.get(UNIT), UNIT + MISSING_MEMBER_MESSAGE).asText(); | ||
121 | + Meter.Unit meterUnit; | ||
122 | + | ||
123 | + switch (unit) { | ||
124 | + case "KB_PER_SEC": | ||
125 | + meterUnit = Meter.Unit.KB_PER_SEC; | ||
126 | + break; | ||
127 | + case "PKTS_PER_SEC": | ||
128 | + meterUnit = Meter.Unit.PKTS_PER_SEC; | ||
129 | + break; | ||
130 | + default: | ||
131 | + log.warn("The requested unit {} is not defined for meter.", unit); | ||
132 | + return null; | ||
133 | + } | ||
134 | + | ||
135 | + // parse meter bands | ||
136 | + List<Band> bandList = new ArrayList<>(); | ||
137 | + JsonNode bandsJson = json.get(BANDS); | ||
138 | + checkNotNull(bandsJson); | ||
139 | + if (bandsJson != null) { | ||
140 | + IntStream.range(0, bandsJson.size()).forEach(i -> { | ||
141 | + ObjectNode bandJson = get(bandsJson, i); | ||
142 | + bandList.add(meterBandCodec.decode(bandJson, context)); | ||
143 | + }); | ||
144 | + } | ||
145 | + | ||
146 | + Meter meter; | ||
147 | + if (burst) { | ||
148 | + meter = DefaultMeter.builder() | ||
149 | + .withId(meterId) | ||
150 | + .fromApp(appId) | ||
151 | + .forDevice(deviceId) | ||
152 | + .withUnit(meterUnit) | ||
153 | + .withBands(bandList) | ||
154 | + .burst().build(); | ||
155 | + } else { | ||
156 | + meter = DefaultMeter.builder() | ||
157 | + .withId(meterId) | ||
158 | + .fromApp(appId) | ||
159 | + .forDevice(deviceId) | ||
160 | + .withUnit(meterUnit) | ||
161 | + .withBands(bandList).build(); | ||
162 | + } | ||
163 | + | ||
164 | + return meter; | ||
165 | + } | ||
166 | +} |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.codec.impl; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import org.hamcrest.Description; | ||
20 | +import org.hamcrest.TypeSafeDiagnosingMatcher; | ||
21 | +import org.onosproject.net.meter.Band; | ||
22 | + | ||
23 | +/** | ||
24 | + * Hamcrest matcher for bands. | ||
25 | + */ | ||
26 | +public final class MeterBandJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> { | ||
27 | + | ||
28 | + private final Band band; | ||
29 | + | ||
30 | + private MeterBandJsonMatcher(Band band) { | ||
31 | + this.band = band; | ||
32 | + } | ||
33 | + | ||
34 | + /** | ||
35 | + * Matches the contents of a meter band. | ||
36 | + * | ||
37 | + * @param bandJson JSON representation of band to match | ||
38 | + * @param description Description object used for recording errors | ||
39 | + * @return true if contents match, false otherwise | ||
40 | + */ | ||
41 | + @Override | ||
42 | + protected boolean matchesSafely(JsonNode bandJson, Description description) { | ||
43 | + // check type | ||
44 | + final String jsonType = bandJson.get("type").textValue(); | ||
45 | + if (!band.type().name().equals(jsonType)) { | ||
46 | + description.appendText("type was " + jsonType); | ||
47 | + return false; | ||
48 | + } | ||
49 | + | ||
50 | + // check rate | ||
51 | + final long jsonRate = bandJson.get("rate").longValue(); | ||
52 | + if (band.rate() != jsonRate) { | ||
53 | + description.appendText("rate was " + jsonRate); | ||
54 | + return false; | ||
55 | + } | ||
56 | + | ||
57 | + // check burst size | ||
58 | + final long jsonBurstSize = bandJson.get("burstSize").longValue(); | ||
59 | + if (band.burst() != jsonBurstSize) { | ||
60 | + description.appendText("burst size was " + jsonBurstSize); | ||
61 | + return false; | ||
62 | + } | ||
63 | + | ||
64 | + // check precedence | ||
65 | + final JsonNode jsonNodePrec = bandJson.get("prec"); | ||
66 | + if (jsonNodePrec != null) { | ||
67 | + if (band.dropPrecedence() != jsonNodePrec.shortValue()) { | ||
68 | + description.appendText("drop precedence was " + jsonNodePrec.shortValue()); | ||
69 | + return false; | ||
70 | + } | ||
71 | + } | ||
72 | + | ||
73 | + // check packets | ||
74 | + final JsonNode jsonNodePackets = bandJson.get("packets"); | ||
75 | + if (jsonNodePackets != null) { | ||
76 | + if (band.packets() != jsonNodePackets.asLong()) { | ||
77 | + description.appendText("packets was " + jsonNodePackets.asLong()); | ||
78 | + return false; | ||
79 | + } | ||
80 | + } | ||
81 | + | ||
82 | + final JsonNode jsonNodeBytes = bandJson.get("bytes"); | ||
83 | + if (jsonNodeBytes != null) { | ||
84 | + if (band.bytes() != jsonNodeBytes.asLong()) { | ||
85 | + description.appendText("bytes was " + jsonNodeBytes.asLong()); | ||
86 | + return false; | ||
87 | + } | ||
88 | + } | ||
89 | + | ||
90 | + return true; | ||
91 | + } | ||
92 | + | ||
93 | + @Override | ||
94 | + public void describeTo(Description description) { | ||
95 | + description.appendText(band.toString()); | ||
96 | + } | ||
97 | + | ||
98 | + /** | ||
99 | + * Factory to allocate a band matcher. | ||
100 | + * | ||
101 | + * @param band band object we are looking for | ||
102 | + * @return matcher | ||
103 | + */ | ||
104 | + public static MeterBandJsonMatcher matchesMeterBand(Band band) { | ||
105 | + return new MeterBandJsonMatcher(band); | ||
106 | + } | ||
107 | +} |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.codec.impl; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
20 | +import com.google.common.collect.ImmutableList; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.onosproject.codec.JsonCodec; | ||
24 | +import org.onosproject.core.CoreService; | ||
25 | +import org.onosproject.net.NetTestTools; | ||
26 | +import org.onosproject.net.meter.Band; | ||
27 | +import org.onosproject.net.meter.DefaultBand; | ||
28 | +import org.onosproject.net.meter.DefaultMeter; | ||
29 | +import org.onosproject.net.meter.Meter; | ||
30 | +import org.onosproject.net.meter.MeterId; | ||
31 | + | ||
32 | +import java.io.IOException; | ||
33 | +import java.io.InputStream; | ||
34 | + | ||
35 | +import static org.easymock.EasyMock.createMock; | ||
36 | +import static org.easymock.EasyMock.expect; | ||
37 | +import static org.easymock.EasyMock.replay; | ||
38 | +import static org.hamcrest.MatcherAssert.assertThat; | ||
39 | +import static org.hamcrest.Matchers.is; | ||
40 | +import static org.hamcrest.Matchers.notNullValue; | ||
41 | +import static org.onosproject.codec.impl.MeterJsonMatcher.matchesMeter; | ||
42 | +import static org.onosproject.net.NetTestTools.APP_ID; | ||
43 | + | ||
44 | +/** | ||
45 | + * Unit tests for Meter codec. | ||
46 | + */ | ||
47 | +public class MeterCodecTest { | ||
48 | + | ||
49 | + MockCodecContext context; | ||
50 | + JsonCodec<Meter> meterCodec; | ||
51 | + final CoreService mockCoreService = createMock(CoreService.class); | ||
52 | + | ||
53 | + /** | ||
54 | + * Sets up for each test. Creates a context and fetches the flow rule | ||
55 | + * codec. | ||
56 | + */ | ||
57 | + @Before | ||
58 | + public void setUp() { | ||
59 | + context = new MockCodecContext(); | ||
60 | + meterCodec = context.codec(Meter.class); | ||
61 | + assertThat(meterCodec, notNullValue()); | ||
62 | + | ||
63 | + expect(mockCoreService.registerApplication(MeterCodec.REST_APP_ID)) | ||
64 | + .andReturn(APP_ID).anyTimes(); | ||
65 | + replay(mockCoreService); | ||
66 | + context.registerService(CoreService.class, mockCoreService); | ||
67 | + } | ||
68 | + | ||
69 | + /** | ||
70 | + * Tests encoding of a Meter object. | ||
71 | + */ | ||
72 | + @Test | ||
73 | + public void testMeterEncode() { | ||
74 | + Band band1 = DefaultBand.builder() | ||
75 | + .ofType(Band.Type.DROP) | ||
76 | + .burstSize(10) | ||
77 | + .withRate(10).build(); | ||
78 | + Band band2 = DefaultBand.builder() | ||
79 | + .ofType(Band.Type.REMARK) | ||
80 | + .burstSize(10) | ||
81 | + .withRate(10) | ||
82 | + .dropPrecedence((short) 10).build(); | ||
83 | + | ||
84 | + Meter meter = DefaultMeter.builder() | ||
85 | + .fromApp(APP_ID) | ||
86 | + .withId(MeterId.meterId(1L)) | ||
87 | + .forDevice(NetTestTools.did("d1")) | ||
88 | + .withBands(ImmutableList.of(band1, band2)) | ||
89 | + .withUnit(Meter.Unit.KB_PER_SEC).build(); | ||
90 | + | ||
91 | + ObjectNode meterJson = meterCodec.encode(meter, context); | ||
92 | + assertThat(meterJson, matchesMeter(meter)); | ||
93 | + } | ||
94 | + | ||
95 | + /** | ||
96 | + * Test decoding of a Meter object. | ||
97 | + */ | ||
98 | + @Test | ||
99 | + public void testMeterDecode() throws IOException { | ||
100 | + Meter meter = getMeter("simple-meter.json"); | ||
101 | + checkCommonData(meter); | ||
102 | + | ||
103 | + assertThat(meter.bands().size(), is(1)); | ||
104 | + Band band = meter.bands().iterator().next(); | ||
105 | + assertThat(band.type().toString(), is("REMARK")); | ||
106 | + assertThat(band.rate(), is(10L)); | ||
107 | + assertThat(band.dropPrecedence(), is((short) 20)); | ||
108 | + assertThat(band.burst(), is(30L)); | ||
109 | + } | ||
110 | + | ||
111 | + /** | ||
112 | + * Checks that the data shared by all the resource is correct for a given meter. | ||
113 | + * | ||
114 | + * @param meter meter to check | ||
115 | + */ | ||
116 | + private void checkCommonData(Meter meter) { | ||
117 | + assertThat(meter.id().id(), is(1L)); | ||
118 | + assertThat(meter.deviceId().toString(), is("of:0000000000000001")); | ||
119 | + assertThat(meter.appId(), is(APP_ID)); | ||
120 | + assertThat(meter.unit().toString(), is("KB_PER_SEC")); | ||
121 | + } | ||
122 | + | ||
123 | + /** | ||
124 | + * Reads in a meter from the given resource and decodes it. | ||
125 | + * | ||
126 | + * @param resourceName resource to use to read the JSON for the rule | ||
127 | + * @return decoded meter | ||
128 | + * @throws IOException if processing the resource fails | ||
129 | + */ | ||
130 | + private Meter getMeter(String resourceName) throws IOException { | ||
131 | + InputStream jsonStream = MeterCodecTest.class.getResourceAsStream(resourceName); | ||
132 | + JsonNode json = context.mapper().readTree(jsonStream); | ||
133 | + assertThat(json, notNullValue()); | ||
134 | + Meter meter = meterCodec.decode((ObjectNode) json, context); | ||
135 | + assertThat(meter, notNullValue()); | ||
136 | + return meter; | ||
137 | + } | ||
138 | +} |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.codec.impl; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import org.hamcrest.Description; | ||
20 | +import org.hamcrest.TypeSafeDiagnosingMatcher; | ||
21 | +import org.onosproject.net.meter.Band; | ||
22 | +import org.onosproject.net.meter.Meter; | ||
23 | + | ||
24 | +/** | ||
25 | + * Hamcrest matcher for meters. | ||
26 | + */ | ||
27 | +public final class MeterJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> { | ||
28 | + | ||
29 | + private final Meter meter; | ||
30 | + | ||
31 | + private MeterJsonMatcher(Meter meter) { | ||
32 | + this.meter = meter; | ||
33 | + } | ||
34 | + | ||
35 | + @Override | ||
36 | + protected boolean matchesSafely(JsonNode jsonMeter, Description description) { | ||
37 | + // check id | ||
38 | + String jsonMeterId = jsonMeter.get("id").asText(); | ||
39 | + String meterId = meter.id().toString(); | ||
40 | + if (!jsonMeterId.equals(meterId)) { | ||
41 | + description.appendText("meter id was " + jsonMeterId); | ||
42 | + return false; | ||
43 | + } | ||
44 | + | ||
45 | + // check unit | ||
46 | + String jsonUnit = jsonMeter.get("unit").asText(); | ||
47 | + String unit = meter.unit().toString(); | ||
48 | + if (!jsonUnit.equals(unit)) { | ||
49 | + description.appendText("unit was " + jsonUnit); | ||
50 | + return false; | ||
51 | + } | ||
52 | + | ||
53 | + // check burst | ||
54 | + boolean jsonBurst = jsonMeter.get("burst").asBoolean(); | ||
55 | + boolean burst = meter.isBurst(); | ||
56 | + if (jsonBurst != burst) { | ||
57 | + description.appendText("isBurst was " + jsonBurst); | ||
58 | + return false; | ||
59 | + } | ||
60 | + | ||
61 | + // check state | ||
62 | + JsonNode jsonNodeState = jsonMeter.get("state"); | ||
63 | + if (jsonNodeState != null) { | ||
64 | + String state = meter.state().toString(); | ||
65 | + if (!jsonNodeState.asText().equals(state)) { | ||
66 | + description.appendText("state was " + jsonNodeState.asText()); | ||
67 | + return false; | ||
68 | + } | ||
69 | + } | ||
70 | + | ||
71 | + // check life | ||
72 | + JsonNode jsonNodeLife = jsonMeter.get("life"); | ||
73 | + if (jsonNodeLife != null) { | ||
74 | + long life = meter.life(); | ||
75 | + if (jsonNodeLife.asLong() != life) { | ||
76 | + description.appendText("life was " + jsonNodeLife.asLong()); | ||
77 | + return false; | ||
78 | + } | ||
79 | + } | ||
80 | + | ||
81 | + // check bytes | ||
82 | + JsonNode jsonNodeBytes = jsonMeter.get("bytes"); | ||
83 | + if (jsonNodeBytes != null) { | ||
84 | + long bytes = meter.bytesSeen(); | ||
85 | + if (jsonNodeBytes.asLong() != bytes) { | ||
86 | + description.appendText("bytes was " + jsonNodeBytes.asLong()); | ||
87 | + return false; | ||
88 | + } | ||
89 | + } | ||
90 | + | ||
91 | + // check packets | ||
92 | + JsonNode jsonNodePackets = jsonMeter.get("packets"); | ||
93 | + if (jsonNodePackets != null) { | ||
94 | + long packets = meter.packetsSeen(); | ||
95 | + if (jsonNodePackets.asLong() != packets) { | ||
96 | + description.appendText("packets was " + jsonNodePackets.asLong()); | ||
97 | + return false; | ||
98 | + } | ||
99 | + } | ||
100 | + | ||
101 | + // check size of band array | ||
102 | + JsonNode jsonBands = jsonMeter.get("bands"); | ||
103 | + if (jsonBands.size() != meter.bands().size()) { | ||
104 | + description.appendText("bands size was " + jsonBands.size()); | ||
105 | + return false; | ||
106 | + } | ||
107 | + | ||
108 | + // check bands | ||
109 | + for (Band band : meter.bands()) { | ||
110 | + boolean bandFound = false; | ||
111 | + for (int bandIndex = 0; bandIndex < jsonBands.size(); bandIndex++) { | ||
112 | + MeterBandJsonMatcher bandMatcher = MeterBandJsonMatcher.matchesMeterBand(band); | ||
113 | + if (bandMatcher.matches(jsonBands.get(bandIndex))) { | ||
114 | + bandFound = true; | ||
115 | + break; | ||
116 | + } | ||
117 | + } | ||
118 | + if (!bandFound) { | ||
119 | + description.appendText("band not found " + band.toString()); | ||
120 | + return false; | ||
121 | + } | ||
122 | + } | ||
123 | + | ||
124 | + return true; | ||
125 | + } | ||
126 | + | ||
127 | + @Override | ||
128 | + public void describeTo(Description description) { | ||
129 | + description.appendText(meter.toString()); | ||
130 | + } | ||
131 | + | ||
132 | + /** | ||
133 | + * Factory to allocate a meter matcher. | ||
134 | + * | ||
135 | + * @param meter meter object we are looking for | ||
136 | + * @return matcher | ||
137 | + */ | ||
138 | + public static MeterJsonMatcher matchesMeter(Meter meter) { | ||
139 | + return new MeterJsonMatcher(meter); | ||
140 | + } | ||
141 | +} |
... | @@ -38,6 +38,7 @@ public class CoreWebApplication extends AbstractWebApplication { | ... | @@ -38,6 +38,7 @@ public class CoreWebApplication extends AbstractWebApplication { |
38 | IntentsWebResource.class, | 38 | IntentsWebResource.class, |
39 | FlowsWebResource.class, | 39 | FlowsWebResource.class, |
40 | GroupsWebResource.class, | 40 | GroupsWebResource.class, |
41 | + MetersWebResource.class, | ||
41 | TopologyWebResource.class, | 42 | TopologyWebResource.class, |
42 | ConfigWebResource.class, | 43 | ConfigWebResource.class, |
43 | PathsWebResource.class, | 44 | PathsWebResource.class, | ... | ... |
1 | +/* | ||
2 | + * Copyright 2014-2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.rest.resources; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import com.fasterxml.jackson.databind.node.ArrayNode; | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import org.onosproject.net.DeviceId; | ||
22 | +import org.onosproject.net.meter.DefaultMeterRequest; | ||
23 | +import org.onosproject.net.meter.Meter; | ||
24 | +import org.onosproject.net.meter.MeterId; | ||
25 | +import org.onosproject.net.meter.MeterRequest; | ||
26 | +import org.onosproject.net.meter.MeterService; | ||
27 | +import org.onosproject.rest.AbstractWebResource; | ||
28 | +import org.slf4j.Logger; | ||
29 | + | ||
30 | +import javax.ws.rs.Consumes; | ||
31 | +import javax.ws.rs.DELETE; | ||
32 | +import javax.ws.rs.GET; | ||
33 | +import javax.ws.rs.POST; | ||
34 | +import javax.ws.rs.Path; | ||
35 | +import javax.ws.rs.PathParam; | ||
36 | +import javax.ws.rs.Produces; | ||
37 | +import javax.ws.rs.core.MediaType; | ||
38 | +import javax.ws.rs.core.Response; | ||
39 | +import java.io.IOException; | ||
40 | +import java.io.InputStream; | ||
41 | +import java.net.URI; | ||
42 | +import java.net.URISyntaxException; | ||
43 | + | ||
44 | +import static org.onlab.util.Tools.nullIsNotFound; | ||
45 | +import static org.slf4j.LoggerFactory.getLogger; | ||
46 | + | ||
47 | +/** | ||
48 | + * Query and program meter rules. | ||
49 | + */ | ||
50 | +@Path("meters") | ||
51 | +public class MetersWebResource extends AbstractWebResource { | ||
52 | + private final Logger log = getLogger(getClass()); | ||
53 | + public static final String DEVICE_INVALID = "Invalid deviceId in meter creation request"; | ||
54 | + | ||
55 | + final MeterService meterService = get(MeterService.class); | ||
56 | + final ObjectNode root = mapper().createObjectNode(); | ||
57 | + final ArrayNode metersNode = root.putArray("meters"); | ||
58 | + | ||
59 | + /** | ||
60 | + * Returns all meters of all devices. | ||
61 | + * | ||
62 | + * @return array of all the meters in the system | ||
63 | + * @onos.rsModel Meters | ||
64 | + */ | ||
65 | + @GET | ||
66 | + @Produces(MediaType.APPLICATION_JSON) | ||
67 | + public Response getMeters() { | ||
68 | + final Iterable<Meter> meters = meterService.getAllMeters(); | ||
69 | + if (meters != null) { | ||
70 | + meters.forEach(meter -> metersNode.add(codec(Meter.class).encode(meter, this))); | ||
71 | + } | ||
72 | + return ok(root).build(); | ||
73 | + } | ||
74 | + | ||
75 | + /** | ||
76 | + * Returns a meter by the meter id. | ||
77 | + * | ||
78 | + * @param deviceId device identifier | ||
79 | + * @return array of all the groups in the system | ||
80 | + * @onos.rsModel Meter | ||
81 | + */ | ||
82 | + @GET | ||
83 | + @Produces(MediaType.APPLICATION_JSON) | ||
84 | + @Path("{deviceId}/{meterId}") | ||
85 | + public Response getMeterByDeviceIdAndMeterId(@PathParam("deviceId") String deviceId, | ||
86 | + @PathParam("meterId") String meterId) { | ||
87 | + DeviceId did = DeviceId.deviceId(deviceId); | ||
88 | + MeterId mid = MeterId.meterId(Long.valueOf(meterId)); | ||
89 | + | ||
90 | + final Meter meter = nullIsNotFound(meterService.getMeter(did, mid), | ||
91 | + "Meter is not found for " + mid.id()); | ||
92 | + | ||
93 | + metersNode.add(codec(Meter.class).encode(meter, this)); | ||
94 | + return ok(root).build(); | ||
95 | + } | ||
96 | + | ||
97 | + /** | ||
98 | + * Create new meter rule. Creates and installs a new meter rule for the | ||
99 | + * specified device. | ||
100 | + * | ||
101 | + * @param deviceId device identifier | ||
102 | + * @param stream meter rule JSON | ||
103 | + * @return status of the request - CREATED if the JSON is correct, | ||
104 | + * BAD_REQUEST if the JSON is invalid | ||
105 | + * @onos.rsModel MeterPost | ||
106 | + */ | ||
107 | + @POST | ||
108 | + @Path("{deviceId}") | ||
109 | + @Consumes(MediaType.APPLICATION_JSON) | ||
110 | + @Produces(MediaType.APPLICATION_JSON) | ||
111 | + public Response createMeter(@PathParam("deviceId") String deviceId, | ||
112 | + InputStream stream) { | ||
113 | + URI location; | ||
114 | + try { | ||
115 | + ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); | ||
116 | + JsonNode specifiedDeviceId = jsonTree.get("deviceId"); | ||
117 | + | ||
118 | + if (specifiedDeviceId != null && | ||
119 | + !specifiedDeviceId.asText().equals(deviceId)) { | ||
120 | + throw new IllegalArgumentException(DEVICE_INVALID); | ||
121 | + } | ||
122 | + jsonTree.put("deviceId", deviceId); | ||
123 | + final Meter tmpMeter = codec(Meter.class).decode(jsonTree, this); | ||
124 | + final MeterRequest meterRequest = meterToMeterRequest(tmpMeter, "ADD"); | ||
125 | + final Meter meter = meterService.submit(meterRequest); | ||
126 | + location = new URI(Long.toString(meter.id().id())); | ||
127 | + } catch (IOException | URISyntaxException ex) { | ||
128 | + throw new IllegalArgumentException(ex); | ||
129 | + } | ||
130 | + | ||
131 | + return Response | ||
132 | + .created(location) | ||
133 | + .build(); | ||
134 | + } | ||
135 | + | ||
136 | + /** | ||
137 | + * Removes the specified meter. | ||
138 | + * | ||
139 | + * @param deviceId device identifier | ||
140 | + * @param meterId meter identifier | ||
141 | + */ | ||
142 | + @DELETE | ||
143 | + @Produces(MediaType.APPLICATION_JSON) | ||
144 | + @Path("{deviceId}/{meterId}") | ||
145 | + public void deleteMeterByDeviceIdAndMeterId(@PathParam("deviceId") String deviceId, | ||
146 | + @PathParam("meterId") String meterId) { | ||
147 | + DeviceId did = DeviceId.deviceId(deviceId); | ||
148 | + MeterId mid = MeterId.meterId(Long.valueOf(meterId)); | ||
149 | + final Meter tmpMeter = meterService.getMeter(did, mid); | ||
150 | + if (tmpMeter != null) { | ||
151 | + final MeterRequest meterRequest = meterToMeterRequest(tmpMeter, "REMOVE"); | ||
152 | + meterService.withdraw(meterRequest, tmpMeter.id()); | ||
153 | + } | ||
154 | + } | ||
155 | + | ||
156 | + /** | ||
157 | + * Convert a meter instance to meterRequest instance with a certain operation. | ||
158 | + * | ||
159 | + * @param meter meter instance | ||
160 | + * @param operation operation | ||
161 | + * @return converted meterRequest instance | ||
162 | + */ | ||
163 | + private MeterRequest meterToMeterRequest(Meter meter, String operation) { | ||
164 | + MeterRequest.Builder builder; | ||
165 | + MeterRequest meterRequest; | ||
166 | + | ||
167 | + if (meter == null) { | ||
168 | + return null; | ||
169 | + } | ||
170 | + | ||
171 | + if (meter.isBurst()) { | ||
172 | + builder = DefaultMeterRequest.builder() | ||
173 | + .fromApp(meter.appId()) | ||
174 | + .forDevice(meter.deviceId()) | ||
175 | + .withUnit(meter.unit()) | ||
176 | + .withBands(meter.bands()) | ||
177 | + .burst(); | ||
178 | + } else { | ||
179 | + builder = DefaultMeterRequest.builder() | ||
180 | + .fromApp(meter.appId()) | ||
181 | + .forDevice(meter.deviceId()) | ||
182 | + .withUnit(meter.unit()) | ||
183 | + .withBands(meter.bands()); | ||
184 | + } | ||
185 | + | ||
186 | + switch (operation) { | ||
187 | + case "ADD": | ||
188 | + meterRequest = builder.add(); | ||
189 | + break; | ||
190 | + case "REMOVE": | ||
191 | + meterRequest = builder.remove(); | ||
192 | + break; | ||
193 | + default: | ||
194 | + log.warn("Invalid operation {}.", operation); | ||
195 | + return null; | ||
196 | + } | ||
197 | + | ||
198 | + return meterRequest; | ||
199 | + } | ||
200 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{ | ||
2 | + "type": "object", | ||
3 | + "title": "meter", | ||
4 | + "required": [ | ||
5 | + "id", | ||
6 | + "appId", | ||
7 | + "deviceId", | ||
8 | + "unit", | ||
9 | + "burst", | ||
10 | + "state", | ||
11 | + "life", | ||
12 | + "refCount", | ||
13 | + "packets", | ||
14 | + "bytes", | ||
15 | + "bands" | ||
16 | + ], | ||
17 | + "properties": { | ||
18 | + "id": { | ||
19 | + "type": "string", | ||
20 | + "example": "1" | ||
21 | + }, | ||
22 | + "appId": { | ||
23 | + "type": "string", | ||
24 | + "example": "1" | ||
25 | + }, | ||
26 | + "deviceId": { | ||
27 | + "type": "string", | ||
28 | + "example": "of:0000000000000001" | ||
29 | + }, | ||
30 | + "unit": { | ||
31 | + "type": "string", | ||
32 | + "example": "KB_PER_SEC" | ||
33 | + }, | ||
34 | + "burst": { | ||
35 | + "type": "boolean", | ||
36 | + "example": true | ||
37 | + }, | ||
38 | + "state": { | ||
39 | + "type": "string", | ||
40 | + "example": "ADDED" | ||
41 | + }, | ||
42 | + "life": { | ||
43 | + "type": "integer", | ||
44 | + "format": "int64", | ||
45 | + "example": 0 | ||
46 | + }, | ||
47 | + "refCount": { | ||
48 | + "type": "integer", | ||
49 | + "format": "int64", | ||
50 | + "example": 0 | ||
51 | + }, | ||
52 | + "packets": { | ||
53 | + "type": "integer", | ||
54 | + "format": "int64", | ||
55 | + "example": 0 | ||
56 | + }, | ||
57 | + "bytes": { | ||
58 | + "type": "integer", | ||
59 | + "format": "int64", | ||
60 | + "example": 0 | ||
61 | + }, | ||
62 | + "bands": { | ||
63 | + "type": "array", | ||
64 | + "xml": { | ||
65 | + "name": "bands", | ||
66 | + "wrapped": true | ||
67 | + }, | ||
68 | + "items": { | ||
69 | + "type": "object", | ||
70 | + "title": "bands", | ||
71 | + "required": [ | ||
72 | + "type", | ||
73 | + "rate", | ||
74 | + "burstSize", | ||
75 | + "prec", | ||
76 | + "packets", | ||
77 | + "bytes" | ||
78 | + ], | ||
79 | + "properties": { | ||
80 | + "type": { | ||
81 | + "type": "string", | ||
82 | + "example": "REMARK" | ||
83 | + }, | ||
84 | + "rate": { | ||
85 | + "type": "integer", | ||
86 | + "format": "int64", | ||
87 | + "example": 0 | ||
88 | + }, | ||
89 | + "burstSize": { | ||
90 | + "type": "integer", | ||
91 | + "format": "int64", | ||
92 | + "example": 0 | ||
93 | + }, | ||
94 | + "prec": { | ||
95 | + "type": "integer", | ||
96 | + "format": "int16", | ||
97 | + "example": 0 | ||
98 | + }, | ||
99 | + "packets": { | ||
100 | + "type": "integer", | ||
101 | + "format": "int64", | ||
102 | + "example": 0 | ||
103 | + }, | ||
104 | + "bytes": { | ||
105 | + "type": "integer", | ||
106 | + "format": "int64", | ||
107 | + "example": 0 | ||
108 | + } | ||
109 | + } | ||
110 | + } | ||
111 | + } | ||
112 | + } | ||
113 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{ | ||
2 | + "type": "object", | ||
3 | + "title": "meter", | ||
4 | + "required": [ | ||
5 | + "id", | ||
6 | + "deviceId", | ||
7 | + "unit", | ||
8 | + "burst", | ||
9 | + "bands" | ||
10 | + ], | ||
11 | + "properties": { | ||
12 | + "id": { | ||
13 | + "type": "string", | ||
14 | + "example": "1" | ||
15 | + }, | ||
16 | + "deviceId": { | ||
17 | + "type": "string", | ||
18 | + "example": "of:0000000000000001" | ||
19 | + }, | ||
20 | + "unit": { | ||
21 | + "type": "string", | ||
22 | + "example": "KB_PER_SEC" | ||
23 | + }, | ||
24 | + "burst": { | ||
25 | + "type": "boolean", | ||
26 | + "example": true | ||
27 | + }, | ||
28 | + "bands": { | ||
29 | + "type": "array", | ||
30 | + "xml": { | ||
31 | + "name": "bands", | ||
32 | + "wrapped": true | ||
33 | + }, | ||
34 | + "items": { | ||
35 | + "type": "object", | ||
36 | + "title": "bands", | ||
37 | + "required": [ | ||
38 | + "type", | ||
39 | + "rate", | ||
40 | + "burstSize", | ||
41 | + "prec" | ||
42 | + ], | ||
43 | + "properties": { | ||
44 | + "type": { | ||
45 | + "type": "string", | ||
46 | + "example": "REMARK" | ||
47 | + }, | ||
48 | + "rate": { | ||
49 | + "type": "integer", | ||
50 | + "format": "int64", | ||
51 | + "example": "0" | ||
52 | + }, | ||
53 | + "burstSize": { | ||
54 | + "type": "integer", | ||
55 | + "format": "int64", | ||
56 | + "example": "0" | ||
57 | + }, | ||
58 | + "prec": { | ||
59 | + "type": "integer", | ||
60 | + "format": "int16", | ||
61 | + "example": "0" | ||
62 | + } | ||
63 | + } | ||
64 | + } | ||
65 | + } | ||
66 | + } | ||
67 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{ | ||
2 | + "type": "object", | ||
3 | + "title": "meters", | ||
4 | + "required": [ | ||
5 | + "meters" | ||
6 | + ], | ||
7 | + "properties": { | ||
8 | + "groups": { | ||
9 | + "type": "array", | ||
10 | + "xml": { | ||
11 | + "name": "meters", | ||
12 | + "wrapped": true | ||
13 | + }, | ||
14 | + "items": { | ||
15 | + "type": "object", | ||
16 | + "title": "meter", | ||
17 | + "required": [ | ||
18 | + "id", | ||
19 | + "appId", | ||
20 | + "deviceId", | ||
21 | + "unit", | ||
22 | + "burst", | ||
23 | + "state", | ||
24 | + "life", | ||
25 | + "refCount", | ||
26 | + "packets", | ||
27 | + "bytes", | ||
28 | + "bands" | ||
29 | + ], | ||
30 | + "properties": { | ||
31 | + "id": { | ||
32 | + "type": "string", | ||
33 | + "example": "1" | ||
34 | + }, | ||
35 | + "appId": { | ||
36 | + "type": "string", | ||
37 | + "example": "1" | ||
38 | + }, | ||
39 | + "deviceId": { | ||
40 | + "type": "string", | ||
41 | + "example": "of:0000000000000001" | ||
42 | + }, | ||
43 | + "unit": { | ||
44 | + "type": "string", | ||
45 | + "example": "KB_PER_SEC" | ||
46 | + }, | ||
47 | + "burst": { | ||
48 | + "type": "boolean", | ||
49 | + "example": true | ||
50 | + }, | ||
51 | + "state": { | ||
52 | + "type": "string", | ||
53 | + "example": "ADDED" | ||
54 | + }, | ||
55 | + "life": { | ||
56 | + "type": "integer", | ||
57 | + "format": "int64", | ||
58 | + "example": 0 | ||
59 | + }, | ||
60 | + "refCount": { | ||
61 | + "type": "integer", | ||
62 | + "format": "int64", | ||
63 | + "example": 0 | ||
64 | + }, | ||
65 | + "packets": { | ||
66 | + "type": "integer", | ||
67 | + "format": "int64", | ||
68 | + "example": 0 | ||
69 | + }, | ||
70 | + "bytes": { | ||
71 | + "type": "integer", | ||
72 | + "format": "int64", | ||
73 | + "example": 0 | ||
74 | + }, | ||
75 | + "bands": { | ||
76 | + "type": "array", | ||
77 | + "xml": { | ||
78 | + "name": "bands", | ||
79 | + "wrapped": true | ||
80 | + }, | ||
81 | + "items": { | ||
82 | + "type": "object", | ||
83 | + "title": "bands", | ||
84 | + "required": [ | ||
85 | + "type", | ||
86 | + "rate", | ||
87 | + "burstSize", | ||
88 | + "prec", | ||
89 | + "packets", | ||
90 | + "bytes" | ||
91 | + ], | ||
92 | + "properties": { | ||
93 | + "type": { | ||
94 | + "type": "string", | ||
95 | + "example": "REMARK" | ||
96 | + }, | ||
97 | + "rate": { | ||
98 | + "type": "integer", | ||
99 | + "format": "int64", | ||
100 | + "example": 0 | ||
101 | + }, | ||
102 | + "burstSize": { | ||
103 | + "type": "integer", | ||
104 | + "format": "int64", | ||
105 | + "example": 0 | ||
106 | + }, | ||
107 | + "prec": { | ||
108 | + "type": "integer", | ||
109 | + "format": "int16", | ||
110 | + "example": 0 | ||
111 | + }, | ||
112 | + "packets": { | ||
113 | + "type": "integer", | ||
114 | + "format": "int64", | ||
115 | + "example": 0 | ||
116 | + }, | ||
117 | + "bytes": { | ||
118 | + "type": "integer", | ||
119 | + "format": "int64", | ||
120 | + "example": 0 | ||
121 | + } | ||
122 | + } | ||
123 | + } | ||
124 | + } | ||
125 | + } | ||
126 | + } | ||
127 | + } | ||
128 | + } | ||
129 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +/* | ||
2 | + * Copyright 2014-2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +package org.onosproject.rest; | ||
18 | + | ||
19 | +import com.eclipsesource.json.JsonArray; | ||
20 | +import com.eclipsesource.json.JsonObject; | ||
21 | +import com.google.common.collect.ImmutableSet; | ||
22 | +import com.sun.jersey.api.client.ClientResponse; | ||
23 | +import com.sun.jersey.api.client.WebResource; | ||
24 | +import org.hamcrest.Description; | ||
25 | +import org.hamcrest.TypeSafeMatcher; | ||
26 | +import org.junit.After; | ||
27 | +import org.junit.Before; | ||
28 | +import org.junit.Test; | ||
29 | +import org.onlab.osgi.ServiceDirectory; | ||
30 | +import org.onlab.osgi.TestServiceDirectory; | ||
31 | +import org.onlab.rest.BaseResource; | ||
32 | +import org.onosproject.codec.CodecService; | ||
33 | +import org.onosproject.codec.impl.CodecManager; | ||
34 | +import org.onosproject.codec.impl.MeterCodec; | ||
35 | +import org.onosproject.core.ApplicationId; | ||
36 | +import org.onosproject.core.CoreService; | ||
37 | +import org.onosproject.core.DefaultApplicationId; | ||
38 | +import org.onosproject.net.DefaultDevice; | ||
39 | +import org.onosproject.net.Device; | ||
40 | +import org.onosproject.net.DeviceId; | ||
41 | +import org.onosproject.net.NetTestTools; | ||
42 | +import org.onosproject.net.device.DeviceService; | ||
43 | +import org.onosproject.net.meter.Band; | ||
44 | +import org.onosproject.net.meter.DefaultBand; | ||
45 | +import org.onosproject.net.meter.Meter; | ||
46 | +import org.onosproject.net.meter.MeterId; | ||
47 | +import org.onosproject.net.meter.MeterService; | ||
48 | +import org.onosproject.net.meter.MeterState; | ||
49 | + | ||
50 | +import javax.ws.rs.core.MediaType; | ||
51 | +import java.io.InputStream; | ||
52 | +import java.net.HttpURLConnection; | ||
53 | +import java.util.ArrayList; | ||
54 | +import java.util.Collection; | ||
55 | +import java.util.HashMap; | ||
56 | +import java.util.HashSet; | ||
57 | +import java.util.List; | ||
58 | +import java.util.Set; | ||
59 | + | ||
60 | +import static org.easymock.EasyMock.anyObject; | ||
61 | +import static org.easymock.EasyMock.anyShort; | ||
62 | +import static org.easymock.EasyMock.createMock; | ||
63 | +import static org.easymock.EasyMock.expect; | ||
64 | +import static org.easymock.EasyMock.expectLastCall; | ||
65 | +import static org.easymock.EasyMock.replay; | ||
66 | +import static org.easymock.EasyMock.verify; | ||
67 | +import static org.hamcrest.Matchers.hasSize; | ||
68 | +import static org.hamcrest.Matchers.is; | ||
69 | +import static org.junit.Assert.assertThat; | ||
70 | +import static org.hamcrest.Matchers.notNullValue; | ||
71 | +import static org.onosproject.net.NetTestTools.APP_ID; | ||
72 | + | ||
73 | +/** | ||
74 | + * Unit tests for meters REST APIs. | ||
75 | + */ | ||
76 | +public class MetersResourceTest extends ResourceTest { | ||
77 | + final MeterService mockMeterService = createMock(MeterService.class); | ||
78 | + CoreService mockCoreService = createMock(CoreService.class); | ||
79 | + final DeviceService mockDeviceService = createMock(DeviceService.class); | ||
80 | + | ||
81 | + final HashMap<DeviceId, Set<Meter>> meters = new HashMap<>(); | ||
82 | + | ||
83 | + final DeviceId deviceId1 = DeviceId.deviceId("1"); | ||
84 | + final DeviceId deviceId2 = DeviceId.deviceId("2"); | ||
85 | + final DeviceId deviceId3 = DeviceId.deviceId("3"); | ||
86 | + final Device device1 = new DefaultDevice(null, deviceId1, Device.Type.OTHER, | ||
87 | + "", "", "", "", null); | ||
88 | + final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER, | ||
89 | + "", "", "", "", null); | ||
90 | + | ||
91 | + final MockMeter meter1 = new MockMeter(deviceId1, 1, 111, 1); | ||
92 | + final MockMeter meter2 = new MockMeter(deviceId1, 2, 222, 2); | ||
93 | + final MockMeter meter3 = new MockMeter(deviceId2, 3, 333, 3); | ||
94 | + final MockMeter meter4 = new MockMeter(deviceId2, 4, 444, 4); | ||
95 | + final MockMeter meter5 = new MockMeter(deviceId3, 5, 555, 5); | ||
96 | + | ||
97 | + /** | ||
98 | + * Mock class for a meter. | ||
99 | + */ | ||
100 | + private static class MockMeter implements Meter { | ||
101 | + | ||
102 | + final DeviceId deviceId; | ||
103 | + final ApplicationId appId; | ||
104 | + final MeterId meterId; | ||
105 | + final long baseValue; | ||
106 | + final List<Band> bandList; | ||
107 | + | ||
108 | + public MockMeter(DeviceId deviceId, int appId, long meterId, int id) { | ||
109 | + this.deviceId = deviceId; | ||
110 | + this.appId = new DefaultApplicationId(appId, String.valueOf(appId)); | ||
111 | + this.baseValue = id * 200; | ||
112 | + this.meterId = MeterId.meterId(meterId); | ||
113 | + | ||
114 | + Band band = DefaultBand.builder() | ||
115 | + .ofType(Band.Type.REMARK) | ||
116 | + .withRate(10) | ||
117 | + .dropPrecedence((short) 20) | ||
118 | + .burstSize(30).build(); | ||
119 | + | ||
120 | + this.bandList = new ArrayList<>(); | ||
121 | + this.bandList.add(band); | ||
122 | + } | ||
123 | + | ||
124 | + @Override | ||
125 | + public DeviceId deviceId() { | ||
126 | + return this.deviceId; | ||
127 | + } | ||
128 | + | ||
129 | + @Override | ||
130 | + public MeterId id() { | ||
131 | + return this.meterId; | ||
132 | + } | ||
133 | + | ||
134 | + @Override | ||
135 | + public ApplicationId appId() { | ||
136 | + return this.appId; | ||
137 | + } | ||
138 | + | ||
139 | + @Override | ||
140 | + public Unit unit() { | ||
141 | + return Unit.KB_PER_SEC; | ||
142 | + } | ||
143 | + | ||
144 | + @Override | ||
145 | + public boolean isBurst() { | ||
146 | + return false; | ||
147 | + } | ||
148 | + | ||
149 | + @Override | ||
150 | + public Collection<Band> bands() { | ||
151 | + return this.bandList; | ||
152 | + } | ||
153 | + | ||
154 | + @Override | ||
155 | + public MeterState state() { | ||
156 | + return MeterState.ADDED; | ||
157 | + } | ||
158 | + | ||
159 | + @Override | ||
160 | + public long life() { | ||
161 | + return baseValue + 11; | ||
162 | + } | ||
163 | + | ||
164 | + @Override | ||
165 | + public long referenceCount() { | ||
166 | + return baseValue + 22; | ||
167 | + } | ||
168 | + | ||
169 | + @Override | ||
170 | + public long packetsSeen() { | ||
171 | + return baseValue + 33; | ||
172 | + } | ||
173 | + | ||
174 | + @Override | ||
175 | + public long bytesSeen() { | ||
176 | + return baseValue + 44; | ||
177 | + } | ||
178 | + } | ||
179 | + | ||
180 | + /** | ||
181 | + * Populates some meters used as testing data. | ||
182 | + */ | ||
183 | + private void setupMockMeters() { | ||
184 | + final Set<Meter> meters1 = new HashSet<>(); | ||
185 | + meters1.add(meter1); | ||
186 | + meters1.add(meter2); | ||
187 | + | ||
188 | + final Set<Meter> meters2 = new HashSet<>(); | ||
189 | + meters2.add(meter3); | ||
190 | + meters2.add(meter4); | ||
191 | + | ||
192 | + meters.put(deviceId1, meters1); | ||
193 | + meters.put(deviceId2, meters2); | ||
194 | + | ||
195 | + Set<Meter> allMeters = new HashSet<>(); | ||
196 | + for (DeviceId deviceId : meters.keySet()) { | ||
197 | + allMeters.addAll(meters.get(deviceId)); | ||
198 | + } | ||
199 | + | ||
200 | + expect(mockMeterService.getAllMeters()).andReturn(allMeters).anyTimes(); | ||
201 | + } | ||
202 | + | ||
203 | + /** | ||
204 | + * Sets up the global values for all the tests. | ||
205 | + */ | ||
206 | + @Before | ||
207 | + public void setUpTest() { | ||
208 | + // Mock device service | ||
209 | + expect(mockDeviceService.getDevice(deviceId1)) | ||
210 | + .andReturn(device1); | ||
211 | + expect(mockDeviceService.getDevice(deviceId2)) | ||
212 | + .andReturn(device2); | ||
213 | + expect(mockDeviceService.getDevices()) | ||
214 | + .andReturn(ImmutableSet.of(device1, device2)); | ||
215 | + | ||
216 | + // Mock Core Service | ||
217 | + expect(mockCoreService.getAppId(anyShort())) | ||
218 | + .andReturn(NetTestTools.APP_ID).anyTimes(); | ||
219 | + expect(mockCoreService.registerApplication(MeterCodec.REST_APP_ID)) | ||
220 | + .andReturn(APP_ID).anyTimes(); | ||
221 | + replay(mockCoreService); | ||
222 | + | ||
223 | + // Register the services needed for the test | ||
224 | + final CodecManager codecService = new CodecManager(); | ||
225 | + codecService.activate(); | ||
226 | + ServiceDirectory testDirectory = | ||
227 | + new TestServiceDirectory() | ||
228 | + .add(MeterService.class, mockMeterService) | ||
229 | + .add(DeviceService.class, mockDeviceService) | ||
230 | + .add(CodecService.class, codecService) | ||
231 | + .add(CoreService.class, mockCoreService); | ||
232 | + | ||
233 | + BaseResource.setServiceDirectory(testDirectory); | ||
234 | + } | ||
235 | + | ||
236 | + /** | ||
237 | + * Cleans up and verifies the mocks. | ||
238 | + */ | ||
239 | + @After | ||
240 | + public void tearDownTest() { | ||
241 | + verify(mockMeterService); | ||
242 | + verify(mockCoreService); | ||
243 | + } | ||
244 | + | ||
245 | + /** | ||
246 | + * Hamcrest matcher to check that a meter representation in JSON matches | ||
247 | + * the actual meter. | ||
248 | + */ | ||
249 | + public static class MeterJsonMatcher extends TypeSafeMatcher<JsonObject> { | ||
250 | + private final Meter meter; | ||
251 | + private String reason = ""; | ||
252 | + | ||
253 | + public MeterJsonMatcher(Meter meterValue) { | ||
254 | + this.meter = meterValue; | ||
255 | + } | ||
256 | + | ||
257 | + @Override | ||
258 | + protected boolean matchesSafely(JsonObject jsonMeter) { | ||
259 | + | ||
260 | + // check application id | ||
261 | + final String jsonAppId = jsonMeter.get("appId").asString(); | ||
262 | + final String appId = meter.appId().toString(); | ||
263 | + if (!jsonAppId.equals(appId)) { | ||
264 | + reason = "appId " + meter.appId().toString(); | ||
265 | + return false; | ||
266 | + } | ||
267 | + | ||
268 | + // check device id | ||
269 | + final String jsonDeviceId = jsonMeter.get("deviceId").asString(); | ||
270 | + if (!jsonDeviceId.equals(meter.deviceId().toString())) { | ||
271 | + reason = "deviceId " + meter.deviceId(); | ||
272 | + return false; | ||
273 | + } | ||
274 | + | ||
275 | + // check band array | ||
276 | + if (meter.bands() != null) { | ||
277 | + final JsonArray jsonBands = jsonMeter.get("bands").asArray(); | ||
278 | + if (meter.bands().size() != jsonBands.size()) { | ||
279 | + reason = "bands array size of " + | ||
280 | + Integer.toString(meter.bands().size()); | ||
281 | + return false; | ||
282 | + } | ||
283 | + for (final Band band : meter.bands()) { | ||
284 | + boolean bandFound = false; | ||
285 | + for (int bandIndex = 0; bandIndex < jsonBands.size(); bandIndex++) { | ||
286 | + final String jsonType = jsonBands.get(bandIndex).asObject().get("type").asString(); | ||
287 | + final String bandType = band.type().name(); | ||
288 | + if (jsonType.equals(bandType)) { | ||
289 | + bandFound = true; | ||
290 | + } | ||
291 | + } | ||
292 | + if (!bandFound) { | ||
293 | + reason = "meter band " + band.toString(); | ||
294 | + return false; | ||
295 | + } | ||
296 | + } | ||
297 | + } | ||
298 | + | ||
299 | + return true; | ||
300 | + } | ||
301 | + | ||
302 | + @Override | ||
303 | + public void describeTo(Description description) { | ||
304 | + description.appendText(reason); | ||
305 | + } | ||
306 | + } | ||
307 | + | ||
308 | + private static MeterJsonMatcher matchesMeter(Meter meter) { | ||
309 | + return new MeterJsonMatcher(meter); | ||
310 | + } | ||
311 | + | ||
312 | + /** | ||
313 | + * Hamcrest matcher to check that a meter is represented properly in a JSON | ||
314 | + * array of meters. | ||
315 | + */ | ||
316 | + public static class MeterJsonArrayMatcher extends TypeSafeMatcher<JsonArray> { | ||
317 | + private final Meter meter; | ||
318 | + private String reason = ""; | ||
319 | + | ||
320 | + public MeterJsonArrayMatcher(Meter meterValue) { | ||
321 | + meter = meterValue; | ||
322 | + } | ||
323 | + | ||
324 | + @Override | ||
325 | + protected boolean matchesSafely(JsonArray json) { | ||
326 | + boolean meterFound = false; | ||
327 | + for (int jsonMeterIndex = 0; jsonMeterIndex < json.size(); jsonMeterIndex++) { | ||
328 | + final JsonObject jsonMeter = json.get(jsonMeterIndex).asObject(); | ||
329 | + | ||
330 | + final String meterId = meter.id().toString(); | ||
331 | + final String jsonMeterId = jsonMeter.get("id").asString(); | ||
332 | + if (jsonMeterId.equals(meterId)) { | ||
333 | + meterFound = true; | ||
334 | + | ||
335 | + assertThat(jsonMeter, matchesMeter(meter)); | ||
336 | + } | ||
337 | + } | ||
338 | + if (!meterFound) { | ||
339 | + reason = "Meter with id " + meter.id().toString() + " not found"; | ||
340 | + return false; | ||
341 | + } else { | ||
342 | + return true; | ||
343 | + } | ||
344 | + } | ||
345 | + | ||
346 | + @Override | ||
347 | + public void describeTo(Description description) { | ||
348 | + description.appendText(reason); | ||
349 | + } | ||
350 | + } | ||
351 | + | ||
352 | + /** | ||
353 | + * Factory to allocate a meter array matcher. | ||
354 | + * | ||
355 | + * @param meter meter object we are looking for | ||
356 | + * @return matcher | ||
357 | + */ | ||
358 | + private static MeterJsonArrayMatcher hasMeter(Meter meter) { | ||
359 | + return new MeterJsonArrayMatcher(meter); | ||
360 | + } | ||
361 | + | ||
362 | + @Test | ||
363 | + public void testMeterEmptyArray() { | ||
364 | + expect(mockMeterService.getAllMeters()).andReturn(null).anyTimes(); | ||
365 | + replay(mockMeterService); | ||
366 | + replay(mockDeviceService); | ||
367 | + final WebResource rs = resource(); | ||
368 | + final String response = rs.path("meters").get(String.class); | ||
369 | + assertThat(response, is("{\"meters\":[]}")); | ||
370 | + } | ||
371 | + | ||
372 | + /** | ||
373 | + * Tests the result of the rest api GET when there are active meters. | ||
374 | + */ | ||
375 | + @Test | ||
376 | + public void testMetersPopulatedArray() { | ||
377 | + setupMockMeters(); | ||
378 | + replay(mockMeterService); | ||
379 | + replay(mockDeviceService); | ||
380 | + final WebResource rs = resource(); | ||
381 | + final String response = rs.path("meters").get(String.class); | ||
382 | + final JsonObject result = JsonObject.readFrom(response); | ||
383 | + assertThat(result, notNullValue()); | ||
384 | + | ||
385 | + assertThat(result.names(), hasSize(1)); | ||
386 | + assertThat(result.names().get(0), is("meters")); | ||
387 | + final JsonArray jsonMeters = result.get("meters").asArray(); | ||
388 | + assertThat(jsonMeters, notNullValue()); | ||
389 | + assertThat(jsonMeters, hasMeter(meter1)); | ||
390 | + assertThat(jsonMeters, hasMeter(meter2)); | ||
391 | + assertThat(jsonMeters, hasMeter(meter3)); | ||
392 | + assertThat(jsonMeters, hasMeter(meter4)); | ||
393 | + } | ||
394 | + | ||
395 | + /** | ||
396 | + * Tests the result of a rest api GET for a device. | ||
397 | + */ | ||
398 | + @Test | ||
399 | + public void testMeterSingleDeviceWithId() { | ||
400 | + setupMockMeters(); | ||
401 | + | ||
402 | + expect(mockMeterService.getMeter(anyObject(), anyObject())) | ||
403 | + .andReturn(meter5).anyTimes(); | ||
404 | + replay(mockMeterService); | ||
405 | + replay(mockDeviceService); | ||
406 | + | ||
407 | + final WebResource rs = resource(); | ||
408 | + final String response = rs.path("meters/" + deviceId3.toString() | ||
409 | + + "/" + meter5.id().id()).get(String.class); | ||
410 | + final JsonObject result = JsonObject.readFrom(response); | ||
411 | + assertThat(result, notNullValue()); | ||
412 | + | ||
413 | + assertThat(result.names(), hasSize(1)); | ||
414 | + assertThat(result.names().get(0), is("meters")); | ||
415 | + final JsonArray jsonFlows = result.get("meters").asArray(); | ||
416 | + assertThat(jsonFlows, notNullValue()); | ||
417 | + assertThat(jsonFlows, hasMeter(meter5)); | ||
418 | + } | ||
419 | + | ||
420 | + /** | ||
421 | + * Tests creating a meter with POST. | ||
422 | + */ | ||
423 | + @Test | ||
424 | + public void testPost() { | ||
425 | + mockMeterService.submit(anyObject()); | ||
426 | + expectLastCall().andReturn(meter5).anyTimes(); | ||
427 | + replay(mockMeterService); | ||
428 | + | ||
429 | + WebResource rs = resource(); | ||
430 | + InputStream jsonStream = MetersResourceTest.class | ||
431 | + .getResourceAsStream("post-meter.json"); | ||
432 | + | ||
433 | + ClientResponse response = rs.path("meters/of:0000000000000001") | ||
434 | + .type(MediaType.APPLICATION_JSON_TYPE) | ||
435 | + .post(ClientResponse.class, jsonStream); | ||
436 | + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED)); | ||
437 | + } | ||
438 | + | ||
439 | + /** | ||
440 | + * Tests deleting a meter. | ||
441 | + */ | ||
442 | + @Test | ||
443 | + public void testDelete() { | ||
444 | + setupMockMeters(); | ||
445 | + expect(mockMeterService.getMeter(anyObject(), anyObject())) | ||
446 | + .andReturn(meter5).anyTimes(); | ||
447 | + mockMeterService.withdraw(anyObject(), anyObject()); | ||
448 | + expectLastCall(); | ||
449 | + replay(mockMeterService); | ||
450 | + | ||
451 | + WebResource rs = resource(); | ||
452 | + | ||
453 | + String location = "/meters/3/555"; | ||
454 | + | ||
455 | + ClientResponse deleteResponse = rs.path(location) | ||
456 | + .type(MediaType.APPLICATION_JSON_TYPE) | ||
457 | + .delete(ClientResponse.class); | ||
458 | + assertThat(deleteResponse.getStatus(), | ||
459 | + is(HttpURLConnection.HTTP_NO_CONTENT)); | ||
460 | + } | ||
461 | +} |
-
Please register or login to post a comment