diff --git a/libcloud/compute/drivers/vultr.py b/libcloud/compute/drivers/vultr.py index 5b0d990e79..0313144975 100644 --- a/libcloud/compute/drivers/vultr.py +++ b/libcloud/compute/drivers/vultr.py @@ -93,6 +93,26 @@ class VultrNodeDriver(NodeDriver): NODE_STATE_MAP = {'pending': NodeState.PENDING, 'active': NodeState.RUNNING} + EX_CREATE_YES_NO_ATTRIBUTES = ['enable_ipv6', + 'enable_private_network', + 'auto_backups', + 'notify_activate', + 'ddos_protection'] + + EX_CREATE_ID_ATTRIBUTES = {'iso_id': 'ISOID', + 'script_id': 'SCRIPTID', + 'snapshot_id': 'SNAPSHOTID', + 'app_id': 'APPID'} + + EX_CREATE_ATTRIBUTES = ['ipxe_chain_url', + 'label', + 'userdata', + 'reserved_ip_v4', + 'hostname', + 'tag'] + EX_CREATE_ATTRIBUTES.extend(EX_CREATE_YES_NO_ATTRIBUTES) + EX_CREATE_ATTRIBUTES.extend(EX_CREATE_ID_ATTRIBUTES.keys()) + def list_nodes(self): return self._list_resources('/v1/server/list', self._to_node) @@ -142,13 +162,75 @@ def list_sizes(self): def list_images(self): return self._list_resources('/v1/os/list', self._to_image) - def create_node(self, name, size, image, location, ex_ssh_key_ids=None): + def create_node(self, name, size, image, location, ex_ssh_key_ids=None, + ex_create_attr=None): + """ + Create a node + + :param name: Name for the new node + :type name: ``str`` + + :param size: Size of the new node + :type size: :class:`NodeSize` + + :param image: Image for the new node + :type image: :class:`NodeImage` + + :param location: Location of the new node + :type location: :class:`NodeLocation` + + :param ex_ssh_key_ids: IDs of the SSH keys to initialize + :type ex_sshkeyid: ``list`` of ``str`` + + :param ex_create_attr: Extra attributes for node creation + :type ex_create_attr: ``dict`` + + The `ex_create_attr` parameter can include the following dictionary + key and value pairs: + + * `ipxe_chain_url`: ``str`` for specifying URL to boot via IPXE + * `iso_id`: ``str`` the ID of a specific ISO to mount, + only meaningful with the `Custom` `NodeImage` + * `script_id`: ``int`` ID of a startup script to execute on boot, + only meaningful when the `NodeImage` is not `Custom` + * 'snapshot_id`: ``str`` Snapshot ID to restore for the initial + installation, only meaningful with the `Snapshot` `NodeImage` + * `enable_ipv6`: ``bool`` Whether an IPv6 subnet should be assigned + * `enable_private_network`: ``bool`` Whether private networking + support should be added + * `label`: ``str`` Text label to be shown in the control panel + * `auto_backups`: ``bool`` Whether automatic backups should be enabled + * `app_id`: ``int`` App ID to launch if launching an application, + only meaningful when the `NodeImage` is `Application` + * `userdata`: ``str`` Base64 encoded cloud-init user-data + * `notify_activate`: ``bool`` Whether an activation email should be + sent when the server is ready + * `ddos_protection`: ``bool`` Whether DDOS protection should be enabled + * `reserved_ip_v4`: ``str`` IP address of the floating IP to use as + the main IP of this server + * `hostname`: ``str`` The hostname to assign to this server + * `tag`: ``str`` The tag to assign to this server + + :return: The newly created node. + :rtype: :class:`Node` + + """ params = {'DCID': location.id, 'VPSPLANID': size.id, 'OSID': image.id, 'label': name} if ex_ssh_key_ids is not None: params['SSHKEYID'] = ','.join(ex_ssh_key_ids) + ex_create_attr = ex_create_attr or {} + for key, value in ex_create_attr.items(): + if key in self.EX_CREATE_ATTRIBUTES: + if key in self.EX_CREATE_YES_NO_ATTRIBUTES: + params[key] = 'yes' if value else 'no' + else: + if key in self.EX_CREATE_ID_ATTRIBUTES: + key = self.EX_CREATE_ID_ATTRIBUTES[key] + params[key] = value + result = self.connection.post('/v1/server/create', params) if result.status != httplib.OK: return False diff --git a/libcloud/test/compute/fixtures/vultr/create_node.json b/libcloud/test/compute/fixtures/vultr/create_node.json new file mode 100644 index 0000000000..57aa548290 --- /dev/null +++ b/libcloud/test/compute/fixtures/vultr/create_node.json @@ -0,0 +1,3 @@ +{ + "SUBID": "1" +} diff --git a/libcloud/test/compute/test_vultr.py b/libcloud/test/compute/test_vultr.py index 538e62f187..919aabfbf4 100644 --- a/libcloud/test/compute/test_vultr.py +++ b/libcloud/test/compute/test_vultr.py @@ -79,6 +79,14 @@ def test_reboot_node_success(self): result = self.driver.reboot_node(node) self.assertTrue(result) + def test_create_node_success(self): + test_size = self.driver.list_sizes()[0] + test_image = self.driver.list_images()[0] + test_location = self.driver.list_locations()[0] + created_node = self.driver.create_node('test-node', test_size, + test_image, test_location) + self.assertEqual(created_node.id, "1") + def test_destroy_node_success(self): node = self.driver.list_nodes()[0] result = self.driver.destroy_node(node) @@ -121,6 +129,10 @@ def _v1_server_list(self, method, url, body, headers): body = self.fixtures.load('list_nodes.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _v1_server_create(self, method, url, body, headers): + body = self.fixtures.load('create_node.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _v1_server_destroy(self, method, url, body, headers): return (httplib.OK, "", {}, httplib.responses[httplib.OK])