-
Notifications
You must be signed in to change notification settings - Fork 927
uniformize create node in ovh #2124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ovh driver | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from libcloud.utils.publickey import get_pubkey_comment, get_pubkey_ssh2_fingerprint | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from libcloud.utils.py3 import httplib | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from libcloud.common.ovh import API_ROOT, OvhConnection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from libcloud.compute.base import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -119,21 +120,11 @@ def ex_get_node(self, node_id): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self._to_node(response.object) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def create_node(self, name, image, size, location, ex_keyname=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def create_node(self, name, image, size, location, auth=None, ex_keyname=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Create a new node | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :keyword name: Name of created node | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :type name: ``str`` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :keyword image: Image used for node | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :type image: :class:`NodeImage` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :keyword size: Size (flavor) used for node | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :type size: :class:`NodeSize` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :keyword location: Location (region) where to create node | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :type location: :class:`NodeLocation` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @inherits: :class:`NodeDriver.create_node` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :keyword ex_keyname: Name of SSH key used | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :type ex_keyname: ``str`` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -148,14 +139,33 @@ def create_node(self, name, image, size, location, ex_keyname=None): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "flavorId": size.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "region": location.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if auth and ex_keyname: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise AttributeError("Cannot specify auth and ex_keyname together") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if auth: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auth = self._get_and_check_auth(auth) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # pylint: disable=no-member | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| key = self.ex_find_or_import_keypair_by_key_material(location,auth.pubkey) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ex_keyname = key["keyName"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+148
to
+149
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| key = self.ex_find_or_import_keypair_by_key_material(location,auth.pubkey) | |
| ex_keyname = key["keyName"] | |
| key = self.ex_find_or_import_keypair_by_key_material(location, auth.pubkey) | |
| ex_keyname = key.get("keyName") or key.get("name") | |
| if not ex_keyname: | |
| raise AttributeError("Unable to determine key name from auth-provided key material") |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace detected. Remove the trailing whitespace at the end of this line to maintain consistency with the rest of the codebase and adhere to Python style guidelines (PEP 8).
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing documentation for the new public method ex_find_or_import_keypair_by_key_material. Public methods should have docstrings that describe their purpose, parameters, return values, and any exceptions they may raise. Consider following the pattern from the EC2 driver's implementation for consistency.
| def ex_find_or_import_keypair_by_key_material(self, location, pubkey): | |
| def ex_find_or_import_keypair_by_key_material(self, location, pubkey): | |
| """ | |
| Find an existing key pair matching the given public key material or | |
| import it as a new key pair in the specified location. | |
| :param location: Location (region) where the key pair should be | |
| looked up or imported. | |
| :type location: :class:`NodeLocation` | |
| :param pubkey: Public key material (for example, in OpenSSH format). | |
| :type pubkey: ``str`` | |
| :return: Dictionary containing key pair information. For an existing | |
| key pair this includes ``keyName`` and ``keyId``; for a | |
| newly imported key pair this includes ``keyName`` and | |
| ``keyFingerprint``. | |
| :rtype: ``dict`` | |
| :raises Exception: If listing existing key pairs or importing a new | |
| key pair fails in the underlying API. | |
| """ |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential issue with key name collision. If the same public key material already exists in OVH but with a different name than the generated one (computed from fingerprint and comment), attempting to import it on line 166 may fail if OVH enforces uniqueness on public key material. Consider handling potential exceptions from import_key_pair_from_string and checking if the failure is due to a duplicate key, in which case you should search for the existing key by its fingerprint or public key material and return that instead.
| key_pair=self.import_key_pair_from_string(key_name, pubkey, ex_location=location) | |
| try: | |
| key_pair = self.import_key_pair_from_string( | |
| key_name, pubkey, ex_location=location | |
| ) | |
| except Exception as e: | |
| # If the provider enforces uniqueness on public key material, trying to | |
| # import an already-existing key (possibly under a different name) may | |
| # raise an error. In that case, attempt to find and return the existing | |
| # key instead of failing. | |
| message = str(e).lower() | |
| duplicate_indicators = ("already exists", "duplicate", "conflict") | |
| if not any(indicator in message for indicator in duplicate_indicators): | |
| # Not clearly a duplicate-key situation; propagate the error. | |
| raise | |
| # Best-effort lookup of an existing key that matches this material. | |
| key_pairs = self.list_key_pairs(ex_location=location) | |
| for key_pair in key_pairs: | |
| if getattr(key_pair, "public_key", None) == pubkey: | |
| return { | |
| "keyName": key_pair.name, | |
| "keyFingerprint": getattr(key_pair, "fingerprint", None), | |
| } | |
| if getattr(key_pair, "fingerprint", None) == key_fingerprint: | |
| return { | |
| "keyName": key_pair.name, | |
| "keyFingerprint": key_pair.fingerprint, | |
| } | |
| # No matching key could be found even though the provider reported a | |
| # duplicate; re-raise to avoid hiding a potential inconsistency. | |
| raise |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space around assignment operator. Should be key_pair = self.import_key_pair_from_string(...) for consistency with Python style guidelines (PEP 8).
| key_pair=self.import_key_pair_from_string(key_name, pubkey, ex_location=location) | |
| key_pair = self.import_key_pair_from_string(key_name, pubkey, ex_location=location) |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent return value structure and potential issue with None value. Line 162 returns keyId (from key_pair.extra["id"]) but line 167 returns keyFingerprint (from key_pair.fingerprint). However, the _to_key_pair method (line 612) sets fingerprint=None for OVH keypairs, so this will return None instead of a valid fingerprint value. Both code paths should return the same dictionary structure with the same keys. Since the calling code on line 149 only uses key["keyName"], you should either: 1) return keyId in both cases (preferred, for consistency), or 2) ensure that only keyName is used and remove the extra keys from the return values.
| key_pair=self.import_key_pair_from_string(key_name, pubkey, ex_location=location) | |
| return {"keyName": key_pair.name, "keyFingerprint": key_pair.fingerprint} | |
| key_pair = self.import_key_pair_from_string( | |
| key_name, pubkey, ex_location=location | |
| ) | |
| return {"keyName": key_pair.name, "keyId": key_pair.extra["id"]} |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace detected. Remove the trailing whitespace at the end of this line to maintain consistency with the rest of the codebase and adhere to Python style guidelines (PEP 8).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space after comma. Should be
self.ex_find_or_import_keypair_by_key_material(location, auth.pubkey)for consistency with Python style guidelines (PEP 8).