def delete_user(self, user_id=None, external_id=None, detail=True): """Deletes the specified user. Will look up by external_id if provided. This action cannot be reversed so use with care. Args: user_id(int): Pterodactyl user ID external_id(int): User ID from external system like WHMCS detail(bool): If True includes created and updated timestamps. """ if not user_id and not external_id: raise BadRequestError( 'Must specify either user_id or external_id.') if user_id and external_id: raise BadRequestError('Specify either user_id or external_id, ' 'not both.') if external_id: response = self._api_request( endpoint='application/users/external/%s' % external_id) user_id = response['attributes']['id'] response = self._api_request(endpoint='application/users/%s' % user_id, mode='DELETE') return base.parse_response(response, detail=detail)
def send_power_action(self, server_id, signal): """Sends a console command to the specified server. The server must be online, otherwise API will return a HTTP 412 error. If successful, there will be an empty response body. Args: server_id(str): Server identifier (abbreviated UUID) signal(str): Power signal to send to the server. Valid options: start - Sends the startup command to the server. stop - Sends the stop command to the server. restart - Stops the server then immediately starts it. kill - Instantly ends all processes and marks the server as stopped. The kill signal can corrupt server files and should only be used as a last resort. """ if signal not in POWER_SIGNALS: raise BadRequestError( 'Invalid power signal sent(%s), must be one of: %r' % ( signal, POWER_SIGNALS)) data = {'signal': signal} response = self._api_request( endpoint='client/servers/%s/power' % server_id, mode='POST', data=data, json=False) return response
def get_user_info(self, user_id=None, external_id=None, detail=True): """List detailed user information for specified user_id. Args: user_id(int): Pterodactyl user ID external_id(int): User ID from external system like WHMCS detail(bool): If True includes created and updated timestamps. """ if not user_id and not external_id: raise BadRequestError( 'Must specify either user_id or external_id.') if user_id and external_id: raise BadRequestError('Specify either user_id or external_id, ' 'not both.') if user_id: endpoint = 'application/users/%s' % user_id else: endpoint = 'application/users/external/%s' % external_id response = self._api_request(endpoint=endpoint) return base.parse_response(response, detail=detail)
def get_server_info(self, server_id=None, external_id=None, detail=False): """Get detailed info for the specified server. Args: server_id(int): Pterodactyl Server ID. external_id(int): Server ID from an external system like WHMCS detail(bool): If True includes created and updated timestamps. """ if not server_id and not external_id: raise BadRequestError('Must specify either server_id or ' 'external_id.') if server_id and external_id: raise BadRequestError('Specify either server_id or external_id, ' 'not both.') if server_id: endpoint = 'application/servers/%s' % server_id else: endpoint = 'application/servers/external/%s' % external_id response = self._api_request(endpoint=endpoint) return base.parse_response(response, detail)
def edit_node(self, node_id, shortcode=None, description=None): """Modify an existing node. Args: node_id(int): Pterodactyl Node ID. shortcode(str): Short identifier between 1 and 60 characters, e.g. us.nyc.lvl3 description(str): A long description of the node. Max 255 characters. """ if not shortcode and not description: raise BadRequestError( 'Must specify either shortcode or description for edit_node.') data = {} if shortcode: data['shortcode'] = shortcode if description: data['description'] = description response = self._api_request(endpoint='application/nodes/%s' % node_id, mode='PATCH', data=data) return response
def create_server(self, name, user_id, nest_id, egg_id, memory_limit, swap_limit, disk_limit, location_ids=[], port_range=[], environment={}, cpu_limit=0, io_limit=500, database_limit=0, allocation_limit=0, docker_image=None, startup_cmd=None, dedicated_ip=False, start_on_completion=True, oom_disabled=True, default_allocation=None, additional_allocations=None): """Creates one or more servers in the specified locations. Creates server instance(s) and begins the install process using the specified configurations and limits. If more than one value is specified for location_ids then identical servers will be created in each location. Args: name(str): Name of the server to display in the panel. user_id(int): User ID that will own the server. nest_id(int): Nest ID for the created server. egg_id(int): Egg ID for the created server. memory_limit(int): Memory limit in MB for the Docker container. To allow unlimited memory limit set to 0. swap_limit(int): Swap limit in MB for the Docker container. To not assign any swap set to 0. For unlimited swap set to -1. disk_limit(int): Disk limit in MB for the Docker container. To allow unlimited disk space set to 0. environment(dict): Key value pairs of Service Variables to set. Every variable from the egg must be set or the API will return an error. Default values will be pulled from the egg config or set to None. location_ids(iter): List of location_ids where the server(s) will be created. If more than one location is specified identical servers will be created at each. port_range(iter): List of ports or port ranges to use when selecting an allocation. If empty, all ports will be considered. If set, only ports appearing in the list or range will be used. e.g. [20715, '20815-20915'] cpu_limit(int): CPU limit for the Docker container. To allow unlimited CPU usage set to 0. To limit to one core set to 100. For four cores set to 400. io_limit(int): Block IO weight for the Docker container. Must be between 10 and 1000. database_limit(int): Maximum number of databases that can be assigned to this server. allocation_limit(int): Maximum number of allocations that can be assigned to this server. docker_image(str): Name or URL of the Docker server to use. e.g. quay.io/pterodactyl/core:java-glibc startup_cmd(str): Startup command, if specified replaces the egg's default value. dedicated_ip(bool): Limit allocations to IPs without any existing allocations. start_on_completion(bool): Start server after install completes. oom_disabled(bool): Disables OOM-killer on the Docker container. default_allocation(int): Specify allocation(s) instead of using the Pterodactyl deployment service. Uses the allocation's internal ID and not the port number. additional_allocations(iter): Additional allocations on top of default_allocation. """ # Fetch the Egg variables which are required to create the server. egg_info = self._api_request( endpoint='application/nests/%s/eggs/%s' % (nest_id, egg_id), params={'include': 'variables'})['attributes'] egg_vars = egg_info['relationships']['variables']['data'] # Build a dict of environment variables. Prefer values passed in the # environment parameter, otherwise use the default value from the Egg # config. env_with_defaults = {} for var in egg_vars: var_name = var['attributes']['env_variable'] if var_name in environment: env_with_defaults = environment[var_name] else: env_with_defaults[var_name] = var['attributes'].get( 'default_value') if not docker_image: docker_image = egg_info.get('docker_image') if not startup_cmd: startup_cmd = egg_info.get('startup') data = { 'name': name, 'user': user_id, 'nest': nest_id, 'egg': egg_id, 'docker_image': docker_image, 'startup': startup_cmd, 'oom_disabled': oom_disabled, 'limits': { 'memory': memory_limit, 'swap': swap_limit, 'disk': disk_limit, 'io': io_limit, 'cpu': cpu_limit, }, 'feature_limits': { 'databases': database_limit, 'allocations': allocation_limit, }, 'environment': env_with_defaults, 'start_on_completion': start_on_completion, } if default_allocation is not None: data['allocation'] = { 'default': default_allocation, 'additional': additional_allocations } elif location_ids: data['deploy'] = { 'locations': location_ids, 'dedicated_ip': dedicated_ip, 'port_range': port_range } else: raise BadRequestError('Must specify either default_allocation or ' 'location_ids') response = self._api_request(endpoint='application/servers', mode='POST', data=data, json=False) return response
def _api_request(self, endpoint, mode='GET', params=None, data=None, json=None): """Make a request to the Pterodactyl API. Args: endpoint(str): URI for the API mode(str): Request type, one of ('GET', 'POST', 'PATCH', 'DELETE) params(dict): Extra parameters to pass to the endpoint, e.g. a query string data(dict): POST data json(bool): Set to False to return the response object, True for just JSON. Defaults to returning JSON if possible otherwise the response object. Returns: response: A HTTP response object or the JSON response depending on the value of the json parameter. """ if not endpoint: raise BadRequestError('No API endpoint was specified.') url = url_join(self._url, 'api', endpoint) headers = self._get_headers() if mode == 'GET': response = self._session.get(url, params=params, headers=headers) elif mode == 'POST': response = self._session.post(url, params=params, headers=headers, json=data) elif mode == 'PATCH': response = self._session.patch(url, params=params, headers=headers, json=data) elif mode == 'DELETE': response = self._session.delete(url, params=params, headers=headers) elif mode == 'PUT': response = self._session.put(url, params=params, headers=headers, json=data) else: raise BadRequestError( 'Invalid request type specified(%s). Must be one of %r.' % (mode, REQUEST_TYPES)) try: response_json = response.json() except ValueError: response_json = {} if response.status_code in (400, 422): raise PterodactylApiError('API Request resulted in errors: %s' % response_json.get('errors')) else: response.raise_for_status() if json is True: return response_json elif json is False: return response else: return response_json or response
def check_schedule_action_valid(action): if action not in SCHEDULE_ACTIONS: raise BadRequestError( 'Invalid schedule action sent({}), must be one of: {}'.format( action, SCHEDULE_ACTIONS))