Ejemplo n.º 1
0
 def reboot(self, hard: bool = False) -> None:
     question = f"are you sure you want to reboot {self.full_name}?"
     if self.state == self.UP and not self.depl.logger.confirm(question):
         return
     if hard:
         self.logger.log_start(f"sending hard reset to {self.full_name}...")
         self.wait_on_action(self.get_client().servers.reset(
             Server(self.vm_id)))
     else:
         self.logger.log_start(
             f"sending ACPI reboot request to {self.full_name}...")
         self.wait_on_action(self.get_client().servers.reboot(
             Server(self.vm_id)))
     self.wait_for_ssh()
     self.state = self.UP
Ejemplo n.º 2
0
 def test_created_is_datetime(self):
     server = Server(id=1, created="2016-01-30T23:50+00:00")
     assert server.created == datetime.datetime(2016,
                                                1,
                                                30,
                                                23,
                                                50,
                                                tzinfo=tzoffset(None, 0))
Ejemplo n.º 3
0
    def _handle_changed_floating_ips(self, defn: HetznerCloudDefinition,
                                     allow_recreate: bool) -> None:
        """
        Detects and corrects any floating IP state desynchronisation.
        """

        assigned: Set[str] = {
            x.name
            for x in self.get_instance().public_net.floating_ips
        }

        for name in self.ip_addresses.keys():
            fip: Optional[BoundFloatingIP] = self.get_client(
            ).floating_ips.get_by_name(name)

            # Detect manually destroyed floating IPs
            if fip is None:
                if name not in defn.ip_addresses:  # we dont need it
                    self.logger.warn(
                        f"forgetting about floating IP ‘{name}’ that no longer"
                        " exists and is no longer needed by the deployment"
                        " specification")
                    self._update_attr("ip_addresses", name, None)
                else:
                    if name.startswith("nixops-" + self.depl.uuid):
                        raise Exception(
                            f"floating IP ‘{name}’ (used by {self.full_name})"
                            " no longer exists; run ‘nixops deploy --check’"
                            " to update resource state")
                    else:
                        raise Exception(
                            f"floating IP ‘{name}’ (used by {self.full_name})"
                            " was manually destroyed")

            # Detect unassigned floating IPs
            elif name not in assigned:
                if name not in defn.ip_addresses:  # we dont need it
                    self.logger.warn(
                        f"forgetting about unassigned floating IP ‘{name}’ [{fip.id}]"
                        " that is no longer needed by the deployment specification"
                    )
                else:  # we do need it
                    self.logger.warn(
                        f"floating IP ‘{name}’ [{fip.id}] was manually unassigned;"
                        " will reassign it.")
                    self._update_attr("ip_addresses", name, None)

        # Assign missing floating IPs.
        for name in defn.ip_addresses:
            if name not in self.ip_addresses:
                fip = self.get_client().floating_ips.get_by_name(name)
                if fip is None:
                    raise Exception(f"tried to assign floating IP ‘{name}’"
                                    " but it doesn't exist...")
                self.logger.log(
                    f"assigning floating IP ‘{name}’ [{fip.id}]...")
                self.wait_on_action(fip.assign(Server(self.vm_id)))
                self._update_attr("ip_addresses", name, fip.ip)
Ejemplo n.º 4
0
 def stop(self) -> None:
     question = f"are you sure you want to stop {self.full_name}?"
     if not self.depl.logger.confirm(question):
         return
     self.logger.log_start(
         f"sending ACPI shutdown request to {self.full_name}...")
     self.wait_on_action(self.get_client().servers.shutdown(
         Server(self.vm_id)))
     while not self._check_status("off"):
         time.sleep(1)
     self.state = self.STOPPED
Ejemplo n.º 5
0
def delete():
    with open(data_file, "r") as file:
        data = json.load(file)

    client = Client(token=data["token"])
    response = client.servers.delete(server=Server(id=data["server_id"]), )

    data["server_id"] = "SERVER-IS-DOWN"

    with open(data_file, "w") as file:
        json.dump(data, file, indent=4)
    click.echo("deletion complete")
Ejemplo n.º 6
0
 def destroy(self, wipe=False):
     if self.vm_id is None:
         return True
     if wipe:
         self.warn("Wipe is not supported")
     if not self.depl.logger.confirm(
             f"are you sure you want to destroy Hetzner server {self.name}?"
     ):
         return False
     self.log_start("destroying Hetzner Cloud VM...")
     self._client.servers.delete(Server(id=self.vm_id))
     self.log_end("")
     self._reset()
     return True
Ejemplo n.º 7
0
    def test_create(self, firewalls_client, response_create_firewall):
        firewalls_client._client.request.return_value = response_create_firewall
        response = firewalls_client.create(
            "Corporate Intranet Protection",
            rules=[
                FirewallRule(direction=FirewallRule.DIRECTION_IN,
                             protocol=FirewallRule.PROTOCOL_ICMP,
                             source_ips=["0.0.0.0/0"])
            ],
            resources=[
                FirewallResource(type=FirewallResource.TYPE_SERVER,
                                 server=Server(id=4711)),
                FirewallResource(type=FirewallResource.TYPE_LABEL_SELECTOR,
                                 label_selector=FirewallResourceLabelSelector(
                                     selector="key==value"))
            ])
        firewalls_client._client.request.assert_called_with(
            url="/firewalls",
            method="POST",
            json={
                'name':
                "Corporate Intranet Protection",
                'rules': [{
                    "direction": "in",
                    "protocol": "icmp",
                    "source_ips": ["0.0.0.0/0"]
                }],
                "apply_to": [{
                    "type": "server",
                    "server": {
                        "id": 4711
                    }
                }, {
                    "type": "label_selector",
                    "label_selector": {
                        "selector": "key==value"
                    }
                }],
            })

        bound_firewall = response.firewall
        actions = response.actions

        assert bound_firewall._client is firewalls_client
        assert bound_firewall.id == 38
        assert bound_firewall.name == "Corporate Intranet Protection"
        assert len(bound_firewall.applied_to) == 2

        assert len(actions) == 2
Ejemplo n.º 8
0
 def attach_to_network() -> bool:
     try:
         self.wait_on_action(
             self.get_client().servers.attach_to_network(
                 server=Server(self.vm_id),
                 network=nw,
                 ip=x["privateIpAddress"],
                 alias_ips=x["aliasIpAddresses"],
             ))
     except APIException as e:
         if e.code == "conflict":
             return False
         else:
             raise
     else:
         self._update_attr("server_networks", x["network"], x)
         return True
Ejemplo n.º 9
0
class TestBoundFloatingIPs(object):
    @pytest.fixture()
    def bound_floating_ip(self, hetzner_client):
        return BoundFloatingIP(client=hetzner_client.floating_ips,
                               data=dict(id=4711))

    def test_get_actions(self, bound_floating_ip):
        actions = bound_floating_ip.get_actions()

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

    def test_update(self, bound_floating_ip):
        floating_ip = bound_floating_ip.update(description="New description",
                                               labels={},
                                               name="Web Frontend")
        assert floating_ip.id == 4711
        assert floating_ip.description == "New description"
        assert floating_ip.name == "Web Frontend"

    def test_delete(self, bound_floating_ip):
        delete_success = bound_floating_ip.delete()
        assert delete_success is True

    @pytest.mark.parametrize(
        "server", (Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))))
    def test_assign(self, bound_floating_ip, server):
        action = bound_floating_ip.assign(server)
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "assign_floating_ip"

    def test_unassign(self, bound_floating_ip):
        action = bound_floating_ip.unassign()
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "unassign_floating_ip"

    def test_change_dns_ptr(self, bound_floating_ip):
        action = bound_floating_ip.change_dns_ptr("1.2.3.4",
                                                  "server02.example.com")
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "change_dns_ptr"
Ejemplo n.º 10
0
    def test_remove_target(self, hetzner_client, response_remove_target,
                           bound_load_balancer):
        hetzner_client.request.return_value = response_remove_target
        target = LoadBalancerTarget(server=Server(id=100))
        action = bound_load_balancer.remove_target(target)
        hetzner_client.request.assert_called_with(
            json={
                'type': None,
                'server': {
                    "id": 100
                }
            },
            url="/load_balancers/14/actions/remove_target",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "remove_target"
Ejemplo n.º 11
0
    def test_add_target(self, hetzner_client, response_add_target,
                        bound_load_balancer):
        hetzner_client.request.return_value = response_add_target
        target = LoadBalancerTarget(server=Server(id=1), use_private_ip=True)
        action = bound_load_balancer.add_target(target)
        hetzner_client.request.assert_called_with(
            json={
                'type': None,
                'server': {
                    "id": 1
                },
                'use_private_ip': True
            },
            url="/load_balancers/14/actions/add_target",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "add_target"
Ejemplo n.º 12
0
    def test_remove_from_resources(self, hetzner_client, bound_firewall,
                                   response_set_rules):
        hetzner_client.request.return_value = response_set_rules
        actions = bound_firewall.remove_from_resources([
            FirewallResource(type=FirewallResource.TYPE_SERVER,
                             server=Server(id=5))
        ])
        hetzner_client.request.assert_called_with(
            url="/firewalls/1/actions/remove_from_resources",
            method="POST",
            json={"remove_from": [{
                "type": "server",
                "server": {
                    "id": 5
                }
            }]})

        assert actions[0].id == 13
        assert actions[0].progress == 100
Ejemplo n.º 13
0
    def test_apply_to_resources(self, firewalls_client, firewall,
                                response_set_rules):
        firewalls_client._client.request.return_value = response_set_rules

        actions = firewalls_client.apply_to_resources(firewall, [
            FirewallResource(type=FirewallResource.TYPE_SERVER,
                             server=Server(id=5))
        ])
        firewalls_client._client.request.assert_called_with(
            url="/firewalls/1/actions/apply_to_resources",
            method="POST",
            json={"apply_to": [{
                "type": "server",
                "server": {
                    "id": 5
                }
            }]})

        assert actions[0].id == 13
        assert actions[0].progress == 100
Ejemplo n.º 14
0
class TestBoundVolume(object):
    @pytest.fixture()
    def bound_volume(self, hetzner_client):
        return BoundVolume(client=hetzner_client.volumes, data=dict(id=4711))

    def test_get_actions(self, bound_volume):
        actions = bound_volume.get_actions()

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

    def test_update(self, bound_volume):
        volume = bound_volume.update(name="new-name", labels={})
        assert volume.id == 4711
        assert volume.name == "new-name"

    def test_delete(self, bound_volume):
        delete_success = bound_volume.delete()
        assert delete_success is True

    @pytest.mark.parametrize(
        "server", (Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))))
    def test_attach(self, hetzner_client, bound_volume, server):
        action = bound_volume.attach(server)
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "attach_volume"

    def test_detach(self, hetzner_client, bound_volume):
        action = bound_volume.detach()
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "detach_volume"

    def test_resize(self, hetzner_client, bound_volume):
        action = bound_volume.resize(50)
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "resize_volume"
Ejemplo n.º 15
0
class TestBoundLoadBalancer(object):
    @pytest.fixture()
    def bound_load_balancer(self, hetzner_client):
        return BoundLoadBalancer(client=hetzner_client.load_balancers,
                                 data=dict(id=14))

    def test_bound_load_balancer_init(self, response_load_balancer):
        bound_load_balancer = BoundLoadBalancer(
            client=mock.MagicMock(),
            data=response_load_balancer['load_balancer'])

        assert bound_load_balancer.id == 4711
        assert bound_load_balancer.name == 'Web Frontend'

    @pytest.mark.parametrize("params", [{"page": 1, "per_page": 10}, {}])
    def test_get_actions_list(self, hetzner_client, bound_load_balancer,
                              response_get_actions, params):
        hetzner_client.request.return_value = response_get_actions
        result = bound_load_balancer.get_actions_list(**params)
        hetzner_client.request.assert_called_with(
            url="/load_balancers/14/actions", method="GET", params=params)

        actions = result.actions
        assert result.meta is None

        assert len(actions) == 1
        assert isinstance(actions[0], BoundAction)
        assert actions[0].id == 13
        assert actions[0].command == "change_protection"

    @pytest.mark.parametrize("params", [{}])
    def test_get_actions(self, hetzner_client, bound_load_balancer,
                         response_get_actions, params):
        hetzner_client.request.return_value = response_get_actions
        actions = bound_load_balancer.get_actions(**params)

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

        hetzner_client.request.assert_called_with(
            url="/load_balancers/14/actions", method="GET", params=params)

        assert len(actions) == 1
        assert isinstance(actions[0], BoundAction)
        assert actions[0].id == 13
        assert actions[0].command == "change_protection"

    def test_update(self, hetzner_client, bound_load_balancer,
                    response_update_load_balancer):
        hetzner_client.request.return_value = response_update_load_balancer
        load_balancer = bound_load_balancer.update(name="new-name", labels={})
        hetzner_client.request.assert_called_with(url="/load_balancers/14",
                                                  method="PUT",
                                                  json={
                                                      "name": "new-name",
                                                      "labels": {}
                                                  })

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

    def test_delete(self, hetzner_client, generic_action, bound_load_balancer):
        hetzner_client.request.return_value = generic_action
        delete_success = bound_load_balancer.delete()
        hetzner_client.request.assert_called_with(url="/load_balancers/14",
                                                  method="DELETE")

        assert delete_success is True

    def test_add_service(self, hetzner_client, response_add_service,
                         bound_load_balancer):
        hetzner_client.request.return_value = response_add_service
        service = LoadBalancerService(listen_port=80, protocol="http")
        action = bound_load_balancer.add_service(service)
        hetzner_client.request.assert_called_with(
            json={
                'protocol': 'http',
                'listen_port': 80
            },
            url="/load_balancers/14/actions/add_service",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "add_service"

    def test_delete_service(self, hetzner_client, response_delete_service,
                            bound_load_balancer):
        hetzner_client.request.return_value = response_delete_service
        service = LoadBalancerService(listen_port=12)
        action = bound_load_balancer.delete_service(service)
        hetzner_client.request.assert_called_with(
            json={'listen_port': 12},
            url="/load_balancers/14/actions/delete_service",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "delete_service"

    @pytest.mark.parametrize(
        "target,params",
        [(LoadBalancerTarget(
            type="server", server=Server(id=1), use_private_ip=True), {
                'server': {
                    "id": 1
                }
            }),
         (LoadBalancerTarget(type="ip",
                             ip=LoadBalancerTargetIP(ip="127.0.0.1")), {
                                 'ip': {
                                     "ip": "127.0.0.1"
                                 }
                             }),
         (LoadBalancerTarget(type="label_selector",
                             label_selector=LoadBalancerTargetLabelSelector(
                                 selector="abc=def")), {
                                     'label_selector': {
                                         "selector": "abc=def"
                                     }
                                 })])
    def test_add_target(self, hetzner_client, response_add_target,
                        bound_load_balancer, target, params):
        hetzner_client.request.return_value = response_add_target
        action = bound_load_balancer.add_target(target)
        params.update({
            'type': target.type,
            'use_private_ip': target.use_private_ip
        })
        hetzner_client.request.assert_called_with(
            json=params,
            url="/load_balancers/14/actions/add_target",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "add_target"

    @pytest.mark.parametrize(
        "target,params",
        [(LoadBalancerTarget(
            type="server", server=Server(id=1), use_private_ip=True), {
                'server': {
                    "id": 1
                }
            }),
         (LoadBalancerTarget(type="ip",
                             ip=LoadBalancerTargetIP(ip="127.0.0.1")), {
                                 'ip': {
                                     "ip": "127.0.0.1"
                                 }
                             }),
         (LoadBalancerTarget(type="label_selector",
                             label_selector=LoadBalancerTargetLabelSelector(
                                 selector="abc=def")), {
                                     'label_selector': {
                                         "selector": "abc=def"
                                     }
                                 })])
    def test_remove_target(self, hetzner_client, response_remove_target,
                           bound_load_balancer, target, params):
        hetzner_client.request.return_value = response_remove_target
        action = bound_load_balancer.remove_target(target)
        params.update({'type': target.type})
        hetzner_client.request.assert_called_with(
            json=params,
            url="/load_balancers/14/actions/remove_target",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "remove_target"

    def test_update_service(self, hetzner_client, response_update_service,
                            bound_load_balancer):
        hetzner_client.request.return_value = response_update_service
        new_health_check = LoadBalancerHealthCheck(protocol='http',
                                                   port=13,
                                                   interval=1,
                                                   timeout=1,
                                                   retries=1)
        service = LoadBalancerService(listen_port=12,
                                      health_check=new_health_check)

        action = bound_load_balancer.update_service(service)
        hetzner_client.request.assert_called_with(
            json={
                'listen_port': 12,
                'health_check': {
                    'protocol': 'http',
                    'port': 13,
                    'interval': 1,
                    'timeout': 1,
                    'retries': 1
                }
            },
            url="/load_balancers/14/actions/update_service",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "update_service"

    def test_change_algorithm(self, hetzner_client, response_change_algorithm,
                              bound_load_balancer):
        hetzner_client.request.return_value = response_change_algorithm
        algorithm = LoadBalancerAlgorithm(type="round_robin")
        action = bound_load_balancer.change_algorithm(algorithm)
        hetzner_client.request.assert_called_with(
            json={'type': 'round_robin'},
            url="/load_balancers/14/actions/change_algorithm",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "change_algorithm"

    def test_change_protection(self, hetzner_client,
                               response_change_protection,
                               bound_load_balancer):
        hetzner_client.request.return_value = response_change_protection
        action = bound_load_balancer.change_protection(delete=True)
        hetzner_client.request.assert_called_with(
            json={'delete': True},
            url="/load_balancers/14/actions/change_protection",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "change_protection"

    def test_enable_public_interface(self, response_enable_public_interface,
                                     hetzner_client, bound_load_balancer):
        hetzner_client.request.return_value = response_enable_public_interface
        action = bound_load_balancer.enable_public_interface()
        hetzner_client.request.assert_called_with(
            url="/load_balancers/14/actions/enable_public_interface",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "enable_public_interface"

    def test_disable_public_interface(self, response_disable_public_interface,
                                      hetzner_client, bound_load_balancer):
        hetzner_client.request.return_value = response_disable_public_interface
        action = bound_load_balancer.disable_public_interface()
        hetzner_client.request.assert_called_with(
            url="/load_balancers/14/actions/disable_public_interface",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "disable_public_interface"

    def test_attach_to_network(self, response_attach_load_balancer_to_network,
                               hetzner_client, bound_load_balancer):
        hetzner_client.request.return_value = response_attach_load_balancer_to_network
        action = bound_load_balancer.attach_to_network(Network(id=1))
        hetzner_client.request.assert_called_with(
            json={"network": 1},
            url="/load_balancers/14/actions/attach_to_network",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "attach_to_network"

    def test_detach_from_network(self, response_detach_from_network,
                                 hetzner_client, bound_load_balancer):
        hetzner_client.request.return_value = response_detach_from_network
        action = bound_load_balancer.detach_from_network(Network(id=1))
        hetzner_client.request.assert_called_with(
            json={"network": 1},
            url="/load_balancers/14/actions/detach_from_network",
            method="POST")

        assert action.id == 13
        assert action.progress == 100
        assert action.command == "detach_from_network"

    def test_change_type(self, hetzner_client, bound_load_balancer,
                         generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_load_balancer.change_type(LoadBalancerType(name="lb21"))
        hetzner_client.request.assert_called_with(
            url="/load_balancers/14/actions/change_type",
            method="POST",
            json={"load_balancer_type": "lb21"})

        assert action.id == 1
        assert action.progress == 0
Ejemplo n.º 16
0
class TestServersClient(object):
    @pytest.fixture()
    def servers_client(self):
        return ServersClient(client=mock.MagicMock())

    def test_get_by_id(self, servers_client, response_simple_server):
        servers_client._client.request.return_value = response_simple_server
        bound_server = servers_client.get_by_id(1)
        servers_client._client.request.assert_called_with(url="/servers/1",
                                                          method="GET")
        assert bound_server._client is servers_client
        assert bound_server.id == 1
        assert bound_server.name == "my-server"

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

        bound_servers = result.servers
        assert result.meta is None

        assert len(bound_servers) == 2

        bound_server1 = bound_servers[0]
        bound_server2 = bound_servers[1]

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

        assert bound_server2._client is servers_client
        assert bound_server2.id == 2
        assert bound_server2.name == "my-server2"

    @pytest.mark.parametrize("params", [{
        'name': "server1",
        'label_selector': "label1"
    }, {}])
    def test_get_all(self, servers_client, response_simple_servers, params):
        servers_client._client.request.return_value = response_simple_servers
        bound_servers = servers_client.get_all(**params)

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

        servers_client._client.request.assert_called_with(url="/servers",
                                                          method="GET",
                                                          params=params)

        assert len(bound_servers) == 2

        bound_server1 = bound_servers[0]
        bound_server2 = bound_servers[1]

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

        assert bound_server2._client is servers_client
        assert bound_server2.id == 2
        assert bound_server2.name == "my-server2"

    def test_get_by_name(self, servers_client, response_simple_servers):
        servers_client._client.request.return_value = response_simple_servers
        bound_server = servers_client.get_by_name("my-server")

        params = {'name': "my-server"}

        servers_client._client.request.assert_called_with(url="/servers",
                                                          method="GET",
                                                          params=params)

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

    def test_create_with_datacenter(self, servers_client,
                                    response_create_simple_server):
        servers_client._client.request.return_value = response_create_simple_server
        response = servers_client.create("my-server",
                                         server_type=ServerType(name="cx11"),
                                         image=Image(id=4711),
                                         datacenter=Datacenter(id=1))
        servers_client._client.request.assert_called_with(
            url="/servers",
            method="POST",
            json={
                'name': "my-server",
                'server_type': "cx11",
                'image': 4711,
                'datacenter': 1,
                "start_after_create": True
            })

        bound_server = response.server

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

    def test_create_with_location(self, servers_client,
                                  response_create_simple_server):
        servers_client._client.request.return_value = response_create_simple_server
        response = servers_client.create("my-server",
                                         server_type=ServerType(name="cx11"),
                                         image=Image(name="ubuntu-20.04"),
                                         location=Location(name="fsn1"))
        servers_client._client.request.assert_called_with(
            url="/servers",
            method="POST",
            json={
                'name': "my-server",
                'server_type': "cx11",
                'image': "ubuntu-20.04",
                'location': "fsn1",
                "start_after_create": True
            })

        bound_server = response.server
        bound_action = response.action

        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"

    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 test_create_with_networks(self, servers_client,
                                  response_create_simple_server):
        servers_client._client.request.return_value = response_create_simple_server
        networks = [Network(id=1), BoundNetwork(mock.MagicMock(), dict(id=2))]
        response = servers_client.create("my-server",
                                         server_type=ServerType(name="cx11"),
                                         image=Image(id=4711),
                                         networks=networks,
                                         start_after_create=False)
        servers_client._client.request.assert_called_with(
            url="/servers",
            method="POST",
            json={
                'name': "my-server",
                'server_type': "cx11",
                'image': 4711,
                'networks': [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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_get_actions_list(self, servers_client, server,
                              response_get_actions):
        servers_client._client.request.return_value = response_get_actions
        result = servers_client.get_actions_list(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions", method="GET", params={})

        actions = result.actions
        assert result.meta is None

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

        assert actions[0]._client == servers_client._client.actions
        assert actions[0].id == 13
        assert actions[0].command == "start_server"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_update(self, servers_client, server, response_update_server):
        servers_client._client.request.return_value = response_update_server
        server = servers_client.update(server, name="new-name", labels={})
        servers_client._client.request.assert_called_with(url="/servers/1",
                                                          method="PUT",
                                                          json={
                                                              "name":
                                                              "new-name",
                                                              "labels": {}
                                                          })

        assert server.id == 14
        assert server.name == "new-name"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_delete(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.delete(server)
        servers_client._client.request.assert_called_with(url="/servers/1",
                                                          method="DELETE")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_power_off(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.power_off(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/poweroff", method="POST")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_power_on(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.power_on(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/poweron", method="POST")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_reboot(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.reboot(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/reboot", method="POST")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_reset(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.reset(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/reset", method="POST")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_shutdown(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.shutdown(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/shutdown", method="POST")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_reset_password(self, servers_client, server,
                            response_server_reset_password):
        servers_client._client.request.return_value = response_server_reset_password
        response = servers_client.reset_password(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/reset_password", method="POST")

        assert response.action.id == 1
        assert response.action.progress == 0
        assert response.root_password == "YItygq1v3GYjjMomLaKc"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_change_type_with_server_type_name(self, servers_client, server,
                                               generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.change_type(server,
                                            ServerType(name="cx11"),
                                            upgrade_disk=True)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/change_type",
            method="POST",
            json={
                "server_type": "cx11",
                "upgrade_disk": True
            })

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_change_type_with_server_type_id(self, servers_client, server,
                                             generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.change_type(server,
                                            ServerType(id=1),
                                            upgrade_disk=True)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/change_type",
            method="POST",
            json={
                "server_type": 1,
                "upgrade_disk": True
            })

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_change_type_with_blank_server_type(self, servers_client, server):
        with pytest.raises(ValueError) as e:
            servers_client.change_type(server, ServerType(), upgrade_disk=True)
        assert str(e.value) == "id or name must be set"
        servers_client._client.request.assert_not_called()

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_enable_rescue(self, servers_client, server,
                           response_server_enable_rescue):
        servers_client._client.request.return_value = response_server_enable_rescue
        response = servers_client.enable_rescue(server, "linux64", [2323])
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/enable_rescue",
            method="POST",
            json={
                "type": "linux64",
                "ssh_keys": [2323]
            })

        assert response.action.id == 1
        assert response.action.progress == 0
        assert response.root_password == "YItygq1v3GYjjMomLaKc"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_disable_rescue(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.disable_rescue(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/disable_rescue", method="POST")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_create_image(self, servers_client, server,
                          response_server_create_image):
        servers_client._client.request.return_value = response_server_create_image
        response = servers_client.create_image(server,
                                               description="my image",
                                               type="snapshot",
                                               labels={"key": "value"})
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/create_image",
            method="POST",
            json={
                "description": "my image",
                "type": "snapshot",
                "labels": {
                    "key": "value"
                }
            })

        assert response.action.id == 1
        assert response.action.progress == 0
        assert response.image.description == "my image"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_rebuild(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.rebuild(server, Image(name="ubuntu-20.04"))
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/rebuild",
            method="POST",
            json={"image": "ubuntu-20.04"})

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_enable_backup(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.enable_backup(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/enable_backup", method="POST")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_disable_backup(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.disable_backup(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/disable_backup", method="POST")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_attach_iso(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.attach_iso(
            server, Iso(name="FreeBSD-11.0-RELEASE-amd64-dvd1"))
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/attach_iso",
            method="POST",
            json={"iso": "FreeBSD-11.0-RELEASE-amd64-dvd1"})

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_detach_iso(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.detach_iso(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/detach_iso", method="POST")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_change_dns_ptr(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.change_dns_ptr(server, "1.2.3.4",
                                               "example.com")
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/change_dns_ptr",
            method="POST",
            json={
                "ip": "1.2.3.4",
                "dns_ptr": "example.com"
            })

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_change_protection(self, servers_client, server, generic_action):
        servers_client._client.request.return_value = generic_action
        action = servers_client.change_protection(server, True, True)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/change_protection",
            method="POST",
            json={
                "delete": True,
                "rebuild": True
            })

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_request_console(self, servers_client, server,
                             response_server_request_console):
        servers_client._client.request.return_value = response_server_request_console
        response = servers_client.request_console(server)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/request_console", method="POST")

        assert response.action.id == 1
        assert response.action.progress == 0
        assert response.wss_url == "wss://console.hetzner.cloud/?server_id=1&token=3db32d15-af2f-459c-8bf8-dee1fd05f49c"
        assert response.password == "9MQaTg2VAGI0FIpc10k3UpRXcHj2wQ6x"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    @pytest.mark.parametrize(
        "network",
        [Network(id=4711),
         BoundNetwork(mock.MagicMock(), dict(id=4711))])
    def test_attach_to_network(self, servers_client, server, network,
                               response_attach_to_network):
        servers_client._client.request.return_value = response_attach_to_network
        action = servers_client.attach_to_network(server, network, "10.0.1.1",
                                                  ["10.0.1.2", "10.0.1.3"])
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/attach_to_network",
            method="POST",
            json={
                "network": 4711,
                "ip": "10.0.1.1",
                "alias_ips": ["10.0.1.2", "10.0.1.3"]
            })

        assert action.id == 1
        assert action.progress == 0
        assert action.command == "attach_to_network"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    @pytest.mark.parametrize(
        "network",
        [Network(id=4711),
         BoundNetwork(mock.MagicMock(), dict(id=4711))])
    def test_detach_from_network(self, servers_client, server, network,
                                 response_detach_from_network):
        servers_client._client.request.return_value = response_detach_from_network
        action = servers_client.detach_from_network(server, network)
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/detach_from_network",
            method="POST",
            json={"network": 4711})

        assert action.id == 1
        assert action.progress == 0
        assert action.command == "detach_from_network"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    @pytest.mark.parametrize(
        "network",
        [Network(id=4711),
         BoundNetwork(mock.MagicMock(), dict(id=4711))])
    def test_change_alias_ips(self, servers_client, server, network,
                              response_change_alias_ips):
        servers_client._client.request.return_value = response_change_alias_ips
        action = servers_client.change_alias_ips(server, network,
                                                 ["10.0.1.2", "10.0.1.3"])
        servers_client._client.request.assert_called_with(
            url="/servers/1/actions/change_alias_ips",
            method="POST",
            json={
                "network": 4711,
                "alias_ips": ["10.0.1.2", "10.0.1.3"]
            })

        assert action.id == 1
        assert action.progress == 0
        assert action.command == "change_alias_ips"
Ejemplo n.º 17
0
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
Ejemplo n.º 18
0
class TestBoundVolume(object):

    @pytest.fixture()
    def bound_volume(self, hetzner_client):
        return BoundVolume(client=hetzner_client.volumes, data=dict(id=14))

    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

    def test_get_actions(self, hetzner_client, bound_volume, response_get_actions):
        hetzner_client.request.return_value = response_get_actions
        actions = bound_volume.get_actions(sort="id")
        hetzner_client.request.assert_called_with(url="/volumes/14/actions", method="GET", params={"page": 1, "per_page": 50, "sort": "id"})

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

    def test_update(self, hetzner_client, bound_volume, response_update_volume):
        hetzner_client.request.return_value = response_update_volume
        volume = bound_volume.update(name="new-name")
        hetzner_client.request.assert_called_with(url="/volumes/14", method="PUT", json={"name": "new-name"})

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

    def test_delete(self, hetzner_client, bound_volume, generic_action):
        hetzner_client.request.return_value = generic_action
        delete_success = bound_volume.delete()
        hetzner_client.request.assert_called_with(url="/volumes/14", method="DELETE")

        assert delete_success is True

    def test_change_protection(self, hetzner_client, bound_volume, generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_volume.change_protection(True)
        hetzner_client.request.assert_called_with(url="/volumes/14/actions/change_protection", method="POST", json={"delete": True})

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

    @pytest.mark.parametrize("server",
                             (Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))))
    def test_attach(self, hetzner_client, bound_volume, server, generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_volume.attach(server)
        hetzner_client.request.assert_called_with(
            url="/volumes/14/actions/attach",
            method="POST",
            json={"server": 1}
        )
        assert action.id == 1
        assert action.progress == 0

    @pytest.mark.parametrize("server",
                             (Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))))
    def test_attach_with_automount(self, hetzner_client, bound_volume, server, generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_volume.attach(server, False)
        hetzner_client.request.assert_called_with(
            url="/volumes/14/actions/attach",
            method="POST",
            json={"server": 1, "automount": False}
        )
        assert action.id == 1
        assert action.progress == 0

    def test_detach(self, hetzner_client, bound_volume, generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_volume.detach()
        hetzner_client.request.assert_called_with(
            url="/volumes/14/actions/detach",
            method="POST"
        )
        assert action.id == 1
        assert action.progress == 0

    def test_resize(self, hetzner_client, bound_volume, generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_volume.resize(50)
        hetzner_client.request.assert_called_with(
            url="/volumes/14/actions/resize",
            method="POST",
            json={"size": 50}
        )
        assert action.id == 1
        assert action.progress == 0
Ejemplo n.º 19
0
                                  name="Volume2",
                                  location=server2.location)

volume1 = response1.volume
volume2 = response2.volume

# Attach volume to server

client.volumes.attach(server1, volume1)
client.volumes.attach(server2, volume2)

# Detach second volume

client.volumes.detach(volume2)

# Poweroff 2nd server
client.servers.power_off(server2)

# Create one more volume and attach it to server with id=33

server33 = Server(id=33)
response = client.volumes.create(size=33, name="Volume33", server=server33)

print(response.action.status)

# Create one more server and attach 2 volumes to it
client.servers.create("Server3",
                      server_type=ServerType(name="cx11"),
                      image=Image(id=4711),
                      volumes=[Volume(id=221), Volume(id=222)])
Ejemplo n.º 20
0
class TestServersClient(object):
    def test_get_by_id(self, hetzner_client):
        server = hetzner_client.servers.get_by_id(42)
        assert server.id == 42
        assert server.volumes == []
        assert server.server_type.id == 1
        assert server.datacenter.id == 1
        assert server.image.id == 4711

    def test_get_by_name(self, hetzner_client):
        server = hetzner_client.servers.get_by_name("my-server")
        assert server.id == 42
        assert server.name == "my-server"
        assert server.volumes == []
        assert server.server_type.id == 1
        assert server.datacenter.id == 1
        assert server.image.id == 4711

    def test_get_list(self, hetzner_client):
        result = hetzner_client.servers.get_list()
        servers = result.servers
        assert servers[0].id == 42
        assert servers[0].volumes == []
        assert servers[0].server_type.id == 1
        assert servers[0].datacenter.id == 1
        assert servers[0].image.id == 4711

    def test_create(self, hetzner_client):
        response = hetzner_client.servers.create(
            "my-server",
            server_type=ServerType(name="cx11"),
            image=Image(name="ubuntu-16.04"),
            ssh_keys=[SSHKey(name="my-ssh-key")],
            volumes=[Volume(id=1)],
            user_data=
            "#cloud-config\\nruncmd:\\n- [touch, /root/cloud-init-worked]\\n",
            location=Location(name="nbg1"),
            automount=False)
        server = response.server
        action = response.action
        next_actions = response.next_actions
        root_password = response.root_password

        assert server.id == 42
        assert server.volumes == []
        assert server.server_type.id == 1
        assert server.datacenter.id == 1
        assert server.image.id == 4711

        assert action.id == 1
        assert action.command == "create_server"

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

        assert root_password == "YItygq1v3GYjjMomLaKc"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_get_actions_list(self, hetzner_client, server):
        result = hetzner_client.servers.get_actions_list(server)
        actions = result.actions

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_update(self, hetzner_client, server):
        server = hetzner_client.servers.update(server,
                                               name="new-name",
                                               labels={})

        assert server.id == 42
        assert server.name == "new-name"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_delete(self, hetzner_client, server):
        action = hetzner_client.servers.delete(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_power_off(self, hetzner_client, server):
        action = hetzner_client.servers.power_off(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_power_on(self, hetzner_client, server):
        action = hetzner_client.servers.power_on(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_reboot(self, hetzner_client, server):
        action = hetzner_client.servers.reboot(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_reset(self, hetzner_client, server):
        action = hetzner_client.servers.reset(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_shutdown(self, hetzner_client, server):
        action = hetzner_client.servers.shutdown(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_reset_password(self, hetzner_client, server):
        response = hetzner_client.servers.reset_password(server)

        assert response.action.id == 13
        assert response.action.command == "reset_password"
        assert response.root_password == "zCWbFhnu950dUTko5f40"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_change_type(self, hetzner_client, server):
        action = hetzner_client.servers.change_type(server,
                                                    ServerType(name="cx11"),
                                                    upgrade_disk=True)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_enable_rescue(self, hetzner_client, server):
        response = hetzner_client.servers.enable_rescue(server,
                                                        type="linux64",
                                                        ssh_keys=[2323])

        assert response.action.id == 13
        assert response.action.command == "enable_rescue"
        assert response.root_password == "zCWbFhnu950dUTko5f40"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_disable_rescue(self, hetzner_client, server):
        action = hetzner_client.servers.disable_rescue(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_create_image(self, hetzner_client, server):
        response = hetzner_client.servers.create_image(server,
                                                       description="my image",
                                                       type="snapshot")

        assert response.action.id == 13
        assert response.action.command == "create_image"
        assert response.image.description == "my image"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_rebuild(self, hetzner_client, server):
        action = hetzner_client.servers.rebuild(server,
                                                Image(name="ubuntu-16.04"))

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_enable_backup(self, hetzner_client, server):
        action = hetzner_client.servers.enable_backup(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_disable_backup(self, hetzner_client, server):
        action = hetzner_client.servers.disable_backup(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_attach_iso(self, hetzner_client, server):
        action = hetzner_client.servers.attach_iso(
            server, Iso(name="FreeBSD-11.0-RELEASE-amd64-dvd1"))

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_detach_iso(self, hetzner_client, server):
        action = hetzner_client.servers.detach_iso(server)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_change_dns_ptr(self, hetzner_client, server):
        action = hetzner_client.servers.change_dns_ptr(server, "1.2.3.4",
                                                       "example.com")

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_change_protection(self, hetzner_client, server):
        action = hetzner_client.servers.change_protection(server,
                                                          delete=True,
                                                          rebuild=True)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_request_console(self, hetzner_client, server):
        response = hetzner_client.servers.request_console(server)

        assert response.action.id == 13
        assert response.action.command == "request_console"
        assert response.wss_url == "wss://console.hetzner.cloud/?server_id=1&token=3db32d15-af2f-459c-8bf8-dee1fd05f49c"
        assert response.password == "9MQaTg2VAGI0FIpc10k3UpRXcHj2wQ6x"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    @pytest.mark.parametrize(
        "network",
        [Network(id=4711),
         BoundNetwork(mock.MagicMock(), dict(id=4711))])
    def test_attach_to_network(self, hetzner_client, server, network):
        action = hetzner_client.servers.attach_to_network(
            server, network, ip="10.0.1.1", alias_ips=["10.0.1.2"])

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    @pytest.mark.parametrize(
        "network",
        [Network(id=4711),
         BoundNetwork(mock.MagicMock(), dict(id=4711))])
    def test_detach_from_network(self, hetzner_client, server, network):
        action = hetzner_client.servers.detach_from_network(server, network)

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

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    @pytest.mark.parametrize(
        "network",
        [Network(id=4711),
         BoundNetwork(mock.MagicMock(), dict(id=4711))])
    def test_change_alias_ips(self, hetzner_client, server, network):
        action = hetzner_client.servers.change_alias_ips(
            server, network, alias_ips=["10.0.1.2"])

        assert action.id == 13
        assert action.command == "change_alias_ips"
Ejemplo n.º 21
0
class TestFloatingIPsClient(object):
    def test_get_by_id(self, hetzner_client):
        bound_floating_ip = hetzner_client.floating_ips.get_by_id(4711)
        assert bound_floating_ip.id == 4711
        assert bound_floating_ip.description == "Web Frontend"
        assert bound_floating_ip.type == "ipv4"

    def test_get_list(self, hetzner_client):
        result = hetzner_client.floating_ips.get_list()
        bound_floating_ips = result.floating_ips
        assert bound_floating_ips[0].id == 4711
        assert bound_floating_ips[0].description == "Web Frontend"
        assert bound_floating_ips[0].type == "ipv4"

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_create(self, hetzner_client, server):
        response = hetzner_client.floating_ips.create(
            type="ipv4",
            description="Web Frontend",
            # home_location=Location(description="fsn1"),
            server=server)

        floating_ip = response.floating_ip
        action = response.action

        assert floating_ip.id == 4711
        assert floating_ip.description == "Web Frontend"
        assert floating_ip.type == "ipv4"

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

    @pytest.mark.parametrize(
        "floating_ip",
        [FloatingIP(id=1),
         BoundFloatingIP(mock.MagicMock(), dict(id=1))])
    def test_get_actions(self, hetzner_client, floating_ip):
        actions = hetzner_client.floating_ips.get_actions(floating_ip)

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

    @pytest.mark.parametrize(
        "floating_ip",
        [FloatingIP(id=1),
         BoundFloatingIP(mock.MagicMock(), dict(id=1))])
    def test_update(self, hetzner_client, floating_ip):
        floating_ip = hetzner_client.floating_ips.update(
            floating_ip, description="New description", labels={})

        assert floating_ip.id == 4711
        assert floating_ip.description == "New description"

    @pytest.mark.parametrize(
        "floating_ip",
        [FloatingIP(id=1),
         BoundFloatingIP(mock.MagicMock(), dict(id=1))])
    def test_delete(self, hetzner_client, floating_ip):
        delete_success = hetzner_client.floating_ips.delete(floating_ip)

        assert delete_success is True

    @pytest.mark.parametrize(
        "server,floating_ip",
        [(Server(id=43), FloatingIP(id=4711)),
         (BoundServer(mock.MagicMock(), dict(id=43)),
          BoundFloatingIP(mock.MagicMock(), dict(id=4711)))])
    def test_assign(self, hetzner_client, server, floating_ip):
        action = hetzner_client.floating_ips.assign(floating_ip, server)
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "assign_floating_ip"

    @pytest.mark.parametrize("floating_ip", [
        FloatingIP(id=4711),
        BoundFloatingIP(mock.MagicMock(), dict(id=4711))
    ])
    def test_unassign(self, hetzner_client, floating_ip):
        action = hetzner_client.floating_ips.unassign(floating_ip)
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "unassign_floating_ip"

    @pytest.mark.parametrize("floating_ip", [
        FloatingIP(id=4711),
        BoundFloatingIP(mock.MagicMock(), dict(id=4711))
    ])
    def test_change_dns_ptr(self, hetzner_client, floating_ip):
        action = hetzner_client.floating_ips.change_dns_ptr(
            floating_ip, "1.2.3.4", "server02.example.com")
        assert action.id == 13
        assert action.progress == 0
        assert action.command == "change_dns_ptr"
client = Client(token="......")
serverName = "test" 


oldServer = client.servers.get_by_name(name=serverName)

#Shut down the OldServer
try:
    oldServer.power_off()
except:
    print("Error when turning down the server. Confirm if the server's name is right")
    quit()


#Get all the information of the old server
oldServerType = (Server(id=oldServer).id).server_type
oldServerDatacenter = (Server(id=oldServer).id).datacenter
oldServerVolumes = (Server(id=oldServer).id).volumes
oldServerFloatingIP = ((Server(id=oldServer).id).public_net).floating_ips

sshKeys = client.ssh_keys.get_all()

#Find the newest backup
images = client.images.get_all(type="backup")
backupNewestID = 0
for image in images:
    infImage = (client.images.get_by_id(image.id))
    createdFrom = ((Image(id=infImage).id).created_from).name
    if createdFrom == serverName:
        idImage = infImage.id
        if idImage > backupNewestID:
Ejemplo n.º 23
0
    def _handle_changed_volumes(self, defn: HetznerCloudDefinition,
                                allow_recreate: bool) -> None:
        """
        Detects and corrects any volume state desynchronisation.
        """

        attached: Set[str] = {x.name for x in self.get_instance().volumes}

        for name in self.volumes.keys():
            volume: Optional[BoundVolume] = self.get_client(
            ).volumes.get_by_name(name)

            # Detect destroyed volumes.
            if volume is None:
                if name not in defn.volumes:  # we dont need it
                    self.logger.warn(
                        f"forgetting about volume ‘{name}’ that no longer exists"
                        " and is no longer needed by the deployment specification"
                    )
                else:
                    if name.startswith("nixops-" + self.depl.uuid):
                        raise Exception(
                            f"volume ‘{name}’ (used by {self.full_name}) no longer exists;"
                            " run ‘nixops deploy --check’ to update resource state"
                        )
                    else:
                        raise Exception(
                            f"volume ‘{name}’ (used by {self.full_name}) was"
                            " manually destroyed")
            # Detect detached volumes.
            elif name not in attached:
                if name not in defn.volumes:  # we dont need it
                    self.logger.warn(
                        f"forgetting about detached volume ‘{name}’ [{volume.id}]"
                        " that is no longer needed by the deployment specification"
                    )
                else:  # we do need it
                    self.logger.warn(
                        f"volume ‘{name}’ [{volume.id}] was manually detached;"
                        " will reattach it")
                self._update_attr("volumes", name, None)
            # Detach existing attached volumes if required.
            elif name not in defn.volumes:
                self.logger.warn(
                    f"detaching volume ‘{name}’ [{volume.id}] that is no longer"
                    " needed by the deployment specification")
                volume.detach().wait_until_finished()
                self._update_attr("volumes", name, None)

        # Attach missing volumes. resize filesystems if required, before mounting.
        for name, v in defn.volumes.items():
            if name not in self.volumes:

                # Check if it exists. resources will have been created if user ran check,
                # but prexisting vols which got deleted may be gone (detected in code above)
                volume = self.get_client().volumes.get_by_name(name)

                if volume is None:
                    self.logger.warn(
                        f"tried to attach non-NixOps managed volume ‘{name}’,"
                        " but it doesn't exist... skipping")
                    continue
                elif volume.location.name != self.location:
                    raise Exception(
                        f"volume ‘{name}’ [{volume.id}] is in a different location"
                        " to {self.full_name}; attempting to attach it will fail."
                    )
                elif (volume.server and volume.server.id != self.vm_id
                      and self.depl.logger.confirm(
                          f"volume ‘{name}’ is in use by instance ‘{volume.server.id}’,"
                          " are you sure you want to attach this volume?")
                      ):  # noqa: E124
                    self.logger.log(
                        f"detaching volume ‘{name}’ from instance ‘{volume.server.id}’..."
                    )
                    volume.detach().wait_until_finished()
                    volume.server = None

                # Attach volume.

                self.logger.log(f"attaching volume ‘{name}’ [{volume.id}]... ")
                volume.attach(Server(self.vm_id)).wait_until_finished()

                # Wait until the device is visible in the instance.

                v["device"] = self.get_udev_name(volume.id)

                def check_device() -> bool:
                    return 0 == self.run_command(f"test -e {v['device']}",
                                                 check=False)

                if not check_wait(
                        check_device, initial=1, max_tries=10,
                        exception=False):
                    # If stopping times out, then do an unclean shutdown.
                    self.logger.log_end("(timed out)")
                    self.logger.log(f"can't find device ‘{v['device']}’...")
                    self.logger.log("available devices:")
                    self.run_command("lsblk")
                    raise Exception("operation timed out")
                else:
                    self._update_attr("volumes", name, v)
                    self.logger.log_end("")

            # Grow filesystems on resource based volumes.

            # We want to grow the fs when its volume gets resized, but if the
            # volume isn't attached to any server at the time, thats not possible.
            # Blindly trying to grow all volumes when mounting them just in case
            # they got resized while they were orphaned is bad. Workaround:
            # the needsFSResize attribute of VolumeState is set when the volume
            # gets resized by NixOps. When attaching a volume NixOps will use this
            # flag to decide whether to grow the filesystem.

            if name.startswith("nixops-" + self.depl.uuid):
                res = self.depl.get_typed_resource(name[44:],
                                                   "hetznercloud-volume",
                                                   VolumeState)
                # get correct option definitions for volume resources
                v["size"] = res._state["size"]
                v["fsType"] = res._state["fsType"]
                v["device"] = self.get_udev_name(res._state["resourceId"])

                question = (
                    f"volume {name} was resized, do you wish to grow its"
                    " filesystem to fill the space?")
                op = (f"umount {v['device']} ;"
                      f"e2fsck -fy {v['device']} &&"
                      f"resize2fs {v['device']}")

                if (v["fsType"] == "ext4" and res.needsFSResize
                        and self.depl.logger.confirm(question)
                        and self.run_command(op, check=False) == 0):
                    with res.depl._db:
                        res.needsFSResize = False

                self._update_attr("volumes", name, v)

            if v["mountPoint"]:
                volume = self.get_client().volumes.get_by_name(name)
                v["device"] = self.get_udev_name(volume.id)
                self._update_attr("volumes", name, v)
Ejemplo n.º 24
0
 def start(self) -> None:
     self.logger.log_start(f"powering on {self.full_name}...")
     self.wait_on_action(self.get_client().servers.power_on(
         Server(self.vm_id)))
     self.wait_for_ssh()
     self.state = self.UP
Ejemplo n.º 25
0
class TestBoundFloatingIP(object):
    @pytest.fixture()
    def bound_floating_ip(self, hetzner_client):
        return BoundFloatingIP(client=hetzner_client.floating_ips,
                               data=dict(id=14))

    def test_bound_floating_ip_init(self, floating_ip_response):
        bound_floating_ip = BoundFloatingIP(
            client=mock.MagicMock(), data=floating_ip_response['floating_ip'])

        assert bound_floating_ip.id == 4711
        assert bound_floating_ip.description == "Web Frontend"
        assert bound_floating_ip.name == "Web Frontend"
        assert bound_floating_ip.ip == "131.232.99.1"
        assert bound_floating_ip.type == "ipv4"
        assert bound_floating_ip.protection == {"delete": False}
        assert bound_floating_ip.labels == {}
        assert bound_floating_ip.blocked is False

        assert isinstance(bound_floating_ip.server, BoundServer)
        assert bound_floating_ip.server.id == 42

        assert isinstance(bound_floating_ip.home_location, BoundLocation)
        assert bound_floating_ip.home_location.id == 1
        assert bound_floating_ip.home_location.name == "fsn1"
        assert bound_floating_ip.home_location.description == "Falkenstein DC Park 1"
        assert bound_floating_ip.home_location.country == "DE"
        assert bound_floating_ip.home_location.city == "Falkenstein"
        assert bound_floating_ip.home_location.latitude == 50.47612
        assert bound_floating_ip.home_location.longitude == 12.370071

    def test_get_actions(self, hetzner_client, bound_floating_ip,
                         response_get_actions):
        hetzner_client.request.return_value = response_get_actions
        actions = bound_floating_ip.get_actions(sort="id")
        hetzner_client.request.assert_called_with(
            url="/floating_ips/14/actions",
            method="GET",
            params={
                "sort": "id",
                'page': 1,
                'per_page': 50
            })

        assert len(actions) == 1
        assert isinstance(actions[0], BoundAction)
        assert actions[0].id == 13
        assert actions[0].command == "assign_floating_ip"

    def test_update(self, hetzner_client, bound_floating_ip,
                    response_update_floating_ip):
        hetzner_client.request.return_value = response_update_floating_ip
        floating_ip = bound_floating_ip.update(description="New description",
                                               name="New name")
        hetzner_client.request.assert_called_with(url="/floating_ips/14",
                                                  method="PUT",
                                                  json={
                                                      "description":
                                                      "New description",
                                                      "name": "New name"
                                                  })

        assert floating_ip.id == 4711
        assert floating_ip.description == "New description"
        assert floating_ip.name == "New name"

    def test_delete(self, hetzner_client, bound_floating_ip, generic_action):
        hetzner_client.request.return_value = generic_action
        delete_success = bound_floating_ip.delete()
        hetzner_client.request.assert_called_with(url="/floating_ips/14",
                                                  method="DELETE")

        assert delete_success is True

    def test_change_protection(self, hetzner_client, bound_floating_ip,
                               generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_floating_ip.change_protection(True)
        hetzner_client.request.assert_called_with(
            url="/floating_ips/14/actions/change_protection",
            method="POST",
            json={"delete": True})

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

    @pytest.mark.parametrize(
        "server", (Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))))
    def test_assign(self, hetzner_client, bound_floating_ip, server,
                    generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_floating_ip.assign(server)
        hetzner_client.request.assert_called_with(
            url="/floating_ips/14/actions/assign",
            method="POST",
            json={"server": 1})
        assert action.id == 1
        assert action.progress == 0

    def test_unassign(self, hetzner_client, bound_floating_ip, generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_floating_ip.unassign()
        hetzner_client.request.assert_called_with(
            url="/floating_ips/14/actions/unassign", method="POST")
        assert action.id == 1
        assert action.progress == 0

    def test_change_dns_ptr(self, hetzner_client, bound_floating_ip,
                            generic_action):
        hetzner_client.request.return_value = generic_action
        action = bound_floating_ip.change_dns_ptr("1.2.3.4",
                                                  "server02.example.com")
        hetzner_client.request.assert_called_with(
            url="/floating_ips/14/actions/change_dns_ptr",
            method="POST",
            json={
                "ip": "1.2.3.4",
                "dns_ptr": "server02.example.com"
            })
        assert action.id == 1
        assert action.progress == 0
Ejemplo n.º 26
0
class TestFloatingIPsClient(object):
    @pytest.fixture()
    def floating_ips_client(self):
        return FloatingIPsClient(client=mock.MagicMock())

    def test_get_by_id(self, floating_ips_client, floating_ip_response):
        floating_ips_client._client.request.return_value = floating_ip_response
        bound_floating_ip = floating_ips_client.get_by_id(1)
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips/1", method="GET")
        assert bound_floating_ip._client is floating_ips_client
        assert bound_floating_ip.id == 4711
        assert bound_floating_ip.description == "Web Frontend"

    def test_get_by_name(self, floating_ips_client, one_floating_ips_response):
        floating_ips_client._client.request.return_value = one_floating_ips_response
        bound_floating_ip = floating_ips_client.get_by_name("Web Frontend")
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips", method="GET", params={"name": "Web Frontend"})
        assert bound_floating_ip._client is floating_ips_client
        assert bound_floating_ip.id == 4711
        assert bound_floating_ip.name == "Web Frontend"
        assert bound_floating_ip.description == "Web Frontend"

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

        bound_floating_ips = result.floating_ips
        assert result.meta is None

        assert len(bound_floating_ips) == 2

        bound_floating_ip1 = bound_floating_ips[0]
        bound_floating_ip2 = bound_floating_ips[1]

        assert bound_floating_ip1._client is floating_ips_client
        assert bound_floating_ip1.id == 4711
        assert bound_floating_ip1.description == "Web Frontend"

        assert bound_floating_ip2._client is floating_ips_client
        assert bound_floating_ip2.id == 4712
        assert bound_floating_ip2.description == "Web Backend"

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

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

        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips", method="GET", params=params)

        assert len(bound_floating_ips) == 2

        bound_floating_ip1 = bound_floating_ips[0]
        bound_floating_ip2 = bound_floating_ips[1]

        assert bound_floating_ip1._client is floating_ips_client
        assert bound_floating_ip1.id == 4711
        assert bound_floating_ip1.description == "Web Frontend"

        assert bound_floating_ip2._client is floating_ips_client
        assert bound_floating_ip2.id == 4712
        assert bound_floating_ip2.description == "Web Backend"

    def test_create_with_location(self, floating_ips_client,
                                  floating_ip_response):
        floating_ips_client._client.request.return_value = floating_ip_response
        response = floating_ips_client.create(
            "ipv6",
            "Web Frontend",
            home_location=Location(name="location"),
        )
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips",
            method="POST",
            json={
                'description': "Web Frontend",
                'type': "ipv6",
                'home_location': "location"
            })

        bound_floating_ip = response.floating_ip
        action = response.action

        assert bound_floating_ip._client is floating_ips_client
        assert bound_floating_ip.id == 4711
        assert bound_floating_ip.description == "Web Frontend"
        assert action is None

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_create_with_server(self, floating_ips_client, server,
                                floating_ip_create_response):
        floating_ips_client._client.request.return_value = floating_ip_create_response
        response = floating_ips_client.create(type="ipv6",
                                              description="Web Frontend",
                                              server=server)
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips",
            method="POST",
            json={
                'description': "Web Frontend",
                'type': "ipv6",
                'server': 1
            })
        bound_floating_ip = response.floating_ip
        action = response.action

        assert bound_floating_ip._client is floating_ips_client
        assert bound_floating_ip.id == 4711
        assert bound_floating_ip.description == "Web Frontend"
        assert action.id == 13

    @pytest.mark.parametrize(
        "server",
        [Server(id=1), BoundServer(mock.MagicMock(), dict(id=1))])
    def test_create_with_name(self, floating_ips_client, server,
                              floating_ip_create_response):
        floating_ips_client._client.request.return_value = floating_ip_create_response
        response = floating_ips_client.create(type="ipv6",
                                              description="Web Frontend",
                                              name="Web Frontend")
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips",
            method="POST",
            json={
                'description': "Web Frontend",
                'type': "ipv6",
                'name': "Web Frontend"
            })
        bound_floating_ip = response.floating_ip
        action = response.action

        assert bound_floating_ip._client is floating_ips_client
        assert bound_floating_ip.id == 4711
        assert bound_floating_ip.description == "Web Frontend"
        assert bound_floating_ip.name == "Web Frontend"
        assert action.id == 13

    @pytest.mark.parametrize(
        "floating_ip",
        [FloatingIP(id=1),
         BoundFloatingIP(mock.MagicMock(), dict(id=1))])
    def test_get_actions(self, floating_ips_client, floating_ip,
                         response_get_actions):
        floating_ips_client._client.request.return_value = response_get_actions
        actions = floating_ips_client.get_actions(floating_ip)
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips/1/actions",
            method="GET",
            params={
                'page': 1,
                'per_page': 50
            })

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

        assert actions[0]._client == floating_ips_client._client.actions
        assert actions[0].id == 13
        assert actions[0].command == "assign_floating_ip"

    @pytest.mark.parametrize(
        "floating_ip",
        [FloatingIP(id=1),
         BoundFloatingIP(mock.MagicMock(), dict(id=1))])
    def test_update(self, floating_ips_client, floating_ip,
                    response_update_floating_ip):
        floating_ips_client._client.request.return_value = response_update_floating_ip
        floating_ip = floating_ips_client.update(floating_ip,
                                                 description="New description",
                                                 name="New name")
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips/1",
            method="PUT",
            json={
                "description": "New description",
                "name": "New name"
            })

        assert floating_ip.id == 4711
        assert floating_ip.description == "New description"
        assert floating_ip.name == "New name"

    @pytest.mark.parametrize(
        "floating_ip",
        [FloatingIP(id=1),
         BoundFloatingIP(mock.MagicMock(), dict(id=1))])
    def test_change_protection(self, floating_ips_client, floating_ip,
                               generic_action):
        floating_ips_client._client.request.return_value = generic_action
        action = floating_ips_client.change_protection(floating_ip, True)
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips/1/actions/change_protection",
            method="POST",
            json={"delete": True})

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

    @pytest.mark.parametrize(
        "floating_ip",
        [FloatingIP(id=1),
         BoundFloatingIP(mock.MagicMock(), dict(id=1))])
    def test_delete(self, floating_ips_client, floating_ip, generic_action):
        floating_ips_client._client.request.return_value = generic_action
        delete_success = floating_ips_client.delete(floating_ip)
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips/1", method="DELETE")

        assert delete_success is True

    @pytest.mark.parametrize("server,floating_ip",
                             [(Server(id=1), FloatingIP(id=12)),
                              (BoundServer(mock.MagicMock(), dict(id=1)),
                               BoundFloatingIP(mock.MagicMock(), dict(id=12)))]
                             )
    def test_assign(self, floating_ips_client, server, floating_ip,
                    generic_action):
        floating_ips_client._client.request.return_value = generic_action
        action = floating_ips_client.assign(floating_ip, server)
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips/12/actions/assign",
            method="POST",
            json={"server": 1})
        assert action.id == 1
        assert action.progress == 0

    @pytest.mark.parametrize(
        "floating_ip",
        [FloatingIP(id=12),
         BoundFloatingIP(mock.MagicMock(), dict(id=12))])
    def test_unassign(self, floating_ips_client, floating_ip, generic_action):
        floating_ips_client._client.request.return_value = generic_action
        action = floating_ips_client.unassign(floating_ip)
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips/12/actions/unassign", method="POST")
        assert action.id == 1
        assert action.progress == 0

    @pytest.mark.parametrize(
        "floating_ip",
        [FloatingIP(id=12),
         BoundFloatingIP(mock.MagicMock(), dict(id=12))])
    def test_change_dns_ptr(self, floating_ips_client, floating_ip,
                            generic_action):
        floating_ips_client._client.request.return_value = generic_action
        action = floating_ips_client.change_dns_ptr(floating_ip, "1.2.3.4",
                                                    "server02.example.com")
        floating_ips_client._client.request.assert_called_with(
            url="/floating_ips/12/actions/change_dns_ptr",
            method="POST",
            json={
                "ip": "1.2.3.4",
                "dns_ptr": "server02.example.com"
            })
        assert action.id == 1
        assert action.progress == 0
Ejemplo n.º 27
0
    def _handle_changed_server_networks(self, defn: HetznerCloudDefinition,
                                        allow_recreate: bool) -> None:
        """
        Detects and corrects any virtual network state desynchronisation.
        """

        attached: Set[str] = {
            x.network.id
            for x in self.get_instance().private_net
        }

        # Detach server from networks
        for name in self.server_networks.keys():
            nw: Optional[BoundNetwork] = self.get_client(
            ).networks.get_by_name(name)

            # Detect destroyed networks
            if nw is None:
                if name not in defn.server_networks:  # we dont need it
                    self.logger.warn(
                        f"forgetting about network ‘{name}’ that no longer exists"
                        " and is no longer needed by the deployment specification"
                    )
                    self._update_attr("server_networks", name, None)
                else:  # we do need it
                    raise Exception(
                        f"network ‘{name}’ (used by {self.full_name}) no longer exists;"
                        " run ‘nixops deploy --check’ to update resource state"
                    )
            # Detect network detachment
            elif nw.id not in attached:
                self.logger.warn(
                    f"instance was manually detached from network ‘{name}’ [{nw.id}]"
                )
                if name in defn.server_networks:
                    self._update_attr("server_networks", name, None)
            # Detach from existing networks if required.
            elif name not in defn.server_networks:
                self.logger.log(f"detaching from network ‘{name}’ [{nw.id}]")
                self.get_client().servers.detach_from_network(
                    server=Server(self.vm_id),
                    network=nw).wait_until_finished()
                self._update_attr("server_networks", name, None)

        # Attach server to networks
        for name, x in defn.server_networks.items():
            if name not in self.server_networks:
                nw = self.get_client().networks.get_by_name(name)

                if nw is None:
                    raise Exception(
                        f"tried to attach instance to network ‘{name}’"
                        " but it doesn't exist...")

                # NixOps will update machines in parallel, so retry
                # network attachment to deal with resource conflict.
                def attach_to_network() -> bool:
                    try:
                        self.wait_on_action(
                            self.get_client().servers.attach_to_network(
                                server=Server(self.vm_id),
                                network=nw,
                                ip=x["privateIpAddress"],
                                alias_ips=x["aliasIpAddresses"],
                            ))
                    except APIException as e:
                        if e.code == "conflict":
                            return False
                        else:
                            raise
                    else:
                        self._update_attr("server_networks", x["network"], x)
                        return True

                self.logger.log(
                    f"attaching instance to network ‘{name}’ [{nw.id}]...")
                check_wait(attach_to_network)
Ejemplo n.º 28
0
 def test_remove_target(self, hetzner_client, load_balancer):
     action = hetzner_client.load_balancers.remove_target(load_balancer,
                                                          LoadBalancerTarget(type="server", server=Server(id=1)))
     assert action.id == 13
     assert action.command == "remove_target"
Ejemplo n.º 29
0
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"