diff --git a/CHANGELOG.md b/CHANGELOG.md index db19461..56ececa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added missing fields to the `Volume` class: `pseudo_path`, `mount_command`, `create_directory_command`, `filesystem_to_fstab_command`, `instances`, `contract`, `base_hourly_cost`, `monthly_price`, `currency`, `long_term` + ## [1.23.1] - 2026-03-25 ### Fixed diff --git a/tests/unit_tests/volumes/test_volumes.py b/tests/unit_tests/volumes/test_volumes.py index e573c4d..1c45950 100644 --- a/tests/unit_tests/volumes/test_volumes.py +++ b/tests/unit_tests/volumes/test_volumes.py @@ -50,6 +50,24 @@ 'created_at': NVME_VOL_CREATED_AT, 'target': TARGET_VDA, 'ssh_key_ids': SSH_KEY_ID, + 'pseudo_path': 'volume-nxC2tf9F', + 'mount_command': 'mount -t nfs -o nconnect=16 nfs.fin-01.datacrunch.io:volume-nxC2tf9F /mnt/volume', + 'create_directory_command': 'mkdir -p /mnt/volume', + 'filesystem_to_fstab_command': "grep -qxF 'nfs.fin-01.datacrunch.io:volume-nxC2tf9F /mnt/volume nfs defaults 0 0' /etc/fstab || echo 'nfs.fin-01.datacrunch.io:volume-nxC2tf9F /mnt/volume nfs defaults 0 0' | sudo tee -a /etc/fstab", + 'instances': [ + { + 'id': INSTANCE_ID, + 'ip': '123.123.123.123', + 'instance_type': '4A100.88V', + 'status': 'running', + 'hostname': 'hazy-star-swims-fin-01', + } + ], + 'contract': 'PAY_AS_YOU_GO', + 'base_hourly_cost': 0.0273972602739726, + 'monthly_price': 20, + 'currency': 'eur', + 'long_term': None, } HDD_VOLUME = { @@ -64,6 +82,16 @@ 'created_at': HDD_VOL_CREATED_AT, 'target': None, 'ssh_key_ids': [], + 'pseudo_path': 'volume-iHdL4ysR', + 'mount_command': 'mount -t nfs -o nconnect=16 nfs.fin-01.datacrunch.io:volume-iHdL4ysR /mnt/volume', + 'create_directory_command': 'mkdir -p /mnt/volume', + 'filesystem_to_fstab_command': "grep -qxF 'nfs.fin-01.datacrunch.io:volume-iHdL4ysR /mnt/volume nfs defaults 0 0' /etc/fstab || echo 'nfs.fin-01.datacrunch.io:volume-iHdL4ysR /mnt/volume nfs defaults 0 0' | sudo tee -a /etc/fstab", + 'instances': [], + 'contract': 'PAY_AS_YOU_GO', + 'base_hourly_cost': 0.01, + 'monthly_price': 10, + 'currency': 'eur', + 'long_term': None, } PAYLOAD = [NVME_VOLUME, HDD_VOLUME] @@ -101,6 +129,34 @@ def test_initialize_a_volume(self): assert volume.target is None assert volume.ssh_key_ids == [] + def test_create_from_dict_without_new_fields(self): + """Test that create_from_dict handles API responses missing the new fields.""" + minimal_dict = { + 'id': RANDOM_VOL_ID, + 'status': VolumeStatus.DETACHED, + 'name': HDD_VOL_NAME, + 'size': HDD_VOL_SIZE, + 'type': HDD, + 'is_os_volume': False, + 'created_at': HDD_VOL_CREATED_AT, + 'target': None, + 'location': Locations.FIN_01, + 'instance_id': None, + 'ssh_key_ids': [], + } + volume = Volume.create_from_dict(minimal_dict) + assert volume.id == RANDOM_VOL_ID + assert volume.pseudo_path is None + assert volume.mount_command is None + assert volume.create_directory_command is None + assert volume.filesystem_to_fstab_command is None + assert volume.instances is None + assert volume.contract is None + assert volume.base_hourly_cost is None + assert volume.monthly_price is None + assert volume.currency is None + assert volume.long_term is None + def test_get_volumes(self, volumes_service, endpoint): # arrange - add response mock responses.add(responses.GET, endpoint, json=PAYLOAD, status=200) @@ -126,6 +182,16 @@ def test_get_volumes(self, volumes_service, endpoint): assert volume_nvme.created_at == NVME_VOL_CREATED_AT assert volume_nvme.target == TARGET_VDA assert volume_nvme.ssh_key_ids == SSH_KEY_ID + assert volume_nvme.pseudo_path == NVME_VOLUME['pseudo_path'] + assert volume_nvme.mount_command == NVME_VOLUME['mount_command'] + assert volume_nvme.create_directory_command == NVME_VOLUME['create_directory_command'] + assert volume_nvme.filesystem_to_fstab_command == NVME_VOLUME['filesystem_to_fstab_command'] + assert volume_nvme.instances == NVME_VOLUME['instances'] + assert volume_nvme.contract == 'PAY_AS_YOU_GO' + assert volume_nvme.base_hourly_cost == NVME_VOLUME['base_hourly_cost'] + assert volume_nvme.monthly_price == 20 + assert volume_nvme.currency == 'eur' + assert volume_nvme.long_term is None assert volume_hdd.id == HDD_VOL_ID assert volume_hdd.status == HDD_VOL_STATUS diff --git a/verda/volumes/_volumes.py b/verda/volumes/_volumes.py index ca66573..e896b7f 100644 --- a/verda/volumes/_volumes.py +++ b/verda/volumes/_volumes.py @@ -21,6 +21,16 @@ def __init__( instance_id: str | None = None, ssh_key_ids: list[str] = [], deleted_at: str | None = None, + pseudo_path: str | None = None, + mount_command: str | None = None, + create_directory_command: str | None = None, + filesystem_to_fstab_command: str | None = None, + instances: list[dict] | None = None, + contract: str | None = None, + base_hourly_cost: float | None = None, + monthly_price: float | None = None, + currency: str | None = None, + long_term: dict | None = None, ) -> None: """Initialize the volume object. @@ -48,6 +58,26 @@ def __init__( :type ssh_key_ids: list[str] :param deleted_at: the time the volume was deleted (UTC), defaults to None :type deleted_at: str, optional + :param pseudo_path: volume pseudo path for NFS export, defaults to None + :type pseudo_path: str, optional + :param mount_command: ready-to-use NFS mount command, defaults to None + :type mount_command: str, optional + :param create_directory_command: mkdir command for mount point, defaults to None + :type create_directory_command: str, optional + :param filesystem_to_fstab_command: fstab entry command for persistent mounts, defaults to None + :type filesystem_to_fstab_command: str, optional + :param instances: list of attached instance details, defaults to None + :type instances: list[dict], optional + :param contract: volume contract type e.g. "LONG_TERM", "PAY_AS_YOU_GO", defaults to None + :type contract: str, optional + :param base_hourly_cost: volume base hourly cost, defaults to None + :type base_hourly_cost: float, optional + :param monthly_price: volume monthly price, defaults to None + :type monthly_price: float, optional + :param currency: volume currency e.g. "usd", "eur", defaults to None + :type currency: str, optional + :param long_term: long term contract details, defaults to None + :type long_term: dict, optional """ self._id = id self._status = status @@ -61,6 +91,16 @@ def __init__( self._instance_id = instance_id self._ssh_key_ids = ssh_key_ids self._deleted_at = deleted_at + self._pseudo_path = pseudo_path + self._mount_command = mount_command + self._create_directory_command = create_directory_command + self._filesystem_to_fstab_command = filesystem_to_fstab_command + self._instances = instances + self._contract = contract + self._base_hourly_cost = base_hourly_cost + self._monthly_price = monthly_price + self._currency = currency + self._long_term = long_term @property def id(self) -> str: @@ -170,6 +210,96 @@ def deleted_at(self) -> str | None: """ return self._deleted_at + @property + def pseudo_path(self) -> str | None: + """Get the volume pseudo path for NFS export. + + :return: volume pseudo path + :rtype: str, optional + """ + return self._pseudo_path + + @property + def mount_command(self) -> str | None: + """Get the ready-to-use NFS mount command. + + :return: mount command + :rtype: str, optional + """ + return self._mount_command + + @property + def create_directory_command(self) -> str | None: + """Get the mkdir command for creating the mount point directory. + + :return: create directory command + :rtype: str, optional + """ + return self._create_directory_command + + @property + def filesystem_to_fstab_command(self) -> str | None: + """Get the fstab entry command for persistent mounts. + + :return: fstab command + :rtype: str, optional + """ + return self._filesystem_to_fstab_command + + @property + def instances(self) -> list[dict] | None: + """Get the list of attached instance details. + + :return: list of instance details + :rtype: list[dict], optional + """ + return self._instances + + @property + def contract(self) -> str | None: + """Get the volume contract type. + + :return: contract type e.g. "LONG_TERM", "PAY_AS_YOU_GO" + :rtype: str, optional + """ + return self._contract + + @property + def base_hourly_cost(self) -> float | None: + """Get the volume base hourly cost. + + :return: base hourly cost + :rtype: float, optional + """ + return self._base_hourly_cost + + @property + def monthly_price(self) -> float | None: + """Get the volume monthly price. + + :return: monthly price + :rtype: float, optional + """ + return self._monthly_price + + @property + def currency(self) -> str | None: + """Get the volume currency. + + :return: currency e.g. "usd", "eur" + :rtype: str, optional + """ + return self._currency + + @property + def long_term(self) -> dict | None: + """Get the long term contract details. + + :return: long term contract details + :rtype: dict, optional + """ + return self._long_term + @classmethod def create_from_dict(cls: 'Volume', volume_dict: dict) -> 'Volume': """Create a Volume object from a dictionary. @@ -192,6 +322,16 @@ def create_from_dict(cls: 'Volume', volume_dict: dict) -> 'Volume': instance_id=volume_dict['instance_id'], ssh_key_ids=volume_dict['ssh_key_ids'], deleted_at=volume_dict.get('deleted_at'), + pseudo_path=volume_dict.get('pseudo_path'), + mount_command=volume_dict.get('mount_command'), + create_directory_command=volume_dict.get('create_directory_command'), + filesystem_to_fstab_command=volume_dict.get('filesystem_to_fstab_command'), + instances=volume_dict.get('instances'), + contract=volume_dict.get('contract'), + base_hourly_cost=volume_dict.get('base_hourly_cost'), + monthly_price=volume_dict.get('monthly_price'), + currency=volume_dict.get('currency'), + long_term=volume_dict.get('long_term'), ) def __str__(self) -> str: