mirror of
				https://github.com/Telecominfraproject/wlan-cloud-opensync-controller.git
				synced 2025-11-04 04:27:59 +00:00 
			
		
		
		
	WIFI-691: modify OSGW to send to the AP a new set of commands for managing remote ssh agent and sessions
This commit is contained in:
		@@ -41,7 +41,6 @@ import com.vmware.ovsdb.protocol.methods.RowUpdate;
 | 
				
			|||||||
import com.vmware.ovsdb.protocol.methods.TableUpdate;
 | 
					import com.vmware.ovsdb.protocol.methods.TableUpdate;
 | 
				
			||||||
import com.vmware.ovsdb.protocol.methods.TableUpdates;
 | 
					import com.vmware.ovsdb.protocol.methods.TableUpdates;
 | 
				
			||||||
import com.vmware.ovsdb.protocol.operation.notation.Row;
 | 
					import com.vmware.ovsdb.protocol.operation.notation.Row;
 | 
				
			||||||
import com.vmware.ovsdb.protocol.operation.notation.Value;
 | 
					 | 
				
			||||||
import com.vmware.ovsdb.service.OvsdbClient;
 | 
					import com.vmware.ovsdb.service.OvsdbClient;
 | 
				
			||||||
import com.vmware.ovsdb.service.OvsdbPassiveConnectionListener;
 | 
					import com.vmware.ovsdb.service.OvsdbPassiveConnectionListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -346,6 +345,13 @@ public class TipWlanOvsdbClient implements OvsdbClientInterface {
 | 
				
			|||||||
        } catch (OvsdbClientException e) {
 | 
					        } catch (OvsdbClientException e) {
 | 
				
			||||||
            LOG.debug("Could not enable monitor for DHCP_leased_IP table. {}", e.getMessage());
 | 
					            LOG.debug("Could not enable monitor for DHCP_leased_IP table. {}", e.getMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            monitorCommandStateDbTable(ovsdbClient, key);
 | 
				
			||||||
 | 
					        } catch (OvsdbClientException e) {
 | 
				
			||||||
 | 
					            LOG.debug("Could not enable monitor for Command_State table. {}", e.getMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        LOG.debug("Finished (re)setting monitors for AP {}", key);
 | 
					        LOG.debug("Finished (re)setting monitors for AP {}", key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -427,6 +433,85 @@ public class TipWlanOvsdbClient implements OvsdbClientInterface {
 | 
				
			|||||||
        awCf.join();
 | 
					        awCf.join();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void monitorCommandStateDbTable(OvsdbClient ovsdbClient, String key) throws OvsdbClientException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CompletableFuture<TableUpdates> csCf = ovsdbClient.monitor(OvsdbDao.ovsdbName,
 | 
				
			||||||
 | 
					                OvsdbDao.commandStateDbTable + "_" + key,
 | 
				
			||||||
 | 
					                new MonitorRequests(ImmutableMap.of(OvsdbDao.commandStateDbTable, new MonitorRequest())),
 | 
				
			||||||
 | 
					                new MonitorCallback() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    @Override
 | 
				
			||||||
 | 
					                    public void update(TableUpdates tableUpdates) {
 | 
				
			||||||
 | 
					                        LOG.info(OvsdbDao.commandStateDbTable + "_" + key + " monitor callback received {}",
 | 
				
			||||||
 | 
					                                tableUpdates);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        List<Map<String, String>> insert = new ArrayList<>();
 | 
				
			||||||
 | 
					                        List<Map<String, String>> delete = new ArrayList<>();
 | 
				
			||||||
 | 
					                        List<Map<String, String>> update = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        for (TableUpdate tableUpdate : tableUpdates.getTableUpdates().values()) {
 | 
				
			||||||
 | 
					                            for (RowUpdate rowUpdate : tableUpdate.getRowUpdates().values()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                if (rowUpdate.getNew() == null) {
 | 
				
			||||||
 | 
					                                    Map<String, String> rowMap = new HashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    rowUpdate.getOld().getColumns().entrySet().stream().forEach(c -> {
 | 
				
			||||||
 | 
					                                        rowMap.put(c.getKey(), c.getValue().toString());
 | 
				
			||||||
 | 
					                                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    delete.add(rowMap);
 | 
				
			||||||
 | 
					                                    // delete
 | 
				
			||||||
 | 
					                                } else if (rowUpdate.getOld() == null) {
 | 
				
			||||||
 | 
					                                    // insert
 | 
				
			||||||
 | 
					                                    Map<String, String> rowMap = new HashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    rowUpdate.getNew().getColumns().entrySet().stream().forEach(c -> {
 | 
				
			||||||
 | 
					                                        rowMap.put(c.getKey(), c.getValue().toString());
 | 
				
			||||||
 | 
					                                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    insert.add(rowMap);
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    // insert
 | 
				
			||||||
 | 
					                                    Map<String, String> rowMap = new HashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    rowUpdate.getOld().getColumns().putAll(rowUpdate.getNew().getColumns());
 | 
				
			||||||
 | 
					                                    rowUpdate.getOld().getColumns().entrySet().stream().forEach(c -> {
 | 
				
			||||||
 | 
					                                        rowMap.put(c.getKey(), c.getValue().toString());
 | 
				
			||||||
 | 
					                                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    update.add(rowMap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (!insert.isEmpty()) {
 | 
				
			||||||
 | 
					                            extIntegrationInterface.commandStateDbTableUpdate(insert, key, RowUpdateOperation.INSERT);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (!delete.isEmpty()) {
 | 
				
			||||||
 | 
					                            extIntegrationInterface.commandStateDbTableUpdate(delete, key, RowUpdateOperation.DELETE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (!update.isEmpty()) {
 | 
				
			||||||
 | 
					                            extIntegrationInterface.commandStateDbTableUpdate(update, key, RowUpdateOperation.MODIFY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        csCf.join();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void monitorAwlanNodeDbTable(OvsdbClient ovsdbClient, String key) throws OvsdbClientException {
 | 
					    private void monitorAwlanNodeDbTable(OvsdbClient ovsdbClient, String key) throws OvsdbClientException {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,7 @@ import com.telecominfraproject.wlan.opensync.external.integration.models.Opensyn
 | 
				
			|||||||
import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAWLANNode;
 | 
					import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAWLANNode;
 | 
				
			||||||
import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncWifiAssociatedClients;
 | 
					import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncWifiAssociatedClients;
 | 
				
			||||||
import com.telecominfraproject.wlan.opensync.ovsdb.dao.models.BridgeInfo;
 | 
					import com.telecominfraproject.wlan.opensync.ovsdb.dao.models.BridgeInfo;
 | 
				
			||||||
 | 
					import com.telecominfraproject.wlan.opensync.ovsdb.dao.models.CommandConfigInfo;
 | 
				
			||||||
import com.telecominfraproject.wlan.opensync.ovsdb.dao.models.InterfaceInfo;
 | 
					import com.telecominfraproject.wlan.opensync.ovsdb.dao.models.InterfaceInfo;
 | 
				
			||||||
import com.telecominfraproject.wlan.opensync.ovsdb.dao.models.PortInfo;
 | 
					import com.telecominfraproject.wlan.opensync.ovsdb.dao.models.PortInfo;
 | 
				
			||||||
import com.telecominfraproject.wlan.opensync.ovsdb.dao.models.WifiInetConfigInfo;
 | 
					import com.telecominfraproject.wlan.opensync.ovsdb.dao.models.WifiInetConfigInfo;
 | 
				
			||||||
@@ -172,6 +173,10 @@ public class OvsdbDao {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public static final String dhcpLeasedIpDbTable = "DHCP_leased_IP";
 | 
					    public static final String dhcpLeasedIpDbTable = "DHCP_leased_IP";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String commandConfigDbTable = "Command_Config";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String commandStateDbTable = "Command_State";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public ConnectNodeInfo getConnectNodeInfo(OvsdbClient ovsdbClient) {
 | 
					    public ConnectNodeInfo getConnectNodeInfo(OvsdbClient ovsdbClient) {
 | 
				
			||||||
        ConnectNodeInfo ret = new ConnectNodeInfo();
 | 
					        ConnectNodeInfo ret = new ConnectNodeInfo();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -984,6 +989,58 @@ public class OvsdbDao {
 | 
				
			|||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    public Map<String, CommandConfigInfo> getProvisionedCommandConfigs(OvsdbClient ovsdbClient) {
 | 
				
			||||||
 | 
					        Map<String, CommandConfigInfo> ret = new HashMap<>();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        List<Operation> operations = new ArrayList<>();
 | 
				
			||||||
 | 
					        List<Condition> conditions = new ArrayList<>();
 | 
				
			||||||
 | 
					        List<String> columns = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        columns.add("_uuid");
 | 
				
			||||||
 | 
					        columns.add("delay");
 | 
				
			||||||
 | 
					        columns.add("duration");
 | 
				
			||||||
 | 
					        columns.add("command");
 | 
				
			||||||
 | 
					        columns.add("payload");
 | 
				
			||||||
 | 
					        columns.add("timestamp");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            LOG.debug("Retrieving CommandConfig:");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            operations.add(new Select(commandConfigDbTable, conditions, columns));
 | 
				
			||||||
 | 
					            CompletableFuture<OperationResult[]> fResult = ovsdbClient.transact(ovsdbName, operations);
 | 
				
			||||||
 | 
					            OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (OperationResult res : result) {
 | 
				
			||||||
 | 
					                LOG.debug("Op Result {}", res);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (Row row : ((SelectResult) result[0]).getRows()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                CommandConfigInfo commandConfigInfo = new CommandConfigInfo();
 | 
				
			||||||
 | 
					                commandConfigInfo.uuid = row.getUuidColumn("_uuid");
 | 
				
			||||||
 | 
					                commandConfigInfo.delay = row.getIntegerColumn("delay");          
 | 
				
			||||||
 | 
					                commandConfigInfo.duration = row.getIntegerColumn("duration");
 | 
				
			||||||
 | 
					                commandConfigInfo.command = row.getStringColumn("command");
 | 
				
			||||||
 | 
					                commandConfigInfo.payload = row.getMapColumn("payload");
 | 
				
			||||||
 | 
					                commandConfigInfo.timestamp = row.getIntegerColumn("timestamp");
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                ret.put(commandConfigInfo.command, commandConfigInfo);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LOG.debug("Retrieved CommandConfig: {}", ret);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        } catch (ExecutionException | InterruptedException | OvsdbClientException | TimeoutException e) {
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					            LOG.error("Error in getProvisionedCommandConfigs", e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw new RuntimeException(e);
 | 
				
			||||||
 | 
					        } 
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Map<String, WifiRadioConfigInfo> getProvisionedWifiRadioConfigs(OvsdbClient ovsdbClient) {
 | 
					    public Map<String, WifiRadioConfigInfo> getProvisionedWifiRadioConfigs(OvsdbClient ovsdbClient) {
 | 
				
			||||||
        Map<String, WifiRadioConfigInfo> ret = new HashMap<>();
 | 
					        Map<String, WifiRadioConfigInfo> ret = new HashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2142,6 +2199,46 @@ public class OvsdbDao {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void configureCommands(OvsdbClient ovsdbClient, String command, Map<String, String> payload, Long delay,
 | 
				
			||||||
 | 
					            Long duration) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        List<Operation> operations = new ArrayList<>();
 | 
				
			||||||
 | 
					        Map<String, Value> commandConfigColumns = new HashMap<>();
 | 
				
			||||||
 | 
					        List<Condition> conditions = new ArrayList<>();
 | 
				
			||||||
 | 
					        conditions.add(new Condition("command", Function.EQUALS, new Atom<>(command)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        commandConfigColumns.put("command", new Atom<>(command));
 | 
				
			||||||
 | 
					        commandConfigColumns.put("payload", com.vmware.ovsdb.protocol.operation.notation.Map.of(payload));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        commandConfigColumns.put("delay", new Atom<>(delay));
 | 
				
			||||||
 | 
					        commandConfigColumns.put("duration", new Atom<>(delay));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Row row = new Row(commandConfigColumns);
 | 
				
			||||||
 | 
					        if (getProvisionedCommandConfigs(ovsdbClient).containsKey(command)) {
 | 
				
			||||||
 | 
					            operations.add(new Update(commandConfigDbTable, conditions, row));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            operations.add(new Insert(commandConfigDbTable, row));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            CompletableFuture<OperationResult[]> fResult = ovsdbClient.transact(ovsdbName, operations);
 | 
				
			||||||
 | 
					            OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LOG.debug("Configured command {} for duration {} payload {}", command, duration, payload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (OperationResult res : result) {
 | 
				
			||||||
 | 
					                LOG.debug("Op Result {}", res);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (OvsdbClientException | InterruptedException | ExecutionException | TimeoutException e) {
 | 
				
			||||||
 | 
					            LOG.error("configureCommands interrupted.", e);
 | 
				
			||||||
 | 
					            throw new RuntimeException(e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void configureWifiRadios(OvsdbClient ovsdbClient, String freqBand, int channel,
 | 
					    private void configureWifiRadios(OvsdbClient ovsdbClient, String freqBand, int channel,
 | 
				
			||||||
            Map<String, String> hwConfig, String country, int beaconInterval, boolean enabled, String hwMode,
 | 
					            Map<String, String> hwConfig, String country, int beaconInterval, boolean enabled, String hwMode,
 | 
				
			||||||
@@ -2602,7 +2699,7 @@ public class OvsdbDao {
 | 
				
			|||||||
                } else if (ssidSecurityMode.equals("wpaEAP") || ssidSecurityMode.equals("wpa2EAP")
 | 
					                } else if (ssidSecurityMode.equals("wpaEAP") || ssidSecurityMode.equals("wpa2EAP")
 | 
				
			||||||
                        || ssidSecurityMode.equals("wpa2OnlyEAP")) {
 | 
					                        || ssidSecurityMode.equals("wpa2OnlyEAP")) {
 | 
				
			||||||
                    opensyncSecurityMode = "WPA-EAP";
 | 
					                    opensyncSecurityMode = "WPA-EAP";
 | 
				
			||||||
                }  else if (ssidSecurityMode.equals("wpaRadius") || ssidSecurityMode.equals("wpa2OnlyRadius")
 | 
					                } else if (ssidSecurityMode.equals("wpaRadius") || ssidSecurityMode.equals("wpa2OnlyRadius")
 | 
				
			||||||
                        || ssidSecurityMode.equals("wpa2Radius")) {
 | 
					                        || ssidSecurityMode.equals("wpa2Radius")) {
 | 
				
			||||||
                    opensyncSecurityMode = "WPA-EAP";
 | 
					                    opensyncSecurityMode = "WPA-EAP";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -2906,7 +3003,7 @@ public class OvsdbDao {
 | 
				
			|||||||
                // broadcast
 | 
					                // broadcast
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (ipAssignScheme.equals("dhcp")) {
 | 
					            if (ipAssignScheme.equals("dhcp")) {
 | 
				
			||||||
                  insertColumns.put("dhcp_sniff", new Atom<>(true));
 | 
					                insertColumns.put("dhcp_sniff", new Atom<>(true));
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                insertColumns.put("dhcp_sniff", new Atom<>(false));
 | 
					                insertColumns.put("dhcp_sniff", new Atom<>(false));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					package com.telecominfraproject.wlan.opensync.ovsdb.dao.models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.vmware.ovsdb.protocol.operation.notation.Uuid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CommandConfigInfo implements Cloneable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long delay;
 | 
				
			||||||
 | 
					    public long duration;
 | 
				
			||||||
 | 
					    public String command;
 | 
				
			||||||
 | 
					    public Map<String, String> payload;
 | 
				
			||||||
 | 
					    public long timestamp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Uuid uuid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public CommandConfigInfo clone() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            CommandConfigInfo ret = (CommandConfigInfo) super.clone();
 | 
				
			||||||
 | 
					            if (payload != null) {
 | 
				
			||||||
 | 
					                ret.payload = new HashMap<>(this.payload);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return ret;
 | 
				
			||||||
 | 
					        } catch (CloneNotSupportedException e) {
 | 
				
			||||||
 | 
					            throw new IllegalStateException("Cannot clone ", e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String toString() {
 | 
				
			||||||
 | 
					        return String.format("CommandConfigInfo [delay=%s, duration=%s, command=%s, payload=%s, timestamp=%s, uuid=%s]",
 | 
				
			||||||
 | 
					                delay, duration, command, payload, timestamp, uuid);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user