Proper device state management in server driver

This patch complements Change 19229 to fix the issues
caused by multi-server deployments. Previously, only one
server device could be detected by ONOS due to the proxy field.
Also, when that device went offline, the driver did not notify the
RestController.
With this patch, the server device driver cooperates with the
RestController to provide a consistent server activity state for
multiple servers.

Addressed comments made by ONOS reviewers

Change-Id: Ifc0c556a2a5322fd2ee8b02065a2a507cf6b92fc
Signed-off-by: Georgios Katsikas <katsikas.gp@gmail.com>
This commit is contained in:
Georgios Katsikas 2018-07-28 14:46:07 +02:00 committed by Andrea Campanella
parent c5836479cf
commit 30bede5a8b
4 changed files with 91 additions and 95 deletions

View File

@ -21,8 +21,7 @@ import org.onosproject.net.DeviceId;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.driver.DriverHandler;
import org.onosproject.protocol.rest.RestSBController;
import org.onlab.osgi.ServiceNotFoundException;
import org.onosproject.protocol.rest.RestSBDevice;
import org.slf4j.Logger;
@ -47,7 +46,8 @@ public class BasicServerDriver extends AbstractHandlerBehaviour {
*/
public static final MediaType JSON = MediaType.valueOf(MediaType.APPLICATION_JSON);
protected static final String ROOT_URL = "";
public static final String BASE_URL = ROOT_URL + "/metron";
protected static final String SLASH = "/";
public static final String BASE_URL = ROOT_URL + SLASH + "metron";
/**
* Common parameters to be exchanged with the server's agent.
@ -85,60 +85,35 @@ public class BasicServerDriver extends AbstractHandlerBehaviour {
public BasicServerDriver() {};
/**
* Initialize the REST SB controller (if not already).
* Creates a handler and a controller only once, and
* then re-uses these objects.
* Retrieve an instance of the driver handler.
*
* @throws ServiceNotFoundException when either the handler
* or the controller cannot be retrieved.
* @return DriverHandler instance
*/
private void init() {
protected DriverHandler getHandler() {
synchronized (CONTROLLER_LOCK) {
// Already done
if ((handler != null) && (controller != null)) {
return;
}
handler = handler();
checkNotNull(handler, HANDLER_NULL);
controller = handler.get(RestSBController.class);
checkNotNull(controller, CONTROLLER_NULL);
}
return handler;
}
/**
* Retrieve an instance of the REST SB controller.
* Method init will only be called the first time
* this method (or getHandler) is invoked.
*
* @return RestSBController instance
*/
public RestSBController getController() {
synchronized (CONTROLLER_LOCK) {
if (controller == null) {
init();
controller = getHandler().get(RestSBController.class);
checkNotNull(controller, CONTROLLER_NULL);
}
}
return controller;
}
/**
* Retrieve an instance of the driver handler.
* Method init will only be called the first time
* this method (or getController) is invoked.
*
* @return DriverHandler instance
*/
protected DriverHandler getHandler() {
synchronized (CONTROLLER_LOCK) {
if (handler == null) {
init();
}
}
return handler;
}
/**
* Finds the NIC name that corresponds to a device's port number.
*
@ -147,6 +122,10 @@ public class BasicServerDriver extends AbstractHandlerBehaviour {
* @return device's NIC name
*/
public static String findNicInterfaceWithPort(DeviceId deviceId, long port) {
if (controller == null) {
return null;
}
RestServerSBDevice device = null;
try {
device = (RestServerSBDevice) controller.getDevice(deviceId);
@ -165,8 +144,7 @@ public class BasicServerDriver extends AbstractHandlerBehaviour {
* @param enumType the enum class to get its types
* @return String with all enumeration types
*/
public static <E extends Enum<E>> String enumTypesToString(
Class<E> enumType) {
public static <E extends Enum<E>> String enumTypesToString(Class<E> enumType) {
String allTypes = "";
for (E en : EnumSet.allOf(enumType)) {
allTypes += en.toString() + " ";
@ -215,4 +193,21 @@ public class BasicServerDriver extends AbstractHandlerBehaviour {
return false;
}
/**
* Upon a failure to contact a device, the driver
* raises a disconnect event by resetting the
* activity flag of this device.
*
* @param device a device to disconnect
*/
protected void raiseDeviceDisconnect(RestSBDevice device) {
// Already done!
if (!device.isActive()) {
return;
}
log.info("Setting device {} inactive", device.deviceId());
device.setActive(false);
}
}

View File

@ -62,7 +62,7 @@ public class FlowRuleProgrammableServerImpl extends BasicServerDriver
/**
* Resource endpoints of the server agent (REST server-side).
*/
private static final String RULE_MANAGEMENT_URL = BASE_URL + "/rules";
private static final String RULE_MANAGEMENT_URL = BASE_URL + SLASH + "rules";
/**
* Parameters to be exchanged with the server's agent.
@ -360,19 +360,23 @@ public class FlowRuleProgrammableServerImpl extends BasicServerDriver
* @return boolean removal status
*/
private boolean removeNicFlowRule(DeviceId deviceId, long ruleId) {
// Remove rule with ID from this server
int response = getController().delete(deviceId,
RULE_MANAGEMENT_URL + "/" + Long.toString(ruleId), null, JSON);
int response = -1;
if (!checkStatusCode(response)) {
log.error("Failed to remove flow rule {} from device {}",
ruleId, deviceId);
// Try to remove the rule, although server might be unreachable
try {
response = getController().delete(deviceId,
RULE_MANAGEMENT_URL + SLASH + Long.toString(ruleId), null, JSON);
} catch (Exception ex) {
log.error("Failed to remove flow rule {} from device {}", ruleId, deviceId);
return false;
}
log.info("Successfully removed flow rule {} from device {}",
ruleId, deviceId);
if (!checkStatusCode(response)) {
log.error("Failed to remove flow rule {} from device {}", ruleId, deviceId);
return false;
}
log.info("Successfully removed flow rule {} from device {}", ruleId, deviceId);
return true;
}

View File

@ -53,7 +53,7 @@ public class ServerControllerConfig extends BasicServerDriver
/**
* Resource endpoints of the server agent (REST server-side).
*/
private static final String CONTROLLERS_CONF_URL = BASE_URL + "/controllers";
private static final String CONTROLLERS_CONF_URL = BASE_URL + SLASH + "controllers";
/**
* Parameters to be exchanged with the server's agent.
@ -219,7 +219,7 @@ public class ServerControllerConfig extends BasicServerDriver
log.info("Remove controller with {}:{}:{}",
ctrl.type(), ctrl.ip().toString(), ctrl.port());
String remCtrlUrl = CONTROLLERS_CONF_URL + "/" + ctrl.ip().toString();
String remCtrlUrl = CONTROLLERS_CONF_URL + SLASH + ctrl.ip().toString();
// Remove this controller
int response = getController().delete(deviceId, remCtrlUrl, null, JSON);

View File

@ -99,9 +99,9 @@ public class ServerDevicesDiscovery extends BasicServerDriver
/**
* Resource endpoints of the server agent (REST server-side).
*/
private static final String RESOURCE_DISCOVERY_URL = BASE_URL + "/resources";
private static final String GLOBAL_STATS_URL = BASE_URL + "/stats";
private static final String SERVICE_CHAINS_STATS_URL = BASE_URL + "/chains_stats"; // + /ID
private static final String RESOURCE_DISCOVERY_URL = BASE_URL + SLASH + "resources";
private static final String GLOBAL_STATS_URL = BASE_URL + SLASH + "stats";
private static final String SERVICE_CHAINS_STATS_URL = BASE_URL + SLASH + "chains_stats"; // + /ID
/**
* Parameters to be exchanged with the server's agent.
@ -211,9 +211,6 @@ public class ServerDevicesDiscovery extends BasicServerDriver
* @return a DeviceDescription with the device's features
*/
private DeviceDescription getDeviceDetails(DeviceId deviceId) {
// Create a description for this server device
ServerDeviceDescription desc = null;
// Retrieve the device ID, if null given
if (deviceId == null) {
deviceId = getHandler().data().deviceId();
@ -230,7 +227,7 @@ public class ServerDevicesDiscovery extends BasicServerDriver
response = getController().get(deviceId, RESOURCE_DISCOVERY_URL, JSON);
} catch (ProcessingException pEx) {
log.error("Failed to discover the device details of: {}", deviceId);
return desc;
return null;
}
// Load the JSON into objects
@ -244,12 +241,12 @@ public class ServerDevicesDiscovery extends BasicServerDriver
objNode = (ObjectNode) jsonNode;
} catch (IOException ioEx) {
log.error("Failed to discover the device details of: {}", deviceId);
return desc;
return null;
}
if (jsonMap == null) {
log.error("Failed to discover the device details of: {}", deviceId);
return desc;
return null;
}
// Get all the attributes
@ -357,6 +354,9 @@ public class ServerDevicesDiscovery extends BasicServerDriver
getController().removeDevice(deviceId);
getController().addDevice((RestSBDevice) dev);
// Create a description for this server device
ServerDeviceDescription desc = null;
try {
desc = new DefaultServerDeviceDescription(
new URI(id), Device.Type.SERVER, vendor,
@ -376,44 +376,31 @@ public class ServerDevicesDiscovery extends BasicServerDriver
@Override
public List<PortDescription> discoverPortDetails() {
// List of port descriptions to return
List<PortDescription> portDescriptions = Lists.newArrayList();
// Retrieve the device ID
DeviceId deviceId = getHandler().data().deviceId();
checkNotNull(deviceId, DEVICE_ID_NULL);
// .. and object
RestServerSBDevice device = null;
// In case this method is called before discoverDeviceDetails,
// there is missing information to be gathered.
short i = 0;
while ((device == null) && (i < DISCOVERY_RETRIES)) {
i++;
try {
device = (RestServerSBDevice) getController().getDevice(deviceId);
} catch (ClassCastException ccEx) {
try {
Thread.sleep(1);
} catch (InterruptedException intEx) {
// Just retry
continue;
}
}
// No device
if (device == null) {
// This method will add the device to the RestSBController
this.getDeviceDetails(deviceId);
}
try {
device = (RestServerSBDevice) getController().getDevice(deviceId);
} catch (ClassCastException ccEx) {
log.error("Failed to discover ports for device {}", deviceId);
return Collections.EMPTY_LIST;
}
if ((device == null) || (device.nics() == null)) {
if (device == null) {
log.error("No device with ID {} is available for port discovery", deviceId);
return Collections.EMPTY_LIST;
}
if ((device.nics() == null) || (device.nics().size() == 0)) {
log.error("No ports available on {}", deviceId);
return Collections.EMPTY_LIST;
}
// List of port descriptions to return
List<PortDescription> portDescriptions = Lists.newArrayList();
// Sorted list of NIC ports
Set<NicDevice> nics = new TreeSet(device.nics());
@ -537,7 +524,9 @@ public class ServerDevicesDiscovery extends BasicServerDriver
deviceId);
return monStats;
}
checkNotNull(device, DEVICE_NULL);
if ((device == null) || (!device.isActive())) {
return monStats;
}
// Hit the path that provides the server's global resources
InputStream response = null;
@ -546,6 +535,7 @@ public class ServerDevicesDiscovery extends BasicServerDriver
} catch (ProcessingException pEx) {
log.error("Failed to retrieve global monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
@ -560,12 +550,14 @@ public class ServerDevicesDiscovery extends BasicServerDriver
} catch (IOException ioEx) {
log.error("Failed to retrieve global monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
if (jsonMap == null) {
log.error("Failed to retrieve global monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
@ -629,10 +621,12 @@ public class ServerDevicesDiscovery extends BasicServerDriver
deviceId);
return monStats;
}
checkNotNull(device, DEVICE_NULL);
if (device == null) {
return monStats;
}
// Create a resource-specific URL
String scUrl = SERVICE_CHAINS_STATS_URL + "/" + tcId.toString();
String scUrl = SERVICE_CHAINS_STATS_URL + SLASH + tcId.toString();
// Hit the path that provides the server's specific resources
InputStream response = null;
@ -641,6 +635,7 @@ public class ServerDevicesDiscovery extends BasicServerDriver
} catch (ProcessingException pEx) {
log.error("Failed to retrieve monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
@ -656,12 +651,14 @@ public class ServerDevicesDiscovery extends BasicServerDriver
} catch (IOException ioEx) {
log.error("Failed to retrieve monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
if (jsonMap == null) {
log.error("Failed to retrieve monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
@ -711,9 +708,8 @@ public class ServerDevicesDiscovery extends BasicServerDriver
* @param objNode input JSON node with CPU statistics information
* @return list of (per core) CpuStatistics
*/
private Collection<CpuStatistics> parseCpuStatistics(
DeviceId deviceId, JsonNode objNode) {
if (objNode == null) {
private Collection<CpuStatistics> parseCpuStatistics(DeviceId deviceId, JsonNode objNode) {
if ((deviceId == null) || (objNode == null)) {
return Collections.EMPTY_LIST;
}
@ -755,9 +751,8 @@ public class ServerDevicesDiscovery extends BasicServerDriver
* @param objNode input JSON node with NIC statistics information
* @return list of (per port) PortStatistics
*/
private Collection<PortStatistics> parseNicStatistics(
DeviceId deviceId, JsonNode objNode) {
if (objNode == null) {
private Collection<PortStatistics> parseNicStatistics(DeviceId deviceId, JsonNode objNode) {
if ((deviceId == null) || (objNode == null)) {
return Collections.EMPTY_LIST;
}
@ -767,7 +762,9 @@ public class ServerDevicesDiscovery extends BasicServerDriver
} catch (ClassCastException ccEx) {
return Collections.EMPTY_LIST;
}
checkNotNull(device, DEVICE_NULL);
if (device == null) {
return Collections.EMPTY_LIST;
}
Collection<PortStatistics> nicStats = Lists.newArrayList();