Skip to content

Commit e4323e3

Browse files
709 allow selection of pre existing floating ip (#711)
* fixed gres and included installation of NVIDIA drivers * Fixed gres, zabbix repository and logging Untested. Includes installation for nvidia * removed pausing line * support new rest schema * updated gres and volume documentation * allow selection of floating ip by id now a specific floating ip can be assigned to master or vpn by setting the floatingIpId key in the configuration. #709 * prevent preexisting fip deletion now floating ips are no longer released on clouds where they are defined as pre-existing #709 * check floating ip existence added _check_floating_ip to verify the existence of the floating ip #705 * update provider tests updated provider tests to cover new api openstack api calls that had not been used in the past #709 * fixed tests some tests failed due to unexpected new parameters from the new floating_ip check on terminate #709 * fix remaining floating ip apparently when you create a floating ip attached to a server and delete that server quickly, the floating ip remains. #709 * fix terminate for REST REST's terminate did not pass the floating ip list. Now it is passed as well. #709 * pleases linter #709 * adds documentation for floatingIpId #709 --------- Co-authored-by: Jan Krüger <jkrue@users.noreply.github.com>
1 parent f698c1b commit e4323e3

File tree

16 files changed

+213
-54
lines changed

16 files changed

+213
-54
lines changed

bibigrid.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
## If you use a gateway or start a cluster from the cloud, your master does not need a public ip.
2929
# useMasterWithPublicIp: False # defaults True if False no public-ip (floating-ip) will be allocated
30+
# floatingIpId: # if set, uses existing floating ip which will not be deleted on termination.
3031
# gateway: # if you want to use a gateway for create.
3132
# ip: # IP of gateway to use
3233
# portFunction: 30000 + oct4 # variables are called: oct1.oct2.oct3.oct4

bibigrid/core/actions/create.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from bibigrid.core.utility import ansible_configurator
1818
from bibigrid.core.utility import id_generation
1919
from bibigrid.core.utility import image_selection
20-
from bibigrid.core.utility.handler import ssh_handler
20+
from bibigrid.core.utility.handler import ssh_handler, configuration_handler
2121
from bibigrid.core.utility.paths import ansible_resources_path as a_rp
2222
from bibigrid.core.utility.paths.basic_path import CLUSTER_INFO_FOLDER, KEY_FOLDER
2323
from bibigrid.core.utility.statics.create_statics import AC_NAME, KEY_NAME, DEFAULT_SECURITY_GROUP_NAME, \
@@ -223,8 +223,20 @@ def start_vpn_or_master(self, configuration, provider): # pylint: disable=too-m
223223

224224
# pylint: disable=comparison-with-callable
225225
if identifier == vpngtw_identifier or (identifier == master_identifier and self.use_master_with_public_ip):
226-
configuration["floating_ip"] = \
227-
provider.attach_available_floating_ip(network=external_network, server=server)["floating_ip_address"]
226+
if not configuration.get("floatingIpId"):
227+
configuration["floating_ip"] = provider.create_floating_ip(network=external_network, server=server)[
228+
"floating_ip_address"]
229+
else:
230+
floating_ip = provider.get_floating_ip(configuration["floatingIpId"])
231+
if not floating_ip:
232+
raise ValueError(
233+
f"Floating ip {configuration['floatingIpId']} does not exist on "
234+
f"{provider.cloud_specification['identifier']}!")
235+
configuration["floating_ip"] = floating_ip["floating_ip_address"]
236+
_ = provider.add_ip_list(server=server, floating_ip=[configuration["floating_ip"]])
237+
self.log.info(
238+
f"Server {name} uses floating ip {configuration['floating_ip']} ({'pre-existing'
239+
if configuration.get('floatingIpId') else 'created'})")
228240
if identifier == master_identifier:
229241
write_cluster_state({"cluster_id": self.cluster_id, "ssh_user": self.ssh_user,
230242
"floating_ip": configuration["floating_ip"],
@@ -546,8 +558,9 @@ def create(self): # pylint: disable=too-many-branches,too-many-statements
546558
delete_local_keypairs(tmp_keyname=self.key_name, log=self.log)
547559
if self.debug:
548560
self.log.info("DEBUG MODE: Entering termination...")
549-
terminate(cluster_id=self.cluster_id, providers=self.providers, debug=self.debug,
550-
log=self.log)
561+
terminate(cluster_id=self.cluster_id, providers=self.providers,
562+
floating_ip_ids=configuration_handler.get_list_by_key(self.configurations, "floatingIpId"),
563+
log=self.log, debug=self.debug)
551564
except exceptions.ConnectionException:
552565
self.log.error(traceback.format_exc())
553566
self.log.error("Connection couldn't be established. Check Provider connection.")
@@ -577,7 +590,9 @@ def create(self): # pylint: disable=too-many-branches,too-many-statements
577590
self.log.error(f"Unexpected error: '{str(exc)}' ({type(exc)}) Contact a developer!)")
578591
else:
579592
return 0 # will be called if no exception occurred
580-
terminate(cluster_id=self.cluster_id, providers=self.providers, log=self.log, debug=self.debug)
593+
terminate(cluster_id=self.cluster_id, providers=self.providers,
594+
floating_ip_ids=configuration_handler.get_list_by_key(self.configurations, "floatingIpId"),
595+
log=self.log, debug=self.debug)
581596
write_cluster_state({"cluster_id": self.cluster_id, "ssh_user": self.ssh_user,
582597
"floating_ip": self.configurations[0].get("floating_ip"),
583598
"state": "failed",

bibigrid/core/actions/terminate.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ def write_cluster_state(state):
2929
yaml.safe_dump(data=state, stream=cluster_info_file)
3030

3131

32-
def terminate(cluster_id, providers, log, debug=False, assume_yes=False):
32+
# pylint: disable=too-many-locals
33+
def terminate(*, cluster_id, providers, floating_ip_ids, log, debug=False, assume_yes=False):
3334
"""
3435
Goes through all providers and gets info of all servers which name contains cluster ID.
3536
It then checks if any resources are reserved, but not used and frees them that were hold by the cluster.
@@ -58,9 +59,9 @@ def terminate(cluster_id, providers, log, debug=False, assume_yes=False):
5859
f"This might not be your cluster. Are you sure you want to terminate it?\n"
5960
f"Any non-empty input to shutdown cluster {cluster_id}. "
6061
f"Empty input to exit with cluster still alive:"):
61-
for provider in providers:
62+
for provider, floating_ip_id in zip(providers, floating_ip_ids):
6263
log.info("Terminating cluster %s on cloud %s", cluster_id, provider.cloud_specification['identifier'])
63-
cluster_server_state += terminate_servers(cluster_id, provider, log)
64+
cluster_server_state += terminate_servers(cluster_id, provider, floating_ip_id, log)
6465
cluster_keypair_state.append(delete_keypairs(provider, tmp_keyname, log))
6566
cluster_security_group_state.append(delete_security_groups(provider, cluster_id, security_groups, log))
6667
cluster_volume_state.append(delete_non_permanent_volumes(provider, cluster_id, log))
@@ -72,11 +73,12 @@ def terminate(cluster_id, providers, log, debug=False, assume_yes=False):
7273
return 0
7374

7475

75-
def terminate_servers(cluster_id, provider, log):
76+
def terminate_servers(cluster_id, provider, floating_ip_id, log):
7677
"""
7778
Terminates all servers that match the bibigrid regex.
7879
@param cluster_id: id of cluster to terminate
7980
@param provider: provider that holds all servers in server_list
81+
@param floating_ip_id: floating ip id of master/vpn
8082
@param log:
8183
@return: a list of the servers' (that were to be terminated) termination states
8284
"""
@@ -89,19 +91,20 @@ def terminate_servers(cluster_id, provider, log):
8991
if server_regex.match(server["name"]):
9092
log.info("Trying to terminate Server %s on cloud %s.", server['name'],
9193
provider.cloud_specification['identifier'])
92-
cluster_server_state.append(terminate_server(provider, server, log))
94+
cluster_server_state.append(terminate_server(provider, server, not bool(floating_ip_id), log))
9395
return cluster_server_state
9496

9597

96-
def terminate_server(provider, server, log):
98+
def terminate_server(provider, server, delete_ips, log):
9799
"""
98100
Terminates a single server and stores the termination state
99101
@param provider: the provider that holds the server.
100102
@param server: the server that is to be terminated
103+
@param delete_ips: if true all floating ips of the server are released
101104
@param log:
102105
@return: true if the server has been terminated, false else
103106
"""
104-
terminated = provider.delete_server(server["id"])
107+
terminated = provider.delete_server(server["id"], delete_ips=delete_ips)
105108
if not terminated:
106109
log.warning("Unable to terminate server %s on provider %s.", server['name'],
107110
provider.cloud_specification['identifier'])

bibigrid/core/provider.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,43 @@ def get_external_network(self, network_name_or_id):
203203
"""
204204

205205
@abstractmethod
206-
def attach_available_floating_ip(self, network=None, server=None):
206+
def add_ip_list(self, *, server, ips, wait=False, timeout=60, fixed_address=None, nat_destination=None):
207207
"""
208-
Get a floating IP from a network or a pool and attach it to the server
208+
Add ip list to server.
209+
:param server:
210+
:param ips:
211+
:param wait:
212+
:param timeout:
213+
:param fixed_address:
214+
:param nat_destination:
215+
:return:
216+
"""
217+
218+
@abstractmethod
219+
def get_floating_ip(self, floating_ip_id, filters=None):
220+
"""
221+
Get a floating IP by id.
222+
@param floating_ip_id:
223+
@param filters:
224+
@return:
225+
"""
226+
227+
@abstractmethod
228+
def create_floating_ip(self, *, network=None, server=None, fixed_address=None, nat_destination=None, port=None,
229+
wait=False, timeout=60):
230+
"""
231+
209232
@param network:
210233
@param server:
234+
@param fixed_address:
235+
@param nat_destination:
236+
@param port:
237+
@param wait:
238+
@param timeout:
211239
@return:
212240
"""
213241

242+
214243
@abstractmethod
215244
def get_images(self):
216245
"""

bibigrid/core/startup.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,10 @@ def run_action(action, configurations, config_input, cluster_id, debug):
142142
exit_state = creator.create()
143143
case 'terminate':
144144
LOG.info("Action terminate selected")
145-
exit_state = terminate.terminate(cluster_id=cluster_id, providers=providers, log=LOG,
146-
debug=debug)
145+
exit_state = terminate.terminate(cluster_id=cluster_id, providers=providers,
146+
floating_ip_ids=configuration_handler.get_list_by_key(
147+
configurations=configurations, key="floatingIpId"),
148+
log=LOG, debug=debug)
147149
case 'ide':
148150
LOG.info("Action ide selected")
149151
exit_state = ide.ide(cluster_id, providers[0], configurations[0], LOG)

bibigrid/core/startup_rest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,9 @@ async def terminate_cluster(cluster_id: str,
193193

194194
try:
195195
providers = provider_handler.get_providers(configurations, log)
196-
thread = threading.Thread(target=terminate.terminate, args=(cluster_id, providers, log))
196+
thread = threading.Thread(target=terminate.terminate,
197+
args=(cluster_id, providers, configuration_handler.get_list_by_key(
198+
configurations=configurations, key="floatingIpId"), log))
197199
thread.start()
198200
return JSONResponse(content={"message": "Termination successfully requested."}, status_code=202)
199201
except Exception as exc: # pylint: disable=broad-except

bibigrid/core/utility/handler/configuration_handler.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ def read_configuration(log, path, configuration_list=True):
4242
return configuration
4343

4444

45-
def get_list_by_key(configurations, key, get_empty=True):
45+
def get_list_by_key(configurations, key, include_none=True):
4646
"""
4747
Returns a list of objects which are value to the key.
48-
@param get_empty: if true empty configurations return None
48+
@param include_none: if true empty configurations return None
4949
@param configurations: YAML of configuration File containing the configuration-data for each provider
5050
@param key: Key that is looked out for
5151
@return: List of values of said key through all configs
5252
"""
53-
return [configuration.get(key) for configuration in configurations if configuration.get(key) or get_empty]
53+
return [configuration.get(key) for configuration in configurations if configuration.get(key) or include_none]
5454

5555

5656
# def get_dict_list_by_key_list(configurations, keys, get_empty=True):

bibigrid/core/utility/validate_configuration.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ def validate(self):
214214
checks = [("master/vpn", self._check_master_vpn_worker), ("servergroup", self._check_server_groups),
215215
("instances", self._check_instances), ("volumes", self._check_volumes),
216216
("network", self._check_network),
217+
("floatingIp", self._check_floating_ip),
217218
("quotas", self._check_quotas), ("sshPublicKeyFiles", self._check_ssh_public_key_files),
218219
("cloudYamls", self.check_clouds_yamls), ("nfs", self._check_nfs),
219220
("global security groups", self._check_configurations_security_groups)]
@@ -222,6 +223,19 @@ def validate(self):
222223
success = evaluate(check_name, check_function(), self.log) and success
223224
return success
224225

226+
def _check_floating_ip(self):
227+
success = True
228+
for configuration, provider in zip(self.configurations, self.providers):
229+
if configuration.get("floatingIpId"):
230+
floating_ip = provider.get_floating_ip(id=configuration["floatingIpId"])
231+
if not floating_ip:
232+
self.log.warning(f"Couldn't find floating ip {configuration["floatingIpId"]} on cloud "
233+
f"{provider.cloud_specification['identifier']}.")
234+
success = False
235+
else:
236+
self.log.debug(f"Found {floating_ip} on cloud {provider.cloud_specification['identifier']}.")
237+
return success
238+
225239
def _check_security_groups(self, provider, security_groups):
226240
success = True
227241
if not security_groups:

bibigrid/models/configuration.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class BaseConfig(StrictModel):
137137
sshUser: str
138138
subnet: Optional[str] = Field(default=None)
139139
network: Optional[str] = Field(default=None)
140+
floatingIpId: Optional[str] = Field(default=None)
140141
securityGroups: Optional[list[str]] = Field(default_factory=list)
141142
serverGroup: Optional[str] = None
142143
waitForServices: Optional[List[str]] = Field(default_factory=list)

bibigrid/openstack/openstack_provider.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -288,16 +288,45 @@ def get_external_network(self, network_name_or_id):
288288
return router.external_gateway_info["network_id"]
289289
return None
290290

291-
def attach_available_floating_ip(self, network=None, server=None):
291+
def get_floating_ip(self, floating_ip_id, filters=None):
292292
"""
293-
Get a floating IP from a network or a pool and attach it to the server
293+
Get a floating IP by id.
294+
@param floating_ip_id:
295+
@param filters:
296+
@return:
297+
"""
298+
return self.conn.get_floating_ip(floating_ip_id, filters)
299+
300+
def add_ip_list(self, *, server, ips, wait=False, timeout=60, fixed_address=None, nat_destination=None):
301+
"""
302+
Add ip list to server.
303+
:param server:
304+
:param ips:
305+
:param wait:
306+
:param timeout:
307+
:param fixed_address:
308+
:param nat_destination:
309+
:return:
310+
"""
311+
return self.conn.add_ip_list(server, ips, wait=wait, timeout=timeout, fixed_address=fixed_address,
312+
nat_destination=nat_destination)
313+
314+
def create_floating_ip(self, *, network=None, server=None, fixed_address=None, nat_destination=None, port=None,
315+
wait=True, timeout=60):
316+
"""
317+
294318
@param network:
295319
@param server:
320+
@param fixed_address:
321+
@param nat_destination:
322+
@param port:
323+
@param wait:
324+
@param timeout:
296325
@return:
297326
"""
298-
floating_ip = self.conn.available_floating_ip(network=network, server=server)
299-
self.conn.add_ip_list(server, [floating_ip["floating_ip_address"]])
300-
return floating_ip
327+
return self.conn.create_floating_ip(network=network, server=server, fixed_address=fixed_address,
328+
nat_destination=nat_destination,
329+
port=port, wait=wait, timeout=timeout)
301330

302331
def get_images(self):
303332
"""

0 commit comments

Comments
 (0)