def update(self, defn: HcloudVolumeDefinition, model: BoundVolume) -> None:
     if defn.config.location != model.location.name:
         self.logger.error(
             "Cannot update the location of a Hetzner Cloud volume")
     if defn.config.size < model.size:
         self.logger.error("Cannot shrink volume")
     elif defn.config.size > model.size:
         if not self.depl.logger.confirm(f"Resize volume {self.name!r}?"):
             return
         model.resize(defn.config.size).wait_until_finished()
         self.size = defn.config.size
    def test_bound_volume_init(self, volume_response):
        bound_volume = BoundVolume(
            client=mock.MagicMock(),
            data=volume_response['volume']
        )

        assert bound_volume.id == 1
        assert bound_volume.created == isoparse("2016-01-30T23:50:11+00:00")
        assert bound_volume.name == "database-storage"
        assert isinstance(bound_volume.server, BoundServer)
        assert bound_volume.server.id == 12
        assert bound_volume.size == 42
        assert bound_volume.linux_device == "/dev/disk/by-id/scsi-0HC_Volume_4711"
        assert bound_volume.protection == {"delete": False}
        assert bound_volume.labels == {}
        assert bound_volume.status == "available"

        assert isinstance(bound_volume.location, BoundLocation)
        assert bound_volume.location.id == 1
        assert bound_volume.location.name == "fsn1"
        assert bound_volume.location.description == "Falkenstein DC Park 1"
        assert bound_volume.location.country == "DE"
        assert bound_volume.location.city == "Falkenstein"
        assert bound_volume.location.latitude == 50.47612
        assert bound_volume.location.longitude == 12.370071
Exemple #3
0
    def __init__(self, client, data, complete=True):

        datacenter = data.get('datacenter')
        if datacenter is not None:
            data['datacenter'] = BoundDatacenter(client._client.datacenters, datacenter)

        volumes = data.get('volumes', [])
        if volumes:
            volumes = [BoundVolume(client._client.volumes, {"id": volume}, complete=False) for volume in volumes]
            data['volumes'] = volumes

        image = data.get("image", None)
        if image is not None:
            data['image'] = BoundImage(client._client.images, image)

        iso = data.get("iso", None)
        if iso is not None:
            data['iso'] = BoundIso(client._client.isos, iso)

        server_type = data.get("server_type")
        if server_type is not None:
            data['server_type'] = BoundServerType(client._client.server_types, server_type)

        public_net = data.get("public_net")
        if public_net:
            ipv4_address = IPv4Address(**public_net['ipv4'])
            ipv6_network = IPv6Network(**public_net['ipv6'])
            floating_ips = [BoundFloatingIP(client._client.floating_ips, {"id": floating_ip}, complete=False) for
                            floating_ip in public_net['floating_ips']]
            data['public_net'] = PublicNetwork(ipv4=ipv4_address, ipv6=ipv6_network, floating_ips=floating_ips)

        super(BoundServer, self).__init__(client, data, complete)
Exemple #4
0
    def test_create_with_volumes(self, servers_client, response_create_simple_server):
        servers_client._client.request.return_value = response_create_simple_server
        volumes = [Volume(id=1), BoundVolume(mock.MagicMock(), dict(id=2))]
        response = servers_client.create(
            "my-server",
            server_type=ServerType(name="cx11"),
            image=Image(id=4711),
            volumes=volumes,
            start_after_create=False
        )
        servers_client._client.request.assert_called_with(
            url="/servers",
            method="POST",
            json={
                'name': "my-server",
                'server_type': "cx11",
                'image': 4711,
                'volumes': [1, 2],
                "start_after_create": False
            }
        )

        bound_server = response.server
        bound_action = response.action
        next_actions = response.next_actions
        root_password = response.root_password

        assert root_password == "YItygq1v3GYjjMomLaKc"

        assert bound_server._client is servers_client
        assert bound_server.id == 1
        assert bound_server.name == "my-server"

        assert isinstance(bound_action, BoundAction)
        assert bound_action._client == servers_client._client.actions
        assert bound_action.id == 1
        assert bound_action.command == "create_server"

        assert next_actions[0].id == 13
 def bound_volume(self, hetzner_client):
     return BoundVolume(client=hetzner_client.volumes, data=dict(id=14))
class TestVolumesClient(object):

    @pytest.fixture()
    def volumes_client(self):
        return VolumesClient(client=mock.MagicMock())

    def test_get_by_id(self, volumes_client, volume_response):
        volumes_client._client.request.return_value = volume_response
        bound_volume = volumes_client.get_by_id(1)
        volumes_client._client.request.assert_called_with(url="/volumes/1", method="GET")
        assert bound_volume._client is volumes_client
        assert bound_volume.id == 1
        assert bound_volume.name == "database-storage"

    @pytest.mark.parametrize(
        "params",
        [
            {'label_selector': "label1", 'page': 1, 'per_page': 10},
            {}
        ]
    )
    def test_get_list(self, volumes_client, two_volumes_response, params):
        volumes_client._client.request.return_value = two_volumes_response
        result = volumes_client.get_list(**params)
        volumes_client._client.request.assert_called_with(url="/volumes", method="GET", params=params)

        bound_volumes = result.volumes
        assert result.meta is None

        assert len(bound_volumes) == 2

        bound_volume1 = bound_volumes[0]
        bound_volume2 = bound_volumes[1]

        assert bound_volume1._client is volumes_client
        assert bound_volume1.id == 1
        assert bound_volume1.name == "database-storage"

        assert bound_volume2._client is volumes_client
        assert bound_volume2.id == 2
        assert bound_volume2.name == "vault-storage"

    @pytest.mark.parametrize("params", [{'label_selector': "label1"}])
    def test_get_all(self, volumes_client, two_volumes_response, params):
        volumes_client._client.request.return_value = two_volumes_response
        bound_volumes = volumes_client.get_all(**params)

        params.update({'page': 1, 'per_page': 50})

        volumes_client._client.request.assert_called_with(url="/volumes", method="GET", params=params)

        assert len(bound_volumes) == 2

        bound_volume1 = bound_volumes[0]
        bound_volume2 = bound_volumes[1]

        assert bound_volume1._client is volumes_client
        assert bound_volume1.id == 1
        assert bound_volume1.name == "database-storage"

        assert bound_volume2._client is volumes_client
        assert bound_volume2.id == 2
        assert bound_volume2.name == "vault-storage"

    def test_get_by_name(self, volumes_client, one_volumes_response):
        volumes_client._client.request.return_value = one_volumes_response
        bound_volume = volumes_client.get_by_name("database-storage")

        params = {'name': "database-storage"}

        volumes_client._client.request.assert_called_with(url="/volumes", method="GET", params=params)

        assert bound_volume._client is volumes_client
        assert bound_volume.id == 1
        assert bound_volume.name == "database-storage"

    def test_create_with_location(self, volumes_client, volume_create_response):
        volumes_client._client.request.return_value = volume_create_response
        response = volumes_client.create(
            100,
            "database-storage",
            location=Location(name="location"),
            automount=False,
            format="xfs"
        )
        volumes_client._client.request.assert_called_with(
            url="/volumes",
            method="POST",
            json={
                'name': "database-storage",
                'size': 100,
                'location': "location",
                'automount': False,
                'format': "xfs"
            }
        )

        bound_volume = response.volume
        action = response.action
        next_actions = response.next_actions

        assert bound_volume._client is volumes_client
        assert bound_volume.id == 4711
        assert bound_volume.name == "database-storage"

        assert action.id == 13
        assert next_actions[0].command == "start_server"

    @pytest.mark.parametrize("server", [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_create_with_server(self, volumes_client, server, volume_create_response):
        volumes_client._client.request.return_value = volume_create_response
        volumes_client.create(
            100,
            "database-storage",
            server=server,
            automount=False,
            format="xfs"
        )
        volumes_client._client.request.assert_called_with(
            url="/volumes",
            method="POST",
            json={
                'name': "database-storage",
                'size': 100,
                'server': 1,
                'automount': False,
                'format': "xfs"
            }
        )

    def test_create_negative_size(self, volumes_client):
        with pytest.raises(ValueError) as e:
            volumes_client.create(
                -100,
                "database-storage",
                location=Location(name="location")
            )
        assert str(e.value) == "size must be greater than 0"
        volumes_client._client.request.assert_not_called()

    @pytest.mark.parametrize("location,server", [(None, None), ("location", Server(id=1))])
    def test_create_wrong_location_server_combination(self, volumes_client, location, server):
        with pytest.raises(ValueError) as e:
            volumes_client.create(
                100,
                "database-storage",
                location=location,
                server=server
            )
        assert str(e.value) == "only one of server or location must be provided"
        volumes_client._client.request.assert_not_called()

    @pytest.mark.parametrize("volume", [Volume(id=1), BoundVolume(mock.MagicMock(), dict(id=1))])
    def test_get_actions_list(self, volumes_client, volume, response_get_actions):
        volumes_client._client.request.return_value = response_get_actions
        result = volumes_client.get_actions_list(volume, sort="id")
        volumes_client._client.request.assert_called_with(url="/volumes/1/actions", method="GET", params={"sort": "id"})

        actions = result.actions
        assert len(actions) == 1
        assert isinstance(actions[0], BoundAction)

        assert actions[0]._client == volumes_client._client.actions
        assert actions[0].id == 13
        assert actions[0].command == "attach_volume"

    @pytest.mark.parametrize("volume", [Volume(id=1), BoundVolume(mock.MagicMock(), dict(id=1))])
    def test_update(self, volumes_client, volume, response_update_volume):
        volumes_client._client.request.return_value = response_update_volume
        volume = volumes_client.update(volume, name="new-name")
        volumes_client._client.request.assert_called_with(url="/volumes/1", method="PUT", json={"name": "new-name"})

        assert volume.id == 4711
        assert volume.name == "new-name"

    @pytest.mark.parametrize("volume", [Volume(id=1), BoundVolume(mock.MagicMock(), dict(id=1))])
    def test_change_protection(self, volumes_client, volume, generic_action):
        volumes_client._client.request.return_value = generic_action
        action = volumes_client.change_protection(volume, True)
        volumes_client._client.request.assert_called_with(url="/volumes/1/actions/change_protection", method="POST", json={"delete": True})

        assert action.id == 1
        assert action.progress == 0

    @pytest.mark.parametrize("volume", [Volume(id=1), BoundVolume(mock.MagicMock(), dict(id=1))])
    def test_delete(self, volumes_client, volume, generic_action):
        volumes_client._client.request.return_value = generic_action
        delete_success = volumes_client.delete(volume)
        volumes_client._client.request.assert_called_with(url="/volumes/1", method="DELETE")

        assert delete_success is True

    @pytest.mark.parametrize("server,volume",
                             [(Server(id=1), Volume(id=12)),
                              (BoundServer(mock.MagicMock(), dict(id=1)), BoundVolume(mock.MagicMock(), dict(id=12)))])
    def test_attach(self, volumes_client, server, volume, generic_action):
        volumes_client._client.request.return_value = generic_action
        action = volumes_client.attach(volume, server)
        volumes_client._client.request.assert_called_with(
            url="/volumes/12/actions/attach",
            method="POST",
            json={"server": 1}
        )
        assert action.id == 1
        assert action.progress == 0

    @pytest.mark.parametrize("volume", [Volume(id=12), BoundVolume(mock.MagicMock(), dict(id=12))])
    def test_detach(self, volumes_client, volume, generic_action):
        volumes_client._client.request.return_value = generic_action
        action = volumes_client.detach(volume)
        volumes_client._client.request.assert_called_with(
            url="/volumes/12/actions/detach",
            method="POST"
        )
        assert action.id == 1
        assert action.progress == 0

    @pytest.mark.parametrize("volume", [Volume(id=12), BoundVolume(mock.MagicMock(), dict(id=12))])
    def test_resize(self, volumes_client, volume, generic_action):
        volumes_client._client.request.return_value = generic_action
        action = volumes_client.resize(volume, 50)
        volumes_client._client.request.assert_called_with(
            url="/volumes/12/actions/resize",
            method="POST",
            json={"size": 50}
        )
        assert action.id == 1
        assert action.progress == 0
Exemple #7
0
    def __init__(self, client, data, complete=True):

        datacenter = data.get('datacenter')
        if datacenter is not None:
            data['datacenter'] = BoundDatacenter(client._client.datacenters,
                                                 datacenter)

        volumes = data.get('volumes', [])
        if volumes:
            volumes = [
                BoundVolume(client._client.volumes, {"id": volume},
                            complete=False) for volume in volumes
            ]
            data['volumes'] = volumes

        image = data.get("image", None)
        if image is not None:
            data['image'] = BoundImage(client._client.images, image)

        iso = data.get("iso", None)
        if iso is not None:
            data['iso'] = BoundIso(client._client.isos, iso)

        server_type = data.get("server_type")
        if server_type is not None:
            data['server_type'] = BoundServerType(client._client.server_types,
                                                  server_type)

        public_net = data.get("public_net")
        if public_net:
            ipv4_address = IPv4Address(**public_net['ipv4'])
            ipv6_network = IPv6Network(**public_net['ipv6'])
            floating_ips = [
                BoundFloatingIP(client._client.floating_ips,
                                {"id": floating_ip},
                                complete=False)
                for floating_ip in public_net['floating_ips']
            ]
            firewalls = [
                PublicNetworkFirewall(BoundFirewall(client._client.firewalls,
                                                    {"id": firewall["id"]},
                                                    complete=False),
                                      status=firewall["status"])
                for firewall in public_net.get("firewalls", [])
            ]
            data['public_net'] = PublicNetwork(ipv4=ipv4_address,
                                               ipv6=ipv6_network,
                                               floating_ips=floating_ips,
                                               firewalls=firewalls)

        private_nets = data.get("private_net")
        if private_nets:
            private_nets = [
                PrivateNet(network=BoundNetwork(client._client.networks,
                                                {"id": private_net['network']},
                                                complete=False),
                           ip=private_net['ip'],
                           alias_ips=private_net['alias_ips'],
                           mac_address=private_net['mac_address'])
                for private_net in private_nets
            ]
            data['private_net'] = private_nets

        super(BoundServer, self).__init__(client, data, complete)
class TestVolumesClient(object):
    def test_get_by_id(self, hetzner_client):
        bound_volume = hetzner_client.volumes.get_by_id(4711)
        assert bound_volume.id == 4711
        assert bound_volume.name == "database-storage"
        assert bound_volume.size == 42

    def test_get_by_name(self, hetzner_client):
        bound_volume = hetzner_client.volumes.get_by_name("database-storage")
        assert bound_volume.id == 4711
        assert bound_volume.name == "database-storage"
        assert bound_volume.size == 42

    def test_get_list(self, hetzner_client):
        result = hetzner_client.volumes.get_list()
        bound_volumes = result.volumes
        assert bound_volumes[0].id == 4711
        assert bound_volumes[0].name == "database-storage"
        assert bound_volumes[0].size == 42

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_create(self, hetzner_client, server):
        response = hetzner_client.volumes.create(
            42,
            "test-database",
            location=Location(name="nbg1"),
            automount=False,
            format="xfs")

        volume = response.volume
        action = response.action
        next_actions = response.next_actions

        assert volume.id == 4711
        assert volume.name == "database-storage"
        assert volume.size == 42

        assert action.id == 13
        assert action.command == "create_volume"

        assert len(next_actions) == 1
        assert next_actions[0].id == 13
        assert next_actions[0].command == "start_server"

    @pytest.mark.parametrize(
        "volume",
        [Volume(id=1), BoundVolume(mock.MagicMock(), dict(id=1))])
    def test_get_actions(self, hetzner_client, volume):
        actions = hetzner_client.volumes.get_actions(volume)

        assert len(actions) == 1
        assert actions[0].id == 13
        assert actions[0].command == "attach_volume"

    @pytest.mark.parametrize(
        "volume",
        [Volume(id=1), BoundVolume(mock.MagicMock(), dict(id=1))])
    def test_update(self, hetzner_client, volume):
        volume = hetzner_client.volumes.update(volume,
                                               name="new-name",
                                               labels={})

        assert volume.id == 4711
        assert volume.name == "new-name"

    @pytest.mark.parametrize(
        "volume",
        [Volume(id=1), BoundVolume(mock.MagicMock(), dict(id=1))])
    def test_delete(self, hetzner_client, volume):
        delete_success = hetzner_client.volumes.delete(volume)

        assert delete_success is True

    @pytest.mark.parametrize("server,volume",
                             [(Server(id=43), Volume(id=4711)),
                              (BoundServer(mock.MagicMock(), dict(id=43)),
                               BoundVolume(mock.MagicMock(), dict(id=4711)))])
    def test_attach(self, hetzner_client, server, volume):
        action = hetzner_client.volumes.attach(volume, server)
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "attach_volume"

    @pytest.mark.parametrize(
        "volume",
        [Volume(id=4711),
         BoundVolume(mock.MagicMock(), dict(id=4711))])
    def test_detach(self, hetzner_client, volume):
        action = hetzner_client.volumes.detach(volume)
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "detach_volume"

    @pytest.mark.parametrize(
        "volume",
        [Volume(id=4711),
         BoundVolume(mock.MagicMock(), dict(id=4711))])
    def test_resize(self, hetzner_client, volume):
        action = hetzner_client.volumes.resize(volume, 50)
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "resize_volume"