Publish
This commit is contained in:
commit
037f8beb94
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 powermaker450
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
73
README.md
Normal file
73
README.md
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# puffpan
|
||||||
|
|
||||||
|
puffpan is a small Python module for interacting with a Pufferpanel daemon.
|
||||||
|
|
||||||
|
Made this in my spare time because I needed it.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
First make sure you have Pufferpanel set up, and a deploy a server you intend to interact with it.
|
||||||
|
Create an OAuth2 application for this server. Like it says, **write down the secret key in a safe place. It is only ever displayed once.**
|
||||||
|
|
||||||
|
Now you have Pufferpanel, a server, it's server ID (randomly generated 8 character string, usually displayed in the URL in the browser), client ID and secret ID.
|
||||||
|
|
||||||
|
Make sure you have the requests and json modules available.
|
||||||
|
|
||||||
|
### Using in your code
|
||||||
|
|
||||||
|
Place the puffpan file next to the code you want to use it with. Import the module:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import puffpan
|
||||||
|
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you are ready to create a puffpan!
|
||||||
|
Using the information you gathered before, create an object:
|
||||||
|
|
||||||
|
```python
|
||||||
|
server = puffpan.Panel('your-server-url', 'your-client-id', 'your-secret-key', 'your-server-id')
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, alternatively, if you want to use a client that has access to multiple servers at once:
|
||||||
|
|
||||||
|
```python
|
||||||
|
admin = puffpan.Panel('your-server-url', 'your-client-id', 'your-secret-key')
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep in mind that with this method, you will have to specify the server ID for most commands. Instead of
|
||||||
|
|
||||||
|
```python
|
||||||
|
admin.stop()
|
||||||
|
```
|
||||||
|
you must now type
|
||||||
|
```python
|
||||||
|
admin.stop(serverID = 'a1b2c3d4')
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can run various methods that return useful information about your server, or interact with it.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```python
|
||||||
|
server.logs()
|
||||||
|
# Returns logs as a string
|
||||||
|
|
||||||
|
server.stats()
|
||||||
|
# {'cpu_usage': 5, 'ram_usage': 135.50}
|
||||||
|
|
||||||
|
server.status()
|
||||||
|
# True
|
||||||
|
# (Returns True if running, False if not)
|
||||||
|
|
||||||
|
server.stop()
|
||||||
|
# 204
|
||||||
|
|
||||||
|
server.edit_data(key = 'server-name', value = 'new and better name!')
|
||||||
|
# 204
|
||||||
|
```
|
||||||
|
|
||||||
|
Most of the `/daemon` endpoints specified in the Pufferpanel API docs are available for use through this module.
|
491
puffpan.py
Normal file
491
puffpan.py
Normal file
|
@ -0,0 +1,491 @@
|
||||||
|
"""Pretty simple Python module that makes interacting with a Pufferpanel Server Daemon easy."""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from time import time, sleep
|
||||||
|
|
||||||
|
|
||||||
|
class ServerError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Panel:
|
||||||
|
"""
|
||||||
|
A Panel object must get assigned 3 values: serverURL, clientID, and clientSecret.
|
||||||
|
These can be obtained by creating an oauth2 client in PufferPanel.
|
||||||
|
|
||||||
|
The user can also (optionally) define serverID, which is useful if the scope of your token doesn't cover multiple servers.
|
||||||
|
When serverID is defined, the serverID parameter can be skipped in methods where it is required, so
|
||||||
|
|
||||||
|
object.logs('a1b236')
|
||||||
|
|
||||||
|
becomes
|
||||||
|
|
||||||
|
object.logs()
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, serverUrl: str, clientId: str, clientSecret: str, serverId=None):
|
||||||
|
|
||||||
|
self.URL = serverUrl
|
||||||
|
self.ID = clientId
|
||||||
|
self.SECRET = clientSecret
|
||||||
|
self.SERVER_ID = serverId
|
||||||
|
|
||||||
|
if self.SERVER_ID:
|
||||||
|
try:
|
||||||
|
self.name = self.api_server()["server"]["name"]
|
||||||
|
except KeyError:
|
||||||
|
self.name = "Unknown"
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.type = self.api_server()["server"]["type"]
|
||||||
|
except KeyError:
|
||||||
|
self.type = "generic-type"
|
||||||
|
else:
|
||||||
|
if not (self.getToken()):
|
||||||
|
raise ValueError(
|
||||||
|
"Could not obtain a token from the server. Are your credentials correct?"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return (
|
||||||
|
f'URL: {self.URL}, Client ID: {self.ID}, using "{self.name}" ({self.SERVER_ID}) running a {self.type} server'
|
||||||
|
if self.SERVER_ID
|
||||||
|
else f"URL: {self.URL}, Client ID: {self.ID}"
|
||||||
|
)
|
||||||
|
# What this might look like:
|
||||||
|
# "URL: https://panel.domain.com, Client ID: randomClientId, using YourServer (randomSecretKey) running a type-of server"
|
||||||
|
#
|
||||||
|
# or
|
||||||
|
#
|
||||||
|
# "URL: https://panel.domain.com, Client ID: randomClientId"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
f"Panel('{self.URL}', '{self.ID}', '{self.SECRET}', '{self.SERVER_ID}')"
|
||||||
|
if self.SERVER_ID
|
||||||
|
else f"Panel('{self.URL}', '{self.ID}', '{self.SECRET}')"
|
||||||
|
)
|
||||||
|
# What this might look like:
|
||||||
|
# Panel('https://panel.domain.com', 'randomClientId', 'randomSecretKey', 'a1b236')
|
||||||
|
#
|
||||||
|
# or
|
||||||
|
#
|
||||||
|
# Panel('https://panel.domain.com', 'randomClientId', 'randomSecretKey')
|
||||||
|
|
||||||
|
def invalidCode(self, given: object, override=False) -> bool:
|
||||||
|
validCodes = []
|
||||||
|
for i in range(200, 300):
|
||||||
|
validCodes.append(i)
|
||||||
|
isInvalidCode = not (given.status_code in validCodes)
|
||||||
|
|
||||||
|
isServerOfflineMsg = given.json() == {
|
||||||
|
"error": {"msg": "server offline", "code": "ErrServerOffline"}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
isInvalidCode or isServerOfflineMsg if not (override) else isInvalidCode
|
||||||
|
) # Valid codes are 200-299.
|
||||||
|
|
||||||
|
# The server can also return an 'error' as written here, even if the request was successful.
|
||||||
|
# I've just made it so that it behaves exactly as if it had gotten an invalid response code, but added a manual override so that creating an object while this special error is returned is still possible.
|
||||||
|
# If the response message changes or if there is a better way to handle this I'll change it
|
||||||
|
|
||||||
|
def getToken(self) -> str:
|
||||||
|
"""
|
||||||
|
Obtains a token from the server with the provided client ID and secret.
|
||||||
|
Returns the token if succesful, error if not.
|
||||||
|
|
||||||
|
This function is called anytime there is any contact with the daemon,
|
||||||
|
because the token expires after a certain amount of time.
|
||||||
|
"""
|
||||||
|
GET_TOKEN_HEADER = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
f"{self.URL}/oauth2/token",
|
||||||
|
data=f"grant_type=client_credentials&client_id={self.ID}&client_secret={self.SECRET}",
|
||||||
|
headers=GET_TOKEN_HEADER,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.invalidCode(response):
|
||||||
|
print(self.invalidCode(response))
|
||||||
|
raise ValueError(
|
||||||
|
"Could not obtain a token from the server. Are your credentials corret?"
|
||||||
|
)
|
||||||
|
|
||||||
|
accessToken = response.json()["access_token"]
|
||||||
|
|
||||||
|
return accessToken
|
||||||
|
|
||||||
|
def tokenIt(self, serverID) -> tuple:
|
||||||
|
token = self.getToken()
|
||||||
|
serverID = serverID or self.SERVER_ID
|
||||||
|
if not (serverID):
|
||||||
|
raise TypeError("Missing 1 required argument: 'serverID'")
|
||||||
|
AUTH_HEADER = {"Authorization": f"Bearer {token}"}
|
||||||
|
|
||||||
|
return (
|
||||||
|
serverID,
|
||||||
|
AUTH_HEADER,
|
||||||
|
) # tuple[0] is the server ID and tuple[1] is the HTTP header
|
||||||
|
|
||||||
|
# All GET methods
|
||||||
|
|
||||||
|
def api_server(self, serverID=None) -> dict:
|
||||||
|
"""
|
||||||
|
Gets the data of a given server from the API, not the config.
|
||||||
|
|
||||||
|
Returns the physical data of the server.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/api/servers/{data[0]}"
|
||||||
|
|
||||||
|
response = requests.get(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
if self.invalidCode(response):
|
||||||
|
raise ServerError(f"Server returned an invalid response: {response.json()}")
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
# In it's current state this method was only implemented to get the server name, not much else.
|
||||||
|
|
||||||
|
def daemon(self) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the API is active.
|
||||||
|
Different from status(), which checks if the given server is running, not the actual API.
|
||||||
|
|
||||||
|
Returns True if running, False if not.
|
||||||
|
"""
|
||||||
|
data = {"Authorization": f"Bearer {self.getToken()}"}
|
||||||
|
fullURL = f"{self.URL}/daemon"
|
||||||
|
|
||||||
|
response = requests.get(fullURL, headers=data)
|
||||||
|
|
||||||
|
if self.invalidCode(response):
|
||||||
|
return False
|
||||||
|
|
||||||
|
isRunning = response.json()["message"] == "daemon is running"
|
||||||
|
# Returns True if the server is running ASSUMING that the running message is "daemon is running". If this message changes I'll fix it
|
||||||
|
|
||||||
|
return isRunning
|
||||||
|
|
||||||
|
def data_admin(self, serverID=None) -> dict:
|
||||||
|
"""
|
||||||
|
Gets the full data of a server given a server ID as admin.
|
||||||
|
|
||||||
|
Returns the data of the server.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}"
|
||||||
|
|
||||||
|
response = requests.get(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
if self.invalidCode(response):
|
||||||
|
raise ServerError(f"Server returned an invalid response: {response.json()}")
|
||||||
|
|
||||||
|
if response.json() == {"data": {}}:
|
||||||
|
raise ServerError(
|
||||||
|
f"Server returned an empty dataset: {response.json()}\nAre you using a client that inherits permissions for all servers?"
|
||||||
|
)
|
||||||
|
|
||||||
|
return response.json()["data"]
|
||||||
|
# All server data is within one key: 'data'. If this is a bad idea I'll change it
|
||||||
|
|
||||||
|
def logs(self, serverID=None) -> str:
|
||||||
|
"""
|
||||||
|
Gets the server log given a server ID with the process described at
|
||||||
|
https://hosting.povario.com/swagger/index.html#/default/get_daemon_server__id__console
|
||||||
|
|
||||||
|
This is different from the client ID, which is only used when obtaining a token with getToken().
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/console" # Todo: Implement selecting a timestamp
|
||||||
|
|
||||||
|
response = requests.get(fullURL, headers=data[1])
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
if self.invalidCode(response, override=True):
|
||||||
|
raise ServerError(f"Server returned an invalid response: {response.json()}")
|
||||||
|
|
||||||
|
logs = response.json()["logs"]
|
||||||
|
|
||||||
|
return logs
|
||||||
|
|
||||||
|
def data(self, serverID=None) -> dict:
|
||||||
|
"""
|
||||||
|
Gets the full data of a server given a server ID with the process described at
|
||||||
|
https://hosting.povario.com/swagger/index.html#/default/get_daemon_server__id_
|
||||||
|
|
||||||
|
Returns the data of the server.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/data"
|
||||||
|
|
||||||
|
response = requests.get(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
if self.invalidCode(response):
|
||||||
|
raise ServerError(f"Server returned an invalid response: {response.json()}")
|
||||||
|
|
||||||
|
if response.json() == {"data": {}}:
|
||||||
|
raise ServerError(
|
||||||
|
f"Server returned an empty dataset: {response.json()}\nAre you using a client that inherits permissions for all servers?"
|
||||||
|
)
|
||||||
|
|
||||||
|
return response.json()["data"] # Same as data_admin().
|
||||||
|
|
||||||
|
def extract(self, filename: str, serverID=None) -> int:
|
||||||
|
"""
|
||||||
|
Extracts a given file on the given server.
|
||||||
|
|
||||||
|
Only returns the HTTP response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/extract/{filename}"
|
||||||
|
|
||||||
|
response = requests.get(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
if self.invalidCode(response):
|
||||||
|
raise ServerError(f"Server returned an invalid response: {response.json()}")
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
# def get_file(self,filename,serverID=None):
|
||||||
|
# """
|
||||||
|
# Lists a file or directory on the given server.
|
||||||
|
#
|
||||||
|
# Returns either a dictonary with the file properties or a dictionary with individual files in a directory.
|
||||||
|
# """
|
||||||
|
#
|
||||||
|
# data=self.tokenIt(serverID)
|
||||||
|
# fullURL=f'{self.URL}/proxy/daemon/server/{data[0]}/file/{filename}'
|
||||||
|
#
|
||||||
|
# response=requests.get(f'{fullURL}',
|
||||||
|
# headers=data[1])
|
||||||
|
#
|
||||||
|
# if self.invalidCode(response):
|
||||||
|
# raise ServerError(f'Server returned an invalid response: {response.json()}')
|
||||||
|
#
|
||||||
|
# return response.json()
|
||||||
|
#
|
||||||
|
# Kinda broken. Don't use please thank you
|
||||||
|
|
||||||
|
def stats(self, serverID=None, precise=False) -> dict:
|
||||||
|
"""
|
||||||
|
Gets the server stats given a server ID with the process described at
|
||||||
|
https://hosting.povario.com/swagger/index.html#/default/get_daemon_server__id__stats
|
||||||
|
|
||||||
|
Returns a dictonary with the following values rounded to two decimal places:
|
||||||
|
|
||||||
|
CPU Usage (float)
|
||||||
|
RAM Usage in MB (float)
|
||||||
|
|
||||||
|
Unless precise=True, in which case the values will not be rounded.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/stats"
|
||||||
|
|
||||||
|
response = requests.get(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
if self.invalidCode(response):
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
cpu = response.json()["cpu"]
|
||||||
|
ram = (response.json()["memory"] / 1000) / 1000
|
||||||
|
|
||||||
|
formattedUsage = (
|
||||||
|
{"cpu_usage": round(cpu, 2), "ram_usage": round(ram, 2)}
|
||||||
|
if not (precise)
|
||||||
|
else {"cpu_usage": cpu, "ram_usage": ram}
|
||||||
|
)
|
||||||
|
|
||||||
|
return formattedUsage
|
||||||
|
|
||||||
|
def status(self, serverID=None) -> str:
|
||||||
|
"""
|
||||||
|
Checks if the server is running.
|
||||||
|
|
||||||
|
Returns True if running, False if not.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/status"
|
||||||
|
|
||||||
|
response = requests.get(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
if self.invalidCode(response):
|
||||||
|
raise ServerError(f"Server returned an invalid response: {response.json()}")
|
||||||
|
|
||||||
|
return response.json()["running"]
|
||||||
|
|
||||||
|
# All POST methods
|
||||||
|
|
||||||
|
def update(self, serverID=None) -> int:
|
||||||
|
"""
|
||||||
|
Updates a server.
|
||||||
|
|
||||||
|
Only returns the HTTP response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}"
|
||||||
|
|
||||||
|
response = requests.post(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def archive(self, filename, serverID=None) -> int:
|
||||||
|
"""
|
||||||
|
Archives a given file on the server.
|
||||||
|
|
||||||
|
Only returns the HTTP response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/archive/{filename}"
|
||||||
|
|
||||||
|
response = requests.post(fullURL, data=f"{filename}", headers=data[1])
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def exec(self, command, serverID=None) -> int:
|
||||||
|
"""
|
||||||
|
Executes a command on a given server.
|
||||||
|
|
||||||
|
Only returns the HTTP response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/console"
|
||||||
|
|
||||||
|
response = requests.post(fullURL, data=command, headers=data[1])
|
||||||
|
|
||||||
|
# previousLogs = self.logs(serverID)
|
||||||
|
# reply=self.logs(serverID)
|
||||||
|
# return reply.replace(previousLogs,'')
|
||||||
|
#
|
||||||
|
# Todo: return the logs instead of just the status code.
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def edit_data(self, key, value, serverID=None) -> int:
|
||||||
|
"""
|
||||||
|
Edits the server config.
|
||||||
|
|
||||||
|
Only returns the HTTP Response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
reply = self.data(serverID)
|
||||||
|
options = []
|
||||||
|
for option in reply:
|
||||||
|
options.append(option)
|
||||||
|
optionExists = key in options
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/data"
|
||||||
|
|
||||||
|
if optionExists:
|
||||||
|
nonMatchingValue = type(reply[key]["value"]) != type(value)
|
||||||
|
if nonMatchingValue:
|
||||||
|
raise ValueError(
|
||||||
|
f"Value must match type for key '{key}'\n{type(value)} does not match {type(reply[key]['value'])}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"'{key}' does not match any in the options list:\n{options}"
|
||||||
|
)
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
fullURL, data=json.dumps({"data": reply}), headers=data[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def install(self, serverID=None) -> int:
|
||||||
|
"""
|
||||||
|
Queues an installation for the given server.
|
||||||
|
Potentially destructive action. (Can overwrite needed data such as config files)
|
||||||
|
|
||||||
|
Only returns the HTTP response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/install"
|
||||||
|
|
||||||
|
response = requests.post(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def kill(self, serverID=None) -> int:
|
||||||
|
"""
|
||||||
|
Force quits a server without warning.
|
||||||
|
|
||||||
|
Only returns the HTTP response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/kill"
|
||||||
|
|
||||||
|
response = requests.post(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def reload(self, serverID=None) -> int:
|
||||||
|
"""
|
||||||
|
Reloads a server from disk.
|
||||||
|
Potentially destructive action. (May overwrite server data)
|
||||||
|
This method is NOT the same as restart(), which stops then starts a server.
|
||||||
|
|
||||||
|
Only returns the HTTP response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/reload"
|
||||||
|
|
||||||
|
response = requests.post(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def start(self, serverID=None):
|
||||||
|
"""
|
||||||
|
Starts the given server.
|
||||||
|
|
||||||
|
Only returns the HTTP response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/start"
|
||||||
|
|
||||||
|
response = requests.post(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def stop(self, serverID=None):
|
||||||
|
"""
|
||||||
|
Stops the given server.
|
||||||
|
|
||||||
|
Only returns the HTTP response code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = self.tokenIt(serverID)
|
||||||
|
fullURL = f"{self.URL}/proxy/daemon/server/{data[0]}/stop"
|
||||||
|
|
||||||
|
response = requests.post(fullURL, headers=data[1])
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
def restart(self, serverID=None) -> int:
|
||||||
|
"""
|
||||||
|
Stops, then starts a given server.
|
||||||
|
|
||||||
|
Only returns the HTTP status code from the start() method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.stop(serverID)
|
||||||
|
sleep(1.5)
|
||||||
|
response = self.start(serverID)
|
||||||
|
|
||||||
|
return response
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
requests
|
13
setup.py
Normal file
13
setup.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name = "puffpan",
|
||||||
|
description = "puffpan is a small python module for interacting with a Pufferpanel daemon.",
|
||||||
|
version = "1.0",
|
||||||
|
author = "powermaker450",
|
||||||
|
author_email = "contact@povario.com",
|
||||||
|
url = "https://github.com/powermaker450/puff",
|
||||||
|
install_requires = ["setuptools", "requests"],
|
||||||
|
py_modules = ["pufferpy"],
|
||||||
|
license = "MIT"
|
||||||
|
)
|
Loading…
Reference in a new issue