Jian Li
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
...@@ -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 +}
1 +{
2 + "id": 1,
3 + "deviceId": "of:0000000000000001",
4 + "unit": "KB_PER_SEC",
5 + "burst": true,
6 + "bands": [
7 + {
8 + "type": "REMARK",
9 + "rate": 10,
10 + "prec": 20,
11 + "burstSize": 30
12 + }
13 + ]
14 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -28,21 +28,22 @@ public class CoreWebApplication extends AbstractWebApplication { ...@@ -28,21 +28,22 @@ public class CoreWebApplication extends AbstractWebApplication {
28 @Override 28 @Override
29 public Set<Class<?>> getClasses() { 29 public Set<Class<?>> getClasses() {
30 return getClasses(ApiDocResource.class, 30 return getClasses(ApiDocResource.class,
31 - ApplicationsWebResource.class, 31 + ApplicationsWebResource.class,
32 - ComponentConfigWebResource.class, 32 + ComponentConfigWebResource.class,
33 - NetworkConfigWebResource.class, 33 + NetworkConfigWebResource.class,
34 - ClusterWebResource.class, 34 + ClusterWebResource.class,
35 - DevicesWebResource.class, 35 + DevicesWebResource.class,
36 - LinksWebResource.class, 36 + LinksWebResource.class,
37 - HostsWebResource.class, 37 + HostsWebResource.class,
38 - IntentsWebResource.class, 38 + IntentsWebResource.class,
39 - FlowsWebResource.class, 39 + FlowsWebResource.class,
40 - GroupsWebResource.class, 40 + GroupsWebResource.class,
41 - TopologyWebResource.class, 41 + MetersWebResource.class,
42 - ConfigWebResource.class, 42 + TopologyWebResource.class,
43 - PathsWebResource.class, 43 + ConfigWebResource.class,
44 - StatisticsWebResource.class, 44 + PathsWebResource.class,
45 - MetricsWebResource.class 45 + StatisticsWebResource.class,
46 + MetricsWebResource.class
46 ); 47 );
47 } 48 }
48 -} 49 +}
...\ 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 +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 + "id": 1,
3 + "deviceId": "of:0000000000000001",
4 + "unit": "KB_PER_SEC",
5 + "bands": [
6 + {
7 + "type": "REMARK",
8 + "rate": 10,
9 + "prec": 20,
10 + "burstSize": 30
11 + }
12 + ]
13 +}
...\ No newline at end of file ...\ No newline at end of file