v1 code
This commit is contained in:
commit
1cd8eee616
6 changed files with 326 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.env
|
||||||
|
.idea/
|
||||||
|
__pycache__/
|
||||||
|
venv/
|
17
Pipfile
Normal file
17
Pipfile
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[[source]]
|
||||||
|
name = "pypi"
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
pyotp = "*"
|
||||||
|
python-decouple = "*"
|
||||||
|
requests = "*"
|
||||||
|
flask = "*"
|
||||||
|
flask-restful = "*"
|
||||||
|
python-etcd = "*"
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.7"
|
184
Pipfile.lock
generated
Normal file
184
Pipfile.lock
generated
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "edc89a3dee9f14e3a59020136d4d87c006baab717f9f67b61e4b197e6a29865b"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.7"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"aniso8601": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:513d2b6637b7853806ae79ffaca6f3e8754bdd547048f5ccc1420aec4b714f1e",
|
||||||
|
"sha256:d10a4bf949f619f719b227ef5386e31f49a2b6d453004b21f02661ccc8670c7b"
|
||||||
|
],
|
||||||
|
"version": "==7.0.0"
|
||||||
|
},
|
||||||
|
"certifi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
|
||||||
|
"sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"
|
||||||
|
],
|
||||||
|
"version": "==2019.6.16"
|
||||||
|
},
|
||||||
|
"chardet": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||||
|
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||||
|
],
|
||||||
|
"version": "==3.0.4"
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
||||||
|
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
||||||
|
],
|
||||||
|
"version": "==7.0"
|
||||||
|
},
|
||||||
|
"dnspython": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01",
|
||||||
|
"sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d"
|
||||||
|
],
|
||||||
|
"version": "==1.16.0"
|
||||||
|
},
|
||||||
|
"flask": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ad7c6d841e64296b962296c2c2dabc6543752985727af86a975072dea984b6f3",
|
||||||
|
"sha256:e7d32475d1de5facaa55e3958bc4ec66d3762076b074296aa50ef8fdc5b9df61"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.0.3"
|
||||||
|
},
|
||||||
|
"flask-restful": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ecd620c5cc29f663627f99e04f17d1f16d095c83dc1d618426e2ad68b03092f8",
|
||||||
|
"sha256:f8240ec12349afe8df1db168ea7c336c4e5b0271a36982bff7394f93275f2ca9"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.3.7"
|
||||||
|
},
|
||||||
|
"idna": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
||||||
|
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
||||||
|
],
|
||||||
|
"version": "==2.8"
|
||||||
|
},
|
||||||
|
"itsdangerous": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
|
||||||
|
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
|
||||||
|
],
|
||||||
|
"version": "==1.1.0"
|
||||||
|
},
|
||||||
|
"jinja2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
|
||||||
|
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
|
||||||
|
],
|
||||||
|
"version": "==2.10.1"
|
||||||
|
},
|
||||||
|
"markupsafe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
|
||||||
|
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
|
||||||
|
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
|
||||||
|
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
|
||||||
|
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
|
||||||
|
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
|
||||||
|
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
|
||||||
|
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
|
||||||
|
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
|
||||||
|
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
|
||||||
|
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
|
||||||
|
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
|
||||||
|
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
|
||||||
|
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
|
||||||
|
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
|
||||||
|
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
|
||||||
|
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
|
||||||
|
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
|
||||||
|
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
|
||||||
|
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
|
||||||
|
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
|
||||||
|
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
|
||||||
|
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
|
||||||
|
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
|
||||||
|
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
|
||||||
|
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
|
||||||
|
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
|
||||||
|
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
|
||||||
|
],
|
||||||
|
"version": "==1.1.1"
|
||||||
|
},
|
||||||
|
"pyotp": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1e3dc3d16919c4efac528d1dbecc17de1a97c4ecfdacb89d7726ed2c6645adff",
|
||||||
|
"sha256:be0ffeabddaa5ee53e7204e7740da842d070cf69168247a3d0c08541b84de602"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.2.7"
|
||||||
|
},
|
||||||
|
"python-decouple": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1317df14b43efee4337a4aa02914bf004f010cd56d6c4bd894e6474ec8c4fe2d"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.1"
|
||||||
|
},
|
||||||
|
"python-etcd": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:f1b5ebb825a3e8190494f5ce1509fde9069f2754838ed90402a8c11e1f52b8cb"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.4.5"
|
||||||
|
},
|
||||||
|
"pytz": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda",
|
||||||
|
"sha256:d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141"
|
||||||
|
],
|
||||||
|
"version": "==2019.1"
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
||||||
|
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.22.0"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||||
|
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||||
|
],
|
||||||
|
"version": "==1.12.0"
|
||||||
|
},
|
||||||
|
"urllib3": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
|
||||||
|
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
|
||||||
|
],
|
||||||
|
"version": "==1.25.3"
|
||||||
|
},
|
||||||
|
"werkzeug": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:865856ebb55c4dcd0630cdd8f3331a1847a819dda7e8c750d3db6f2aa6c0209c",
|
||||||
|
"sha256:a0b915f0815982fb2a09161cb8f31708052d0951c3ba433ccc5e1aa276507ca6"
|
||||||
|
],
|
||||||
|
"version": "==0.15.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
33
README.md
Normal file
33
README.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# ucloud-api
|
||||||
|
[![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
**Make sure you have Python >= 3.6 and Pipenv installed.**
|
||||||
|
|
||||||
|
1. Clone the repository and `cd` into it.
|
||||||
|
2. Run the following commands
|
||||||
|
- `pipenv install`
|
||||||
|
- `pipenv shell`
|
||||||
|
- `python main.py`
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
### Create VM
|
||||||
|
To Create VM send a `POST` request at `/vm/create` with JSON body.
|
||||||
|
An Example JSON Request Body is shown below
|
||||||
|
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"realm": "user realm",
|
||||||
|
"seed": "user seed",
|
||||||
|
"specs": {
|
||||||
|
"cpu": 16,
|
||||||
|
"ram": 256,
|
||||||
|
"hdd": "2TB",
|
||||||
|
"sdd": "256GB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
45
helper.py
Normal file
45
helper.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import binascii
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from decouple import config
|
||||||
|
from pyotp import TOTP
|
||||||
|
|
||||||
|
|
||||||
|
def check_otp(name, realm, seed):
|
||||||
|
try:
|
||||||
|
data = {
|
||||||
|
"auth_name": config('AUTH_NAME', ''),
|
||||||
|
"auth_token": TOTP(config('AUTH_SEED', '')).now(),
|
||||||
|
"auth_realm": config('AUTH_REALM', ''),
|
||||||
|
"name": name,
|
||||||
|
"realm": realm,
|
||||||
|
"token": TOTP(seed).now()
|
||||||
|
}
|
||||||
|
except binascii.Error:
|
||||||
|
return 400
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
"{OTP_SERVER}{OTP_VERIFY_ENDPOINT}".format(
|
||||||
|
OTP_SERVER=config('OTP_SERVER', ''),
|
||||||
|
OTP_VERIFY_ENDPOINT=config('OTP_VERIFY_ENDPOINT', 'verify/')
|
||||||
|
),
|
||||||
|
data=data
|
||||||
|
)
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
|
||||||
|
def get_next_id(client, path):
|
||||||
|
r = client.read(path)
|
||||||
|
|
||||||
|
max_key_result = max(r.children, key=lambda x: int(strip_nondigit(x.key)))
|
||||||
|
if max_key_result is None:
|
||||||
|
# No key found
|
||||||
|
return 0
|
||||||
|
|
||||||
|
max_key = strip_nondigit(max_key_result.key.split("/")[-1]) # Get the last portion of key
|
||||||
|
|
||||||
|
return int(max_key) + 1
|
||||||
|
|
||||||
|
|
||||||
|
def strip_nondigit(s):
|
||||||
|
return "".join([char for char in s if char.isdigit()])
|
43
main.py
Normal file
43
main.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import etcd
|
||||||
|
|
||||||
|
from helper import check_otp, get_next_id
|
||||||
|
from flask import Flask
|
||||||
|
from flask_restful import Resource, Api, reqparse
|
||||||
|
from decouple import config
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
api = Api(app)
|
||||||
|
|
||||||
|
etcd_client = etcd.Client(host=config("ETCD_HOST"), port=int(config("ETCD_PORT")))
|
||||||
|
|
||||||
|
createvm_argparser = reqparse.RequestParser()
|
||||||
|
createvm_argparser.add_argument("name", type=str, required=True)
|
||||||
|
createvm_argparser.add_argument("realm", type=str, required=True)
|
||||||
|
createvm_argparser.add_argument("seed", type=str, required=True)
|
||||||
|
createvm_argparser.add_argument("specs", type=dict, required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateVM(Resource):
|
||||||
|
def post(self):
|
||||||
|
createvm_args = createvm_argparser.parse_args()
|
||||||
|
name, realm, seed, specs = createvm_args.name, createvm_args.realm,\
|
||||||
|
createvm_args.seed, createvm_args.specs
|
||||||
|
|
||||||
|
if check_otp(name, realm, seed) == 200:
|
||||||
|
# User is good
|
||||||
|
next_vm_id = get_next_id(etcd_client, "/v1/vm/")
|
||||||
|
|
||||||
|
vm_entry = {"owner": name,
|
||||||
|
"specs": specs}
|
||||||
|
|
||||||
|
etcd_client.write(f"/v1/vm/{next_vm_id}", vm_entry)
|
||||||
|
|
||||||
|
return {'message': "VM Created"}, 200
|
||||||
|
else:
|
||||||
|
return {'message': 'Invalid Credentials'}, 400
|
||||||
|
|
||||||
|
|
||||||
|
api.add_resource(CreateVM, '/vm/create')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug=True)
|
Loading…
Reference in a new issue