aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java
blob: ccf7e08acffd61176e537c976b0d4641651b0c67 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
 * Copyright 2015 Open Networking Laboratory
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onosproject.provider.nil;

import com.google.common.collect.Lists;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.link.LinkDescription;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.link.LinkService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.delay;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.Link.Type.DIRECT;
import static org.onosproject.net.MastershipRole.MASTER;
import static org.onosproject.provider.nil.TopologySimulator.description;

/**
 * Drives topology mutations at a specified rate of events per second.
 */
class TopologyMutationDriver implements Runnable {

    private final Logger log = LoggerFactory.getLogger(getClass());

    private static final int WAIT_DELAY = 2_000;
    private static final int MAX_DOWN_LINKS = 5;

    private final Random random = new Random();

    private volatile boolean stopped = true;

    private double mutationRate;
    private int millis, nanos;

    private LinkService linkService;
    private DeviceService deviceService;
    private LinkProviderService linkProviderService;

    private List<LinkDescription> activeLinks;
    private List<LinkDescription> inactiveLinks;

    private final ExecutorService executor =
            newSingleThreadScheduledExecutor(groupedThreads("onos/null", "topo-mutator"));

    /**
     * Starts the mutation process.
     *
     * @param mutationRate        link events per second
     * @param linkService         link service
     * @param deviceService       device service
     * @param linkProviderService link provider service
     */
    void start(double mutationRate,
               LinkService linkService, DeviceService deviceService,
               LinkProviderService linkProviderService) {
        stopped = false;
        this.linkService = linkService;
        this.deviceService = deviceService;
        this.linkProviderService = linkProviderService;
        activeLinks = reduceLinks();
        inactiveLinks = Lists.newArrayList();
        adjustRate(mutationRate);
        executor.submit(this);
    }

    /**
     * Adjusts the topology mutation rate.
     *
     * @param mutationRate new topology mutation rate
     */
    void adjustRate(double mutationRate) {
        this.mutationRate = mutationRate;
        if (mutationRate > 0) {
            this.millis = (int) (1_000 / mutationRate / 2);
            this.nanos = (int) (1_000_000 / mutationRate / 2) % 1_000_000;
        } else {
            this.millis = 0;
            this.nanos = 0;
        }
        log.info("Settings: millis={}, nanos={}", millis, nanos);
    }

    /**
     * Stops the mutation process.
     */
    void stop() {
        stopped = true;
    }

    /**
     * Severs the link between the specified end-points in both directions.
     *
     * @param one link endpoint
     * @param two link endpoint
     */
    void severLink(ConnectPoint one, ConnectPoint two) {
        LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
        linkProviderService.linkVanished(link);
        linkProviderService.linkVanished(reverse(link));

    }

    /**
     * Repairs the link between the specified end-points in both directions.
     *
     * @param one link endpoint
     * @param two link endpoint
     */
    void repairLink(ConnectPoint one, ConnectPoint two) {
        LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
        linkProviderService.linkDetected(link);
        linkProviderService.linkDetected(reverse(link));
    }

    @Override
    public void run() {
        delay(WAIT_DELAY);

        while (!stopped) {
            if (mutationRate > 0 && inactiveLinks.isEmpty()) {
                primeInactiveLinks();
            } else if (mutationRate <= 0 && !inactiveLinks.isEmpty()) {
                repairInactiveLinks();
            } else if (inactiveLinks.isEmpty()) {
                delay(WAIT_DELAY);

            } else {
                activeLinks.add(repairLink());
                pause();
                inactiveLinks.add(severLink());
                pause();
            }
        }
    }

    // Primes the inactive links with a few random links.
    private void primeInactiveLinks() {
        for (int i = 0, n = Math.min(MAX_DOWN_LINKS, activeLinks.size()); i < n; i++) {
            inactiveLinks.add(severLink());
        }
    }

    // Repairs all inactive links.
    private void repairInactiveLinks() {
        while (!inactiveLinks.isEmpty()) {
            repairLink();
        }
    }

    // Picks a random active link and severs it.
    private LinkDescription severLink() {
        LinkDescription link = getRandomLink(activeLinks);
        linkProviderService.linkVanished(link);
        linkProviderService.linkVanished(reverse(link));
        return link;
    }

    // Picks a random inactive link and repairs it.
    private LinkDescription repairLink() {
        LinkDescription link = getRandomLink(inactiveLinks);
        linkProviderService.linkDetected(link);
        linkProviderService.linkDetected(reverse(link));
        return link;
    }

    // Produces a reverse of the specified link.
    private LinkDescription reverse(LinkDescription link) {
        return new DefaultLinkDescription(link.dst(), link.src(), link.type());
    }

    // Returns a random link from the specified list of links.
    private LinkDescription getRandomLink(List<LinkDescription> links) {
        return links.remove(random.nextInt(links.size()));
    }

    // Reduces the given list of links to just a single link in each original pair.
    private List<LinkDescription> reduceLinks() {
        List<LinkDescription> links = Lists.newArrayList();
        linkService.getLinks().forEach(link -> links.add(description(link)));
        return links.stream()
                .filter(this::isOurLink)
                .filter(this::isRightDirection)
                .collect(Collectors.toList());
    }

    // Returns true if the specified link is ours.
    private boolean isOurLink(LinkDescription linkDescription) {
        return deviceService.getRole(linkDescription.src().deviceId()) == MASTER;
    }

    // Returns true if the link source is greater than the link destination.
    private boolean isRightDirection(LinkDescription link) {
        return link.src().deviceId().toString().compareTo(link.dst().deviceId().toString()) > 0;
    }

    // Pauses the current thread for the pre-computed time of millis & nanos.
    private void pause() {
        delay(millis, nanos);
    }

}