@@ -130,6 +130,17 @@ public class Openstack {
130130 // Store the OS session token so clients can be created from it per all threads using this.
131131 private final ClientProvider clientProvider ;
132132
133+ private static final @ Nonnull Cache <Openstack , List <? extends Network >> networksCache
134+ = Caffeine .newBuilder ().expireAfterWrite (10 , TimeUnit .MINUTES ).build ()
135+ ;
136+
137+ // The time to cache here is questionable as the information can get outdated based on activity out of our reach.
138+ // Caching this for few seconds will smooth spikes provisioning many VMs at the time, although it might cause some
139+ // of provisioning attempts to fail in case the number fo the VMs is larger than number of free IPs in the pool with most free IPs.
140+ private static final @ Nonnull Cache <Openstack , List <? extends NetworkIPAvailability >> networkIpAvailabilityCache
141+ = Caffeine .newBuilder ().expireAfterWrite (5 , TimeUnit .SECONDS ).build ()
142+ ;
143+
133144 private Openstack (@ Nonnull String endPointUrl , boolean ignoreSsl , @ Nonnull OpenstackCredential auth , @ CheckForNull String region ) {
134145
135146 final IOSClientBuilder <? extends OSClient <?>, ?> builder = auth .getBuilder (endPointUrl );
@@ -150,7 +161,7 @@ private Openstack(@Nonnull String endPointUrl, boolean ignoreSsl, @Nonnull Opens
150161 debug ("Openstack client created for \" {0}\" , \" {1}\" ." , auth .toString (), region );
151162 }
152163
153- /*exposed for testing*/
164+ @ VisibleForTesting
154165 public Openstack (@ Nonnull final OSClient <?> client ) {
155166 this .clientProvider = new ClientProvider () {
156167 @ Override public @ Nonnull OSClient <?> get () {
@@ -178,8 +189,11 @@ public Openstack(@Nonnull final OSClient<?> client) {
178189 }
179190
180191 @ VisibleForTesting
181- public @ Nonnull List <? extends Network > _listNetworks () {
182- return clientProvider .get ().networking ().network ().list ();
192+ public @ Nonnull List <? extends Network > _listNetworks () {
193+ return Objects .requireNonNull (networksCache .get (
194+ this ,
195+ (os ) -> os .clientProvider .get ().networking ().network ().list ()
196+ ));
183197 }
184198
185199 /**
@@ -223,7 +237,7 @@ public Openstack(@Nonnull final OSClient<?> client) {
223237
224238 List <String > declaredIds = declaredNetworks .values ().stream ().map (Network ::getId ).collect (Collectors .toList ());
225239
226- List <? extends NetworkIPAvailability > networkIPAvailabilities = clientProvider . get (). networking (). networkIPAvailability (). get ();
240+ List <? extends NetworkIPAvailability > networkIPAvailabilities = getNetworkIPAvailability ();
227241
228242 return networkIPAvailabilities .stream ().filter ( // Those we care for
229243 n -> declaredIds .contains (n .getNetworkId ())
@@ -233,6 +247,13 @@ public Openstack(@Nonnull final OSClient<?> client) {
233247 ));
234248 }
235249
250+ public List <? extends NetworkIPAvailability > getNetworkIPAvailability () {
251+ return Objects .requireNonNull (networkIpAvailabilityCache .get (
252+ this ,
253+ (os ) -> os .clientProvider .get ().networking ().networkIPAvailability ().get ()
254+ ));
255+ }
256+
236257 /**
237258 * Finds all {@link Image}s.
238259 *
@@ -868,11 +889,6 @@ public static abstract class FactoryEP implements ExtensionPoint {
868889 public static @ Nonnull Openstack get (
869890 @ Nonnull final String endPointUrl , final boolean ignoreSsl , @ Nonnull final OpenstackCredential auth , @ CheckForNull final String region
870891 ) throws FormValidation {
871- final String fingerprint = Util .getDigestOf (endPointUrl + '\n'
872- + ignoreSsl + '\n'
873- + auth .toString () + '\n'
874- + (auth instanceof PasswordCredentials ? ((PasswordCredentials ) auth ).getPassword ().getEncryptedValue () + '\n' : "" )
875- + region );
876892 final FactoryEP ep = ExtensionList .lookup (FactoryEP .class ).get (0 );
877893 final Function <String , Openstack > cacheMissFunction = (String unused ) -> {
878894 try {
@@ -882,6 +898,7 @@ public static abstract class FactoryEP implements ExtensionPoint {
882898 }
883899 };
884900
901+ final String fingerprint = getCloudConnectionFingerprint (endPointUrl , ignoreSsl , auth , region );
885902 try {
886903 // cacheMissFunction is guaranteed to return nonnull
887904 return Objects .requireNonNull (ep .cache .get (fingerprint , cacheMissFunction ));
@@ -913,6 +930,23 @@ public static abstract class FactoryEP implements ExtensionPoint {
913930 }
914931 }
915932
933+ /**
934+ * Get a string unique per cloud connection.
935+ *
936+ * This is used as caching identifier.
937+ */
938+ @ Nonnull
939+ private static String getCloudConnectionFingerprint (
940+ @ Nonnull String endPointUrl , boolean ignoreSsl , @ Nonnull OpenstackCredential auth , @ CheckForNull String region
941+ ) {
942+ String pwd = auth instanceof PasswordCredentials
943+ ? ((PasswordCredentials ) auth ).getPassword ().getEncryptedValue ()
944+ : ""
945+ ;
946+ String plain = String .join ("::" , endPointUrl , Boolean .toString (ignoreSsl ), auth .toString (), pwd , region );
947+ return Util .getDigestOf (plain );
948+ }
949+
916950 @ Extension
917951 public static final class Factory extends FactoryEP {
918952 public @ Nonnull Openstack getOpenstack (@ Nonnull String endPointUrl , boolean ignoreSsl , @ Nonnull OpenstackCredential auth , @ CheckForNull String region ) throws FormValidation {
0 commit comments