@@ -66,6 +66,9 @@ class VirtualMachineSync {
6666 ))
6767 }
6868 def serverType = context. async. cloud. findComputeServerTypeByCode(" scvmmUnmanaged" ). blockingGet()
69+ def systemNetworks = context. services. cloud. network. list(
70+ new DataQuery (). withFilter(' category' , ' =~' , " scvmm.network.${ cloud.id} .%" )
71+ )?. collectEntries { [(it. externalId): it] } ?: [:]
6972
7073 def existingVms = context. async. computeServer. listIdentityProjections(new DataQuery ()
7174 .withFilter(' zone.id' , cloud. id)
@@ -83,10 +86,10 @@ class VirtualMachineSync {
8386 }
8487 }. onAdd { itemsToAdd ->
8588 if (createNew) {
86- addMissingVirtualMachines(itemsToAdd, availablePlans, fallbackPlan, availablePlanPermissions, hosts, consoleEnabled, serverType)
89+ addMissingVirtualMachines(itemsToAdd, availablePlans, fallbackPlan, availablePlanPermissions, hosts, consoleEnabled, serverType, systemNetworks )
8790 }
8891 }. onUpdate { List<SyncTask.UpdateItem <ComputeServer, Map> > updateItems ->
89- updateMatchedVirtualMachines(updateItems, availablePlans, fallbackPlan, hosts, consoleEnabled, serverType)
92+ updateMatchedVirtualMachines(updateItems, availablePlans, fallbackPlan, hosts, consoleEnabled, serverType, systemNetworks, systemNetworks )
9093 }. onDelete { List<ComputeServerIdentityProjection > removeItems ->
9194 removeMissingVirtualMachines(removeItems)
9295 }. observe(). blockingSubscribe()
@@ -96,7 +99,8 @@ class VirtualMachineSync {
9699 }
97100 }
98101
99- def addMissingVirtualMachines (List addList , Collection<ServicePlan > availablePlans , ServicePlan fallbackPlan , Collection<ResourcePermission > availablePlanPermissions , List hosts , Boolean consoleEnabled , ComputeServerType defaultServerType ) {
102+ def addMissingVirtualMachines (List addList , Collection<ServicePlan > availablePlans , ServicePlan fallbackPlan , Collection<ResourcePermission > availablePlanPermissions , List hosts , Boolean consoleEnabled , ComputeServerType defaultServerType , Map systemNetworks ) {
103+ log. debug(" addMissingVirtualMachines: ${ cloud} ${ addList.size()} " )
100104 try {
101105 for (cloudItem in addList) {
102106 log. debug " Adding new virtual machine: ${ cloudItem.Name} "
@@ -140,6 +144,7 @@ class VirtualMachineSync {
140144 log. error " error adding new virtual machine: ${ add} "
141145 } else {
142146 syncVolumes(savedServer, cloudItem.Disks )
147+ syncInterfaces(savedServer, cloudItem.NetworkAdapters , systemNetworks)
143148 }
144149 }
145150 } catch (ex) {
@@ -148,7 +153,7 @@ class VirtualMachineSync {
148153 }
149154
150155 protected updateMatchedVirtualMachines (List<SyncTask.UpdateItem <ComputeServer, Map> > updateList , availablePlans , fallbackPlan ,
151- List<ComputeServer > hosts , consoleEnabled , ComputeServerType defaultServerType ) {
156+ List<ComputeServer > hosts , consoleEnabled , ComputeServerType defaultServerType , Map systemNetworks , Map< String , Network > existingSystemNetworks ) {
152157 log. debug(" VirtualMachineSync >> updateMatchedVirtualMachines() called" )
153158 try {
154159 def matchedServers = context. services. computeServer. list(new DataQuery (). withFilter(' id' , ' in' , updateList. collect { up -> up. existingItem. id })
@@ -288,6 +293,11 @@ class VirtualMachineSync {
288293 syncVolumes(currentServer, masterItem.Disks )
289294 }
290295 }
296+ if (masterItem.NetworkAdapters ) {
297+ if (syncInterfaces(currentServer, masterItem.NetworkAdapters , systemNetworks)) {
298+ save = true
299+ }
300+ }
291301 log. debug (" updateMatchedVirtualMachines: save: ${ save} " )
292302 if (save) {
293303 saves << currentServer
@@ -609,4 +619,87 @@ class VirtualMachineSync {
609619 return " data-${ index} "
610620 }
611621 }
622+
623+ private boolean syncInterfaces (ComputeServer server , List networkAdapters , Map systemNetworks ) {
624+ def changed = false
625+ try {
626+ def masterItems = networkAdapters?. findAll { it } ?: []
627+ def existingInterfaces = server. interfaces?. findAll { it } ?: []
628+ boolean isPrimaryAssigned = existingInterfaces. any { it. primaryInterface }
629+
630+ def masterById = masterItems. collectEntries { [(it. ID ): it] }
631+ def existingById = existingInterfaces. findAll { it. externalId }. collectEntries { [(it. externalId): it] }
632+
633+ // remove NICs no longer reported by SCVMM
634+ existingInterfaces. findAll { ! masterById. containsKey(it. externalId) }. each { iface ->
635+ context. async. computeServer. computeServerInterface. remove([iface]). blockingGet()
636+ changed = true
637+ }
638+
639+ masterItems. each { masterItem ->
640+ def existing = existingById[masterItem. ID ]
641+ def network = resolveNetworkForAdapter(masterItem, systemNetworks, existing?. network)
642+ def isPrimary = getIsPrimary(existing, masterItem, isPrimaryAssigned)
643+ def dhcp = (masterItem.IPv4AddressType == ' Dynamic' || masterItem.IPv6AddressType == ' Dynamic' )
644+ def allIps = ((masterItem.IPv4Addresses ?: []). findAll { it }) + ((masterItem.IPv6Addresses ?: []). findAll { it })
645+
646+ if (existing) {
647+ def save = false
648+ if (existing. macAddress != masterItem.MacAddress ) { existing. macAddress = masterItem.MacAddress ; save = true }
649+ if (existing. vlanId != masterItem.VLanID ?. toString()) { existing. vlanId = masterItem.VLanID ?. toString(); save = true }
650+ if (existing. network?. id != network?. id) { existing. network = network; save = true }
651+ if (existing. dhcp != dhcp) { existing. dhcp = dhcp; save = true }
652+ if (existing. primaryInterface != isPrimary) { existing. primaryInterface = isPrimary; save = true }
653+ def existingIps = existing. addresses?. collect { it. address } as Set ?: [] as Set
654+ if (existingIps != (allIps as Set )) {
655+ existing. addresses = allIps. collect { ip ->
656+ def type = (masterItem.IPv4Addresses ?: []). contains(ip) ? NetAddress.AddressType . IPV4 : NetAddress.AddressType . IPV6
657+ new NetAddress (type : type, address : ip)
658+ }
659+ save = true
660+ }
661+ if (save) {
662+ context. async. computeServer. computeServerInterface. save([existing]). blockingGet()
663+ changed = true
664+ }
665+ } else {
666+ def iface = new ComputeServerInterface (
667+ externalId : masterItem. ID ,
668+ name : server. sourceImage?. interfaceName ?: ' eth0' ,
669+ macAddress : masterItem.MacAddress ,
670+ vlanId : masterItem.VLanID ?. toString(),
671+ network : network,
672+ primaryInterface : isPrimary,
673+ dhcp : dhcp
674+ )
675+ allIps. each { ip ->
676+ def type = (masterItem.IPv4Addresses ?: []). contains(ip) ? NetAddress.AddressType . IPV4 : NetAddress.AddressType . IPV6
677+ iface. addresses + = new NetAddress (type : type, address : ip)
678+ }
679+ context. async. computeServer. computeServerInterface. create([iface], server). blockingGet()
680+ if (isPrimary) isPrimaryAssigned = true
681+ changed = true
682+ }
683+ }
684+ } catch (e) {
685+ log. error(" syncInterfaces error: ${ e} " , e)
686+ }
687+ return changed
688+ }
689+
690+ private Network resolveNetworkForAdapter (Map masterItem , Map systemNetworks , Network existingNetwork = null ) {
691+ def vlanSuffix = masterItem.VLanID ? ' -' + masterItem.VLanID : ' '
692+ def masterItemNetID = (masterItem.VirtualNetworkId ?: ' ' ) + vlanSuffix
693+ def alternateId = masterItem. ID + vlanSuffix
694+ def network = existingNetwork ?: systemNetworks[masterItemNetID]
695+ if (network?. externalId != masterItemNetID && systemNetworks?. get(alternateId)) {
696+ network = systemNetworks[alternateId]
697+ }
698+ return network
699+ }
700+
701+ private boolean getIsPrimary (ComputeServerInterface iface , Map masterItem , boolean isPrimaryAssigned ) {
702+ return (iface?. primaryInterface == true ) || (masterItem.SlotId == 0 && ! isPrimaryAssigned)
703+ }
704+
612705}
0 commit comments