Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions conf/springConfigXml/sugonSdnController.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,7 @@
</zstack:plugin>
</bean>

<bean id="TfZstackPortSync" class="org.zstack.sugonSdnController.network.TfZstackPortSync">
<zstack:plugin>
<zstack:extension interface="org.zstack.header.managementnode.ManagementNodeReadyExtensionPoint" />
</zstack:plugin>
</bean>
<bean id="TfZstackPortSync" class="org.zstack.sugonSdnController.network.TfZstackPortSync" />

<bean id="SugonApiInterceptor" class="org.zstack.sugonSdnController.header.SugonApiInterceptor">
<zstack:plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.util.SubnetUtils;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.zstack.core.db.Q;
import org.zstack.header.core.Completion;
Expand All @@ -19,6 +20,7 @@
import org.zstack.sugonSdnController.controller.api.*;
import org.zstack.sugonSdnController.controller.api.types.*;
import org.zstack.sugonSdnController.header.APICreateL2TfNetworkMsg;
import org.zstack.sugonSdnController.network.TfZstackPortSync;
import org.zstack.utils.StringDSL;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
Expand All @@ -33,6 +35,9 @@
public class SugonSdnController implements TfSdnController, SdnController {
private static final CLogger logger = Utils.getLogger(SugonSdnController.class);

@Autowired
TfZstackPortSync tfZstackPortSync;

private SdnControllerVO sdnControllerVO;

private TfHttpClient client;
Expand Down Expand Up @@ -94,6 +99,7 @@ public void initSdnController(APIAddSdnControllerMsg msg, Completion completion)

@Override
public void postInitSdnController(APIAddSdnControllerMsg msg, Completion completion){
tfZstackPortSync.triggerSyncIfDue(sdnControllerVO.getUuid());
completion.success();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ public TfPortClient(){
if (sdn == null){
throw new RuntimeException("Can not find a tf sdn controller.");
}
init(sdn);
}

public TfPortClient(String sdnControllerUuid) {
SdnControllerVO sdn = Q.New(SdnControllerVO.class).eq(
SdnControllerVO_.uuid, sdnControllerUuid).find();
if (sdn == null) {
throw new RuntimeException(String.format("Can not find a tf sdn controller[uuid:%s].", sdnControllerUuid));
}
if (!Objects.equals(sdn.getVendorType(), SdnControllerConstant.TF_CONTROLLER)) {
throw new RuntimeException(String.format("Sdn controller[uuid:%s] is not a tf sdn controller.", sdnControllerUuid));
}
init(sdn);
}

private void init(SdnControllerVO sdn) {
client = new TfHttpClient(sdn.getIp());
tenantId = StringDSL.transToTfUuid(sdn.getAccountUuid());
}
Expand Down Expand Up @@ -511,4 +527,3 @@ public Status updateTfPort(String tfPortUUid, String accountId, String deviceId,
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public List<VirtualMachineInterface> getTfPortsDetail() {
return tfPortClient.getVirtualMachineInterfaceDetail();
}

public List<VirtualMachineInterface> getTfPortsDetail(String sdnControllerUuid) {
TfPortClient tfPortClient = new TfPortClient(sdnControllerUuid);
return tfPortClient.getVirtualMachineInterfaceDetail();
}

public TfPortResponse createTfPort(String tfPortUUid, String l2NetworkUuid, String l3NetworkUuid, String mac, String ip) {
//invoke tf rest interface to retrieve real ip and mac and portId
TfPortClient tfPortClient = new TfPortClient();
Expand Down Expand Up @@ -114,6 +119,12 @@ public TfPortResponse deleteTfPort(String portUUid) {
return tfPortClient.deletePort(tfPortUUid);
}

public TfPortResponse deleteTfPort(String sdnControllerUuid, String portUUid) {
String tfPortUUid = StringDSL.transToTfUuid(portUUid);
TfPortClient tfPortClient = new TfPortClient(sdnControllerUuid);
return tfPortClient.deletePort(tfPortUUid);
}

public Status updateTfPort(String portUUid, String bmUuid, KeyValuePairs bindInfo) {
String accountId = StringDSL.transToTfUuid(acntMgr.getOwnerAccountUuidOfResource(bmUuid));
String tfBmUUid = StringDSL.transToTfUuid(bmUuid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.db.Q;
import org.zstack.core.thread.PeriodicTask;
import org.zstack.core.thread.Task;
import org.zstack.core.thread.ThreadFacade;
import org.zstack.header.managementnode.ManagementNodeReadyExtensionPoint;
import org.zstack.header.network.l2.L2NetworkVO;
import org.zstack.header.network.l2.L2NetworkVO_;
import org.zstack.header.vm.VmNicVO;
Expand All @@ -19,39 +18,57 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Future;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class TfZstackPortSync implements ManagementNodeReadyExtensionPoint {
public class TfZstackPortSync {
private static final long SYNC_INTERVAL_MILLIS = TimeUnit.DAYS.toMillis(1);
private static final int MAX_DELETE_COUNT = 10;

@Autowired
protected ThreadFacade thdf;
private Future<Void> trackerThread = null;
@Autowired
private TfPortService tfPortService;
private final static CLogger logger = Utils.getLogger(TfZstackPortSync.class);
private final List<String> excludeTypes = new ArrayList<String>(Arrays.asList("neutron:LOADBALANCER", "VIP", "BMS"));
private final Map<String, Long> lastSyncTime = new ConcurrentHashMap<>();
private final Set<String> runningSyncs = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());

@Override
public void managementNodeReady() {
if (trackerThread != null) {
trackerThread.cancel(true);
public void triggerSyncIfDue(String sdnControllerUuid) {
if (sdnControllerUuid == null) {
logger.warn("Port_Sync_Task: skip sync because sdn controller uuid is null.");
return;
}
trackerThread = thdf.submitPeriodicTask(new SyncPort());
}

private class SyncPort implements PeriodicTask {
long now = System.currentTimeMillis();
Long lastSync = lastSyncTime.get(sdnControllerUuid);
if (lastSync != null && now - lastSync < SYNC_INTERVAL_MILLIS) {
return;
}
if (!runningSyncs.add(sdnControllerUuid)) {
return;
}

@Override
public TimeUnit getTimeUnit() {
return TimeUnit.DAYS;
lastSyncTime.put(sdnControllerUuid, now);
try {
thdf.submit(new SyncPort(sdnControllerUuid));
} catch (RuntimeException e) {
lastSyncTime.remove(sdnControllerUuid);
runningSyncs.remove(sdnControllerUuid);
throw e;
}
}

@Override
public long getInterval() {
return 1;
private class SyncPort implements Task<Void> {
private final String sdnControllerUuid;

private SyncPort(String sdnControllerUuid) {
this.sdnControllerUuid = sdnControllerUuid;
}

@Override
Expand All @@ -64,7 +81,10 @@ private HashSet<String> getPortToDelete() {
List<String> zstackL2NetworksUuid = Q.New(L2NetworkVO.class).select(L2NetworkVO_.uuid).listValues();
List<String> tfPortsUuid = new ArrayList<>();
try{
List<VirtualMachineInterface> tfPorts = tfPortService.getTfPortsDetail();
List<VirtualMachineInterface> tfPorts = tfPortService.getTfPortsDetail(sdnControllerUuid);
if (tfPorts == null) {
return new HashSet<>();
}
for (VirtualMachineInterface vmi : tfPorts) {
// skip port if it's network not in zstack
List<ObjectReference<ApiPropertyBase>> tfNetworks = vmi.getVirtualNetwork();
Expand All @@ -83,7 +103,7 @@ private HashSet<String> getPortToDelete() {
}
} catch (Exception e) {
logger.error(String.format("Port_Sync_Task: Fetch tf VirtualMachineInterface failed: %s.", e));
return null;
return new HashSet<>();
}
HashSet<String> result = new HashSet<>(tfPortsUuid);
result.removeAll(zstackPortsUuid);
Expand All @@ -92,13 +112,13 @@ private HashSet<String> getPortToDelete() {
}

@Override
public void run() {
public Void call() {
logger.info("Port_Sync_Task: begin.");
try {
HashSet<String> portsToDelete = getPortToDelete();
int maxDeleteCount = 10;
int maxDeleteCount = MAX_DELETE_COUNT;
for (String portUuid: portsToDelete) {
TfPortResponse response = tfPortService.deleteTfPort(portUuid);
TfPortResponse response = tfPortService.deleteTfPort(sdnControllerUuid, portUuid);
if (response.getCode() == 200) {
logger.info(String.format("Port_Sync_Task: VirtualMachineInterface: %s delete success.",
portUuid));
Expand All @@ -113,7 +133,10 @@ public void run() {
}
} catch (Exception e) {
logger.error(String.format("Port_Sync_Task failed: %s.", e));
} finally {
runningSyncs.remove(sdnControllerUuid);
}
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.zstack.test.integration.network.sdnController

import org.springframework.http.HttpEntity
import org.zstack.core.db.DatabaseFacade
import org.zstack.sdk.*
import org.zstack.sugonSdnController.controller.SugonSdnControllerConstant
Expand All @@ -8,6 +9,7 @@ import org.zstack.sugonSdnController.controller.api.types.Project
import org.zstack.sugonSdnController.controller.api.types.VirtualMachineInterface
import org.zstack.sugonSdnController.controller.api.ApiSerializer
import org.zstack.sugonSdnController.controller.api.TfCommands
import org.zstack.sugonSdnController.network.TfZstackPortSync
import org.zstack.sdnController.header.SdnControllerVO
import org.zstack.header.network.l3.L3NetworkVO;
import org.zstack.testlib.EnvSpec
Expand All @@ -16,6 +18,9 @@ import javax.persistence.TypedQuery;
import org.springframework.http.ResponseEntity
import org.springframework.http.HttpStatus

import javax.servlet.http.HttpServletRequest
import java.util.concurrent.atomic.AtomicInteger


class SugonSdnControllerCase extends SubCase {
EnvSpec env
Expand All @@ -37,6 +42,7 @@ class SugonSdnControllerCase extends SubCase {
void test() {
env.create {
dbf = bean(DatabaseFacade.class)
testTfPortSyncTriggeredForController()
testTfApi()
}
}
Expand All @@ -46,6 +52,44 @@ class SugonSdnControllerCase extends SubCase {
env.delete()
}

void testTfPortSyncTriggeredForController() {
String sql = "select sdn" +
" from SdnControllerVO sdn" +
" where sdn.vendorType = :vendorType";
TypedQuery<SdnControllerVO> q = dbf.getEntityManager().createQuery(sql, SdnControllerVO.class);
q.setParameter("vendorType", SugonSdnControllerConstant.TF_CONTROLLER);
SdnControllerVO sdn = q.getResultList().get(0);
AtomicInteger syncCount = new AtomicInteger(0)

env.simulator(TfCommands.TF_CREATE_VMI) { HttpServletRequest req, HttpEntity<String> e, EnvSpec spec ->
if (req.method == "GET") {
syncCount.incrementAndGet()
return '{"virtual-machine-interfaces":[]}'
}

VirtualMachineInterface rsp = new VirtualMachineInterface();
rsp.name = TfCommands.TEST_VMI_UUID
rsp.uuid = TfCommands.TEST_VMI_UUID
Project project = new Project();
project.name = TfCommands.TEST_PROJECT_UUID
project.uuid = TfCommands.TEST_PROJECT_UUID
project.displayName = "admin";
rsp.setParent(project)
String json = ApiSerializer.serializeObject("virtual-machine-interface", rsp);
ResponseEntity<String> response = new ResponseEntity<String>(json, HttpStatus.OK);
return response.getBody()
}

bean(TfZstackPortSync.class).triggerSyncIfDue(sdn.uuid)
retryInSecs {
assert syncCount.get() == 1
}

bean(TfZstackPortSync.class).triggerSyncIfDue(sdn.uuid)
sleep(500)
assert syncCount.get() == 1
}

void testTfApi() {
def zone = env.inventoryByName("zone") as ZoneInventory
def cluster1 = env.inventoryByName("cluster1") as ClusterInventory
Expand Down