diff --git a/.gitignore b/.gitignore
index ab6a151..cbb171f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,4 +24,3 @@ venv/
dist/
*.iso
-*.sqlite3
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e468591..afdc4a1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,7 +4,7 @@ stages:
run-tests:
stage: test
- image: code.ungleich.ch:5050/uncloud/uncloud/uncloud-ci:latest
+ image: fedora:latest
services:
- postgres:latest
variables:
@@ -12,7 +12,11 @@ run-tests:
DATABASE_USER: postgres
POSTGRES_HOST_AUTH_METHOD: trust
coverage: /^TOTAL.+?(\d+\%)$/
+ before_script:
+ - dnf install -y python3-devel python3-pip python3-coverage libpq-devel openldap-devel gcc chromium
script:
+ - cd uncloud_django_based/uncloud
- pip install -r requirements.txt
+ - cp uncloud/secrets_sample.py uncloud/secrets.py
- coverage run --source='.' ./manage.py test
- coverage report
diff --git a/README.md b/README.md
index 8c53654..0e32f57 100644
--- a/README.md
+++ b/README.md
@@ -1,62 +1,3 @@
-# Uncloud
+# ucloud
-Cloud management platform, the ungleich way.
-
-
-[](https://code.ungleich.ch/uncloud/uncloud/commits/master)
-[](https://code.ungleich.ch/uncloud/uncloud/commits/master)
-
-## Useful commands
-
-* `./manage.py import-vat-rates path/to/csv`
-* `./manage.py make-admin username`
-
-## Development setup
-
-Install system dependencies:
-
-* On Fedora, you will need the following packages: `python3-virtualenv python3-devel openldap-devel gcc chromium`
-
-NOTE: you will need to configure a LDAP server and credentials for authentication. See `uncloud/settings.py`.
-
-```
-# Initialize virtualenv.
-» virtualenv .venv
-Using base prefix '/usr'
-New python executable in /home/fnux/Workspace/ungleich/uncloud/uncloud/.venv/bin/python3
-Also creating executable in /home/fnux/Workspace/ungleich/uncloud/uncloud/.venv/bin/python
-Installing setuptools, pip, wheel...
-done.
-
-# Enter virtualenv.
-» source .venv/bin/activate
-
-# Install dependencies.
-» pip install -r requirements.txt
-[...]
-
-# Run migrations.
-» ./manage.py migrate
-Operations to perform:
- Apply all migrations: admin, auth, contenttypes, opennebula, sessions, uncloud_auth, uncloud_net, uncloud_pay, uncloud_service, uncloud_vm
-Running migrations:
- [...]
-
-# Run webserver.
-» ./manage.py runserver
-Watching for file changes with StatReloader
-Performing system checks...
-
-System check identified no issues (0 silenced).
-May 07, 2020 - 10:17:08
-Django version 3.0.6, using settings 'uncloud.settings'
-Starting development server at http://127.0.0.1:8000/
-Quit the server with CONTROL-C.
-```
-
-### Note on PGSQL
-
-If you want to use Postgres:
-
-* Install on configure PGSQL on your base system.
-* OR use a container! `podman run --rm -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust -it postgres:latest`
+Checkout https://ungleich.ch/ucloud/ for the documentation of ucloud.
diff --git a/archive/issues.org b/archive/issues.org
deleted file mode 100644
index 840ec3c..0000000
--- a/archive/issues.org
+++ /dev/null
@@ -1,6 +0,0 @@
-* Intro
- This file lists issues that should be handled, are small and likely
- not yet high prio.
-* Issues
-** TODO Register prefered address in User model
-** TODO Allow to specify different recurring periods
diff --git a/archive/uncloud_django_based/hacks/command-wrapper.sh b/archive/uncloud_django_based/hacks/command-wrapper.sh
deleted file mode 100644
index d6ddd13..0000000
--- a/archive/uncloud_django_based/hacks/command-wrapper.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-
-dbhost=$1; shift
-
-ssh -L5432:localhost:5432 "$dbhost" &
-
-python manage.py "$@"
-
-
-
-# command only needs to be active while manage command is running
-
-# -T no pseudo terminal
-
-
-# alternatively: commands output shell code
-
-# ssh uncloud@dbhost "python manage.py --hostname xxx ..."
diff --git a/archive/uncloud_etcd_based/uncloud/hack/hackcloud/nftrules-v2 b/archive/uncloud_etcd_based/uncloud/hack/hackcloud/nftrules-v2
deleted file mode 100644
index b6d4cf3..0000000
--- a/archive/uncloud_etcd_based/uncloud/hack/hackcloud/nftrules-v2
+++ /dev/null
@@ -1,64 +0,0 @@
-flush ruleset
-
-table bridge filter {
- chain prerouting {
- type filter hook prerouting priority 0;
- policy accept;
-
- ibrname br100 jump netpublic
- }
-
- chain netpublic {
- iifname vxlan100 jump from_uncloud
-
- # Default blocks: router advertisements, dhcpv6, dhcpv4
- icmpv6 type nd-router-advert drop
- ip6 version 6 udp sport 547 drop
- ip version 4 udp sport 67 drop
-
- # Individual blocks
-# iifname tap1 jump vm1
- }
-
- chain vm1 {
- ether saddr != 02:00:f0:a9:c4:4e drop
- ip6 saddr != 2a0a:e5c1:111:888:0:f0ff:fea9:c44e drop
- }
-
- chain from_uncloud {
- accept
- }
-}
-
-# table ip6 filter {
-# chain forward {
-# type filter hook forward priority 0;
-
-# # policy drop;
-
-# ct state established,related accept;
-
-# }
-
-# }
-
-# table ip filter {
-# chain input {
-# type filter hook input priority filter; policy drop;
-# iif "lo" accept
-# icmp type { echo-reply, destination-unreachable, source-quench, redirect, echo-request, router-advertisement, router-solicitation, time-exceeded, parameter-problem, timestamp-request, timestamp-reply, info-request, info-reply, address-mask-request, address-mask-reply } accept
-# ct state established,related accept
-# tcp dport { 22 } accept
-# log prefix "firewall-ipv4: "
-# udp sport 67 drop
-# }
-
-# chain forward {
-# type filter hook forward priority filter; policy drop;
-# log prefix "firewall-ipv4: "
-# }
-
-# chain output {
-# type filter hook output priority filter; policy accept;
-# }
-# }
diff --git a/archive/uncloud_etcd_based/uncloud/hack/hackcloud/vm-2.sh b/archive/uncloud_etcd_based/uncloud/hack/hackcloud/vm-2.sh
deleted file mode 100755
index af9dec7..0000000
--- a/archive/uncloud_etcd_based/uncloud/hack/hackcloud/vm-2.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-
-vmid=$1; shift
-
-qemu=/usr/bin/qemu-system-x86_64
-
-accel=kvm
-#accel=tcg
-
-memory=1024
-cores=2
-uuid=732e08c7-84f8-4d43-9571-263db4f80080
-
-export bridge=br100
-
-$qemu -name uc${vmid} \
- -machine pc,accel=${accel} \
- -m ${memory} \
- -smp ${cores} \
- -uuid ${uuid} \
- -drive file=alpine-virt-3.11.2-x86_64.iso,media=cdrom \
- -drive file=alpine-virt-3.11.2-x86_64.iso,media=cdrom \
- -netdev tap,id=netmain,script=./ifup.sh \
- -device virtio-net-pci,netdev=netmain,id=net0,mac=02:00:f0:a9:c4:4e
diff --git a/bin/make-migrations-from-scratch.sh b/bin/make-migrations-from-scratch.sh
deleted file mode 100644
index 8baccfa..0000000
--- a/bin/make-migrations-from-scratch.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-# For undoing/redoing everything
-# Needed in special cases and needs to be avoided as soon as
-# uncloud.version >= 1
-for a in */migrations; do rm ${a}/*.py; done
-for a in */migrations; do python manage.py makemigrations ${a%%/migrations}; done
diff --git a/doc/.gitignore b/doc/.gitignore
deleted file mode 100644
index b51a70d..0000000
--- a/doc/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.pdf
-*.tex
diff --git a/doc/README-billing.org b/doc/README-billing.org
deleted file mode 100644
index 50b26fa..0000000
--- a/doc/README-billing.org
+++ /dev/null
@@ -1,85 +0,0 @@
-* How to handle billing in general
-** Manual test flow / setting up bills
- - Needs orders
- -
-** Orders
- - Orders are the heart of uncloud billing
- - Have a starting date
- - Have an ending date
- - Orders are immutable
- - Can usually not be cancelled / cancellation is not a refund
- - Customer/user commits on a certain period -> gets discount
- based on it
- - Can be upgraded
- - Create a new order
- - We link the new order to the old order and say this one
- replaces it
- - If the price of the new order is HIGHER than the OLD order,
- then we charge the difference until the end of the order period
- - In the next billing run we set the OLD order to not to bill anymore
- - And only the NEW order will be billed afterwards
- - Can be downgraded in the next period (but not for this period)
- - We create a new order, same as for upgrade
- - The new order starts directly after the OLD order
- - As the amount is LOWER than the OLD order, no additional charge is done
- during this order period
- - We might need to have an activate datetime
- - When to implement this
- - Order periods can be
-*** Statuses
- - CREATING/PREPARING
- - INACTIVE (?)
- - TO_BILL
- - NOT_TO_BILL: we use this to accelerate queries to the DB
-*** Updating status of orders
- - If has succeeding order and billing date is last month -> set inactive
-** Bills
- - Are always for a month
- - Can be preliminary
-*** Which orders to include
- - Not the cancelled ones / not active ones
-** Flows / Approach
-*** Finding all orders for a bill
- - Get all orders, state != NOT_TO_BILL; for each order do:
- - is it a one time order?
- - has it a bill assigned?
- - yes: set to NOT_TO_BILL
- - no:
- - get_or_create_bill_for_this_month
- - assign bill to this order
- - set to NOT_TO_BILL
- - is it a recurring order?
- - if it has a REPLACING order:
- -
- - First of month
- - Last of month
-*** Handling replacement of orders
- - The OLD order will appear in the month that it was cancelled on
- the bill
- - The OLD order needs to be set to NOT_TO_BILL after it was billed
- the last time
- - The NEW order will be added pro rata if the amount is higher in
- the same month
- - The NEW order will be used next month
-**** Disabling the old order
- - On billing run
- - If order.replacement_order (naming!) is set
- - if the order.replacement_order starts during THIS_MONTH
- - add order to bill
- - if NOT:
- - the order was already replaced in a previous billing period
- - set the order to NOT_TO_BILL
-**** Billing the new order
- - If order.previous_order
-*** Handling multiple times a recurring order
- - For each recurring order check the order.period
- - Find out when it was billed last
- - lookup latest bill
- - Calculate how many times it has been used until 2359, last day
- of month
- - For preliminary bill: until datetime.now()
- - Call the bill_end_datetime
- - Getting duration: bill_end_datetime - order.last_billed
- - Amount in seconds; duration_in_seconds
- - Divide duration_in_seconds by order.period; amount_used:
- - If >= 1: add amount_used * order.recurring_amount to bill
diff --git a/doc/uncloud-manual-2020-08-01.org b/doc/uncloud-manual-2020-08-01.org
deleted file mode 100644
index 6dd8fb2..0000000
--- a/doc/uncloud-manual-2020-08-01.org
+++ /dev/null
@@ -1,327 +0,0 @@
-* Bootstrap / Installation
-** Pre-requisites by operating system
-*** Alpine
- #+BEGIN_SRC sh
-apk add openldap-dev postgresql-dev libxml2-dev libxslt-dev
-#+END_SRC
-*** Debian/Devuan:
- #+BEGIN_SRC sh
-apt install postgresql-server-dev-all
-#+END_SRC
-** Creating a virtual environment / installing python requirements
-*** Virtual env
- To separate uncloud requirements, you can use a python virtual
- env as follows:
- #+BEGIN_SRC sh
-python3 -m venv venv
-. ./venv/bin/activate
-#+END_SRC
- Then install the requirements
- #+BEGIN_SRC sh
-pip install -r requirements.txt
-#+END_SRC
-** Setting up the the database
-*** Install the database service
- The database can run on the same host as uncloud, but can also run
- a different server. Consult the usual postgresql documentation for
- a secure configuration.
-**** Alpine
- #+BEGIN_SRC sh
-apk add postgresql-server
-rc-update add postgresql
-rc-service postgresql start`
-#+END_SRC
-
-**** Debian/Devuan:
- #+BEGIN_SRC sh
- apt install postgresql
- #+END_SRC
-*** Create the database
- Due to the use of the JSONField, postgresql is required.
- To get started,
- create a database and have it owned by the user that runs uncloud
- (usually "uncloud"):
-
- #+BEGIN_SRC sh
-bridge:~# su - postgres
-bridge:~$ psql
-postgres=# create role uncloud login;
-postgres=# create database uncloud owner nico;
-#+END_SRC
-*** Creating the schema
- #+BEGIN_SRC sh
-python manage.py migrate
-#+END_SRC
-
-** Bootstrap
- - Login via a user so that the user object gets created
- - Run the following (replace nicocustomer with the username)
- #+BEGIN_SRC sh
- python manage.py bootstrap-user --username nicocustomer
- #+END_SRC
-
-** Initialise the database
- While it is not strictly required to add default values to the
- database, it might significantly reduce the starting time with
- uncloud.
-
- To add the default database values run:
-
- #+BEGIN_SRC shell
- # Add local objects
- python manage.py db-add-defaults
-
- # Import VAT rates
- python manage.py import-vat-rates
- #+END_SRC
-
-* Testing / CLI Access
- Access via the commandline (CLI) can be done using curl or
- httpie. In our examples we will use httpie.
-** Checkout out the API
- #+BEGIN_SRC sh
- http localhost:8000/api/
- #+END_SRC
-** Authenticate via ldap user in password store
- #+BEGIN_SRC sh
- http --auth nicocustomer:$(pass ldap/nicocustomer) localhost:8000/api/
- #+END_SRC
-* Database
-** uncloud clients access the data base from a variety of outside hosts
-** So the postgresql data base needs to be remotely accessible
-** Instead of exposing the tcp socket, we make postgresql bind to localhost via IPv6
-*** ::1, port 5432
-** Then we remotely connect to the database server with ssh tunneling
-*** ssh -L5432:localhost:5432 uncloud-database-host
-** Configuring your database for SSH based remote access
-*** host all all ::1/128 trust
-
-* URLs
- - api/ - the rest API
-* uncloud Products
-** Product features
- - Dependencies on other products
- - Minimum parameters (min cpu, min ram, etc).
- - Can also realise the dcl vm
- - dualstack vm = VM + IPv4 + SSD
- - Need to have a non-misguiding name for the "bare VM"
- - Should support network boot (?)
-
-** VPN
-*** How to add a new VPN Host
-**** Install wireguard to the host
-**** Install uncloud to the host
-**** Add `python manage.py vpn --hostname fqdn-of-this-host` to the crontab
-**** Use the CLI to configure one or more VPN Networks for this host
-*** Example of adding a VPN host at ungleich
-**** Create a new dual stack alpine VM
-**** Add it to DNS as vpn-XXX.ungleich.ch
-**** Route a /40 network to its IPv6 address
-**** Install wireguard on it
-**** TODO [#C] Enable wireguard on boot
-**** TODO [#C] Create a new VPNPool on uncloud with
-***** the network address (selecting from our existing pool)
-***** the network size (/...)
-***** the vpn host that provides the network (selecting the created VM)
-***** the wireguard private key of the vpn host (using wg genkey)
-***** http command
- ```
- http -a nicoschottelius:$(pass
- ungleich.ch/nico.schottelius@ungleich.ch)
- http://localhost:8000/admin/vpnpool/ network=2a0a:e5c1:200:: \
- network_size=40 subnetwork_size=48
- vpn_hostname=vpn-2a0ae5c1200.ungleich.ch
- wireguard_private_key=...
- ```
-*** Example http commands / REST calls
-**** creating a new vpn pool
- http -a nicoschottelius:$(pass
- ungleich.ch/nico.schottelius@ungleich.ch)
- http://localhost:8000/admin/vpnpool/ network_size=40
- subnetwork_size=48 network=2a0a:e5c1:200::
- vpn_hostname=vpn-2a0ae5c1200.ungleich.ch wireguard_private_key=$(wg
- genkey)
-**** Creating a new vpn network
-*** Creating a VPN pool
-
- #+BEGIN_SRC sh
-http -a uncloudadmin:$(pass uncloudadmin) https://localhost:8000/v1/admin/vpnpool/ \
- network=2a0a:e5c1:200:: network_size=40 subnetwork_size=48 \
- vpn_hostname=vpn-2a0ae5c1200.ungleich.ch wireguard_private_key=$(wg genkey)
- #+END_SRC
-
-This will create the VPNPool 2a0a:e5c1:200::/40 from which /48
-networks will be used for clients.
-
-VPNPools can only be managed by staff.
-
-*** Managing VPNNetworks
-
-To request a network as a client, use the following call:
-
- #+BEGIN_SRC sh
- http -a user:$(pass user) https://localhost:8000/v1/net/vpn/ \
- network_size=48 \
- wireguard_public_key=$(wg genkey | tee privatekey | wg pubkey)
-```
-
-VPNNetworks can be managed by all authenticated users.
-
-* Developer Handbook
- The following section describe decisions / architecture of
- uncloud. These chapters are intended to be read by developers.
-** Documentation
- This documentation is written in org-mode. To compile it to
- html/pdf, just open emacs and press *C-c C-e l p*.
-** Models
-*** Bill
- Bills are summarising usage in a specific timeframe. Bills usually
- spawn one month.
-*** BillRecord
- Bill records are used to model the usage of one order during the
- timeframe.
-*** Order
- Orders register the intent of a user to buy something. They might
- refer to a product. (???)
- Order register the one time price and the recurring price. These
- fields should be treated as immutable. If they need to be modified,
- a new order that replaces the current order should be created.
-**** Replacing orders
- If an order is updated, a new order is created and points to the
- old order. The old order stops one second before the new order
- starts.
-
- If a order has been replaced can be seen by its replaced_by count:
- #+BEGIN_SRC sh
- >>> Order.objects.get(id=1).replaced_by.count()
- 1
- #+END_SRC
-
-*** Product and Product Children
- - A product describes something a user can buy
- - A product inherits from the uncloud_pay.models.Product model to
- get basic attributes
-** Identifiers
-*** Problem description
- Identifiers can be integers, strings or other objects. They should
- be unique.
-*** Approach 1: integers
- Integers are somewhat easy to remember, but also include
- predictable growth, which might allow access to guessed hacking
- (obivously proper permissions should prevent this).
-*** Approach 2: random uuids
- UUIDs are 128 bit integers. Python supports uuid.uuid4() for random
- uuids.
-*** Approach 3: IPv6 addresses
- uncloud heavily depends on IPv6 in the first place. uncloud could
- use a /48 to identify all objects. Objects that have IPv6 addresses
- on their own, don't need to draw from the system /48.
-**** Possible Subnetworks
- Assuming uncloud uses a /48 to represent all resources.
-
- | Network | Name | Description |
- |-----------------+-----------------+----------------------------------------------|
- | 2001:db8::/48 | uncloud network | All identifiers drawn from here |
- | 2001:db8:1::/64 | VM network | Every VM has an IPv6 address in this network |
- | 2001:db8:2::/64 | Bill network | Every bill has an IPv6 address |
- | 2001:db8:3::/64 | Order network | Every order has an IPv6 address |
- | 2001:db8:5::/64 | Product network | Every product (?) has an IPv6 address |
- | 2001:db8:4::/64 | Disk network | Every disk is identified |
-
-**** Tests
- [15:47:37] black3.place6:~# rbd create -s 10G ssd/2a0a:e5c0:1::8
-
-*** Decision
- We use integers, because they are easy.
-
-** Milestones :uncloud:
-*** 1.1 (cleanup 1)
-**** TODO Unify ValidationError, FieldError - define proper Exception
- - What do we use for model errors
-*** 1.0 (initial release)
-**** TODO Initial Generic product support
- - Product
-***** TODO Recurring product support
-****** TODO Support replacing orders for updates
-****** DONE [#A] Finish split of bill creation
- CLOSED: [2020-09-11 Fri 23:19]
-****** TODO Test the new functions in the Order class
-****** Define the correct order replacement logic
- Assumption:
- - recurringperiods are 30days
-******* Case 1: downgrading
- - User commits to 10 CHF for 30 days
- - Wants to downgrade after 15 days to 5 CHF product
- - Expected result:
- - order 1: 10 CHF until +30days
- - order 2: 5 CHF starting 30days + 1s
- - Sum of the two orders is 15 CHF
- - Question is
- - when is the VM shutdown?
- - a) instantly
- - b) at the end of the cycle
- - best solution
- - user can choose between a ... b any time
-******* Duration
- - You cannot cancel the duration
- - You can upgrade and with that cancel the duration
- - The idea of a duration is that you commit for it
- - If you want to commit lower (daily basis for instance) you
- have higher per period prices
-******* Case X
- - User has VM with 2 Core / 2 GB RAM
- - User modifies with to 1 core / 3 GB RAM
- - We treat it as down/upgrade independent of the modifications
-
-******* Case 2: upgrading after 1 day
- - committed for 30 days
- - upgrade after 1 day
- - so first order will be charged for 1/30ths
-
-******* Case 2: upgrading
- - User commits to 10 CHF for 30 days
- - Wants to upgrade after 15 days to 20 CHF product
- - Order 1 : 1 VM with 2 Core / 2 GB / 10 SSD -- 10 CHF
- - 30days period, stopped after 15, so quantity is 0.5 = 5 CHF
- - Order 2 : 1 VM with 2 Core / 6 GB / 10 SSD -- 20 CHF
- - after 15 days
- - VM is upgraded instantly
- - Expected result:
- - order 1: 10 CHF until +15days = 0.5 units = 5 CHF
- - order 2: 20 CHF starting 15days + 1s ... +30 days after
- the 15 days -> 45 days = 1 unit = 20 CHF
- - Total on bill: 25 CHF
-
-******* Case 2: upgrading
- - User commits to 10 CHF for 30 days
- - Wants to upgrade after 15 days to 20 CHF product
- - Expected result:
- - order 1: 10 CHF until +30days = 1 units = 10 CHF
-
- - order 2: 20 CHF starting 15days + 1s = 1 unit = 20 CHF
- - Total on bill: 30 CHF
-
-
-****** TODO Note: ending date not set if replaced by default (implicit!)
- - Should the new order modify the old order on save()?
-****** DONE Fix totally wrong bill dates in our test case
- CLOSED: [2020-09-09 Wed 01:00]
- - 2020 used instead of 2019
- - Was due to existing test data ...
-***** TODO Bill logic is still wrong
- - Bill starting_date is the date of the first order
- - However first encountered order does not have to be the
- earliest in the bill!
- - Bills should not have a duration
- - Bills should only have a (unique) issue date
- - We charge based on bill_records
- - Last time charged issue date of the bill OR earliest date
- after that
- - Every bill generation checks all (relevant) orders
- - add a flag "not_for_billing" or "closed"
- - query on that flag
- - verify it every time
-
-
-***** TODO Generating bill for admins/staff
- -
diff --git a/opennebula/migrations/0002_auto_20200801_2332.py b/opennebula/migrations/0002_auto_20200801_2332.py
deleted file mode 100644
index 1aa6d34..0000000
--- a/opennebula/migrations/0002_auto_20200801_2332.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 3.0.8 on 2020-08-01 23:32
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('uncloud_pay', '0003_auto_20200801_2332'),
- ('opennebula', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='vm',
- name='order',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order'),
- ),
- ]
diff --git a/opennebula/migrations/0003_auto_20200808_1953.py b/opennebula/migrations/0003_auto_20200808_1953.py
deleted file mode 100644
index 218b9a7..0000000
--- a/opennebula/migrations/0003_auto_20200808_1953.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 3.1 on 2020-08-08 19:53
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('opennebula', '0002_auto_20200801_2332'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='vm',
- name='data',
- field=models.JSONField(),
- ),
- migrations.AlterField(
- model_name='vm',
- name='extra_data',
- field=models.JSONField(blank=True, editable=False, null=True),
- ),
- ]
diff --git a/opennebula/migrations/0006_auto_20200928_1858.py b/opennebula/migrations/0006_auto_20200928_1858.py
deleted file mode 100644
index 49da56f..0000000
--- a/opennebula/migrations/0006_auto_20200928_1858.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 3.1 on 2020-09-28 18:58
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('opennebula', '0005_remove_vm_orders'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='vm',
- name='extra_data',
- ),
- migrations.RemoveField(
- model_name='vm',
- name='owner',
- ),
- migrations.RemoveField(
- model_name='vm',
- name='status',
- ),
- ]
diff --git a/opennebula/views.py b/opennebula/views.py
deleted file mode 100644
index 688f0b4..0000000
--- a/opennebula/views.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from rest_framework import viewsets, permissions
-
-#from .models import VM
-# from .serializers import OpenNebulaVMSerializer
-
-# class VMViewSet(viewsets.ModelViewSet):
-# permission_classes = [permissions.IsAuthenticated]
-# serializer_class = OpenNebulaVMSerializer
-
-# def get_queryset(self):
-# if self.request.user.is_superuser:
-# obj = VM.objects.all()
-# else:
-# obj = VM.objects.filter(owner=self.request.user)
-
-# return obj
diff --git a/resources/ci/.lock b/resources/ci/.lock
deleted file mode 100644
index e69de29..0000000
diff --git a/resources/ci/Dockerfile b/resources/ci/Dockerfile
deleted file mode 100644
index 020b66e..0000000
--- a/resources/ci/Dockerfile
+++ /dev/null
@@ -1,3 +0,0 @@
-FROM fedora:latest
-
-RUN dnf install -y python3-devel python3-pip python3-coverage libpq-devel openldap-devel gcc chromium
diff --git a/uncloud/.gitignore b/uncloud/.gitignore
deleted file mode 100644
index 6a07bff..0000000
--- a/uncloud/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-local_settings.py
diff --git a/uncloud/__init__.py b/uncloud/__init__.py
deleted file mode 100644
index 2676f97..0000000
--- a/uncloud/__init__.py
+++ /dev/null
@@ -1,243 +0,0 @@
-from django.utils.translation import gettext_lazy as _
-
-# http://xml.coverpages.org/country3166.html
-COUNTRIES = (
- ('AD', _('Andorra')),
- ('AE', _('United Arab Emirates')),
- ('AF', _('Afghanistan')),
- ('AG', _('Antigua & Barbuda')),
- ('AI', _('Anguilla')),
- ('AL', _('Albania')),
- ('AM', _('Armenia')),
- ('AN', _('Netherlands Antilles')),
- ('AO', _('Angola')),
- ('AQ', _('Antarctica')),
- ('AR', _('Argentina')),
- ('AS', _('American Samoa')),
- ('AT', _('Austria')),
- ('AU', _('Australia')),
- ('AW', _('Aruba')),
- ('AZ', _('Azerbaijan')),
- ('BA', _('Bosnia and Herzegovina')),
- ('BB', _('Barbados')),
- ('BD', _('Bangladesh')),
- ('BE', _('Belgium')),
- ('BF', _('Burkina Faso')),
- ('BG', _('Bulgaria')),
- ('BH', _('Bahrain')),
- ('BI', _('Burundi')),
- ('BJ', _('Benin')),
- ('BM', _('Bermuda')),
- ('BN', _('Brunei Darussalam')),
- ('BO', _('Bolivia')),
- ('BR', _('Brazil')),
- ('BS', _('Bahama')),
- ('BT', _('Bhutan')),
- ('BV', _('Bouvet Island')),
- ('BW', _('Botswana')),
- ('BY', _('Belarus')),
- ('BZ', _('Belize')),
- ('CA', _('Canada')),
- ('CC', _('Cocos (Keeling) Islands')),
- ('CF', _('Central African Republic')),
- ('CG', _('Congo')),
- ('CH', _('Switzerland')),
- ('CI', _('Ivory Coast')),
- ('CK', _('Cook Iislands')),
- ('CL', _('Chile')),
- ('CM', _('Cameroon')),
- ('CN', _('China')),
- ('CO', _('Colombia')),
- ('CR', _('Costa Rica')),
- ('CU', _('Cuba')),
- ('CV', _('Cape Verde')),
- ('CX', _('Christmas Island')),
- ('CY', _('Cyprus')),
- ('CZ', _('Czech Republic')),
- ('DE', _('Germany')),
- ('DJ', _('Djibouti')),
- ('DK', _('Denmark')),
- ('DM', _('Dominica')),
- ('DO', _('Dominican Republic')),
- ('DZ', _('Algeria')),
- ('EC', _('Ecuador')),
- ('EE', _('Estonia')),
- ('EG', _('Egypt')),
- ('EH', _('Western Sahara')),
- ('ER', _('Eritrea')),
- ('ES', _('Spain')),
- ('ET', _('Ethiopia')),
- ('FI', _('Finland')),
- ('FJ', _('Fiji')),
- ('FK', _('Falkland Islands (Malvinas)')),
- ('FM', _('Micronesia')),
- ('FO', _('Faroe Islands')),
- ('FR', _('France')),
- ('FX', _('France, Metropolitan')),
- ('GA', _('Gabon')),
- ('GB', _('United Kingdom (Great Britain)')),
- ('GD', _('Grenada')),
- ('GE', _('Georgia')),
- ('GF', _('French Guiana')),
- ('GH', _('Ghana')),
- ('GI', _('Gibraltar')),
- ('GL', _('Greenland')),
- ('GM', _('Gambia')),
- ('GN', _('Guinea')),
- ('GP', _('Guadeloupe')),
- ('GQ', _('Equatorial Guinea')),
- ('GR', _('Greece')),
- ('GS', _('South Georgia and the South Sandwich Islands')),
- ('GT', _('Guatemala')),
- ('GU', _('Guam')),
- ('GW', _('Guinea-Bissau')),
- ('GY', _('Guyana')),
- ('HK', _('Hong Kong')),
- ('HM', _('Heard & McDonald Islands')),
- ('HN', _('Honduras')),
- ('HR', _('Croatia')),
- ('HT', _('Haiti')),
- ('HU', _('Hungary')),
- ('ID', _('Indonesia')),
- ('IE', _('Ireland')),
- ('IL', _('Israel')),
- ('IN', _('India')),
- ('IO', _('British Indian Ocean Territory')),
- ('IQ', _('Iraq')),
- ('IR', _('Islamic Republic of Iran')),
- ('IS', _('Iceland')),
- ('IT', _('Italy')),
- ('JM', _('Jamaica')),
- ('JO', _('Jordan')),
- ('JP', _('Japan')),
- ('KE', _('Kenya')),
- ('KG', _('Kyrgyzstan')),
- ('KH', _('Cambodia')),
- ('KI', _('Kiribati')),
- ('KM', _('Comoros')),
- ('KN', _('St. Kitts and Nevis')),
- ('KP', _('Korea, Democratic People\'s Republic of')),
- ('KR', _('Korea, Republic of')),
- ('KW', _('Kuwait')),
- ('KY', _('Cayman Islands')),
- ('KZ', _('Kazakhstan')),
- ('LA', _('Lao People\'s Democratic Republic')),
- ('LB', _('Lebanon')),
- ('LC', _('Saint Lucia')),
- ('LI', _('Liechtenstein')),
- ('LK', _('Sri Lanka')),
- ('LR', _('Liberia')),
- ('LS', _('Lesotho')),
- ('LT', _('Lithuania')),
- ('LU', _('Luxembourg')),
- ('LV', _('Latvia')),
- ('LY', _('Libyan Arab Jamahiriya')),
- ('MA', _('Morocco')),
- ('MC', _('Monaco')),
- ('MD', _('Moldova, Republic of')),
- ('MG', _('Madagascar')),
- ('MH', _('Marshall Islands')),
- ('ML', _('Mali')),
- ('MN', _('Mongolia')),
- ('MM', _('Myanmar')),
- ('MO', _('Macau')),
- ('MP', _('Northern Mariana Islands')),
- ('MQ', _('Martinique')),
- ('MR', _('Mauritania')),
- ('MS', _('Monserrat')),
- ('MT', _('Malta')),
- ('MU', _('Mauritius')),
- ('MV', _('Maldives')),
- ('MW', _('Malawi')),
- ('MX', _('Mexico')),
- ('MY', _('Malaysia')),
- ('MZ', _('Mozambique')),
- ('NA', _('Namibia')),
- ('NC', _('New Caledonia')),
- ('NE', _('Niger')),
- ('NF', _('Norfolk Island')),
- ('NG', _('Nigeria')),
- ('NI', _('Nicaragua')),
- ('NL', _('Netherlands')),
- ('NO', _('Norway')),
- ('NP', _('Nepal')),
- ('NR', _('Nauru')),
- ('NU', _('Niue')),
- ('NZ', _('New Zealand')),
- ('OM', _('Oman')),
- ('PA', _('Panama')),
- ('PE', _('Peru')),
- ('PF', _('French Polynesia')),
- ('PG', _('Papua New Guinea')),
- ('PH', _('Philippines')),
- ('PK', _('Pakistan')),
- ('PL', _('Poland')),
- ('PM', _('St. Pierre & Miquelon')),
- ('PN', _('Pitcairn')),
- ('PR', _('Puerto Rico')),
- ('PT', _('Portugal')),
- ('PW', _('Palau')),
- ('PY', _('Paraguay')),
- ('QA', _('Qatar')),
- ('RE', _('Reunion')),
- ('RO', _('Romania')),
- ('RU', _('Russian Federation')),
- ('RW', _('Rwanda')),
- ('SA', _('Saudi Arabia')),
- ('SB', _('Solomon Islands')),
- ('SC', _('Seychelles')),
- ('SD', _('Sudan')),
- ('SE', _('Sweden')),
- ('SG', _('Singapore')),
- ('SH', _('St. Helena')),
- ('SI', _('Slovenia')),
- ('SJ', _('Svalbard & Jan Mayen Islands')),
- ('SK', _('Slovakia')),
- ('SL', _('Sierra Leone')),
- ('SM', _('San Marino')),
- ('SN', _('Senegal')),
- ('SO', _('Somalia')),
- ('SR', _('Suriname')),
- ('ST', _('Sao Tome & Principe')),
- ('SV', _('El Salvador')),
- ('SY', _('Syrian Arab Republic')),
- ('SZ', _('Swaziland')),
- ('TC', _('Turks & Caicos Islands')),
- ('TD', _('Chad')),
- ('TF', _('French Southern Territories')),
- ('TG', _('Togo')),
- ('TH', _('Thailand')),
- ('TJ', _('Tajikistan')),
- ('TK', _('Tokelau')),
- ('TM', _('Turkmenistan')),
- ('TN', _('Tunisia')),
- ('TO', _('Tonga')),
- ('TP', _('East Timor')),
- ('TR', _('Turkey')),
- ('TT', _('Trinidad & Tobago')),
- ('TV', _('Tuvalu')),
- ('TW', _('Taiwan, Province of China')),
- ('TZ', _('Tanzania, United Republic of')),
- ('UA', _('Ukraine')),
- ('UG', _('Uganda')),
- ('UM', _('United States Minor Outlying Islands')),
- ('US', _('United States of America')),
- ('UY', _('Uruguay')),
- ('UZ', _('Uzbekistan')),
- ('VA', _('Vatican City State (Holy See)')),
- ('VC', _('St. Vincent & the Grenadines')),
- ('VE', _('Venezuela')),
- ('VG', _('British Virgin Islands')),
- ('VI', _('United States Virgin Islands')),
- ('VN', _('Viet Nam')),
- ('VU', _('Vanuatu')),
- ('WF', _('Wallis & Futuna Islands')),
- ('WS', _('Samoa')),
- ('YE', _('Yemen')),
- ('YT', _('Mayotte')),
- ('YU', _('Yugoslavia')),
- ('ZA', _('South Africa')),
- ('ZM', _('Zambia')),
- ('ZR', _('Zaire')),
- ('ZW', _('Zimbabwe')),
-)
diff --git a/uncloud/admin.py b/uncloud/admin.py
deleted file mode 100644
index 4ecc53a..0000000
--- a/uncloud/admin.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from django.contrib import admin
-
-from .models import UncloudProvider, UncloudNetwork
-
-for m in [ UncloudProvider, UncloudNetwork ]:
- admin.site.register(m)
diff --git a/uncloud/management/commands/db-add-defaults.py b/uncloud/management/commands/db-add-defaults.py
deleted file mode 100644
index 605c8f5..0000000
--- a/uncloud/management/commands/db-add-defaults.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import random
-import string
-
-from django.core.management.base import BaseCommand
-from django.core.exceptions import ObjectDoesNotExist
-from django.contrib.auth import get_user_model
-from django.conf import settings
-
-from uncloud_pay.models import BillingAddress, RecurringPeriod, Product
-from uncloud.models import UncloudProvider, UncloudNetwork
-
-
-class Command(BaseCommand):
- help = 'Add standard uncloud values'
-
- def add_arguments(self, parser):
- pass
-
- def handle(self, *args, **options):
- # Order matters, objects can be dependent on each other
-
- admin_username="uncloud-admin"
- pw_length = 32
-
- # Only set password if the user did not exist before
- try:
- admin_user = get_user_model().objects.get(username=settings.UNCLOUD_ADMIN_NAME)
- except ObjectDoesNotExist:
- random_password = ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(pw_length))
-
- admin_user = get_user_model().objects.create_user(username=settings.UNCLOUD_ADMIN_NAME, password=random_password)
- admin_user.is_superuser=True
- admin_user.is_staff=True
- admin_user.save()
-
- print(f"Created admin user '{admin_username}' with password '{random_password}'")
-
- BillingAddress.populate_db_defaults()
- RecurringPeriod.populate_db_defaults()
- Product.populate_db_defaults()
-
- UncloudNetwork.populate_db_defaults()
- UncloudProvider.populate_db_defaults()
diff --git a/uncloud/migrations/0001_initial.py b/uncloud/migrations/0001_initial.py
deleted file mode 100644
index 8753d29..0000000
--- a/uncloud/migrations/0001_initial.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 3.1 on 2020-10-11 19:59
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ]
-
- operations = [
- migrations.CreateModel(
- name='UncloudProvider',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('starting_date', models.DateField()),
- ('ending_date', models.DateField(blank=True)),
- ('name', models.CharField(max_length=256)),
- ('address', models.TextField()),
- ],
- ),
- ]
diff --git a/uncloud/migrations/0002_auto_20201011_2001.py b/uncloud/migrations/0002_auto_20201011_2001.py
deleted file mode 100644
index 16b3f60..0000000
--- a/uncloud/migrations/0002_auto_20201011_2001.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-10-11 20:01
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('uncloud', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='uncloudprovider',
- name='ending_date',
- field=models.DateField(blank=True, null=True),
- ),
- ]
diff --git a/uncloud/migrations/0003_auto_20201011_2009.py b/uncloud/migrations/0003_auto_20201011_2009.py
deleted file mode 100644
index 9aee763..0000000
--- a/uncloud/migrations/0003_auto_20201011_2009.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 3.1 on 2020-10-11 20:09
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('uncloud_net', '0010_auto_20201011_2009'),
- ('uncloud', '0002_auto_20201011_2001'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='uncloudprovider',
- name='billing_network',
- field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='uncloudproviderbill', to='uncloud_net.uncloudnetwork'),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='uncloudprovider',
- name='referral_network',
- field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='uncloudproviderreferral', to='uncloud_net.uncloudnetwork'),
- preserve_default=False,
- ),
- ]
diff --git a/uncloud/migrations/0004_auto_20201011_2031.py b/uncloud/migrations/0004_auto_20201011_2031.py
deleted file mode 100644
index 3b53b7f..0000000
--- a/uncloud/migrations/0004_auto_20201011_2031.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Generated by Django 3.1 on 2020-10-11 20:31
-
-from django.db import migrations, models
-import uncloud.models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('uncloud', '0003_auto_20201011_2009'),
- ]
-
- operations = [
- migrations.RenameField(
- model_name='uncloudprovider',
- old_name='name',
- new_name='full_name',
- ),
- migrations.RemoveField(
- model_name='uncloudprovider',
- name='address',
- ),
- migrations.AddField(
- model_name='uncloudprovider',
- name='city',
- field=models.CharField(default='', max_length=256),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='uncloudprovider',
- name='country',
- field=uncloud.models.CountryField(blank=True, choices=[('AD', 'Andorra'), ('AE', 'United Arab Emirates'), ('AF', 'Afghanistan'), ('AG', 'Antigua & Barbuda'), ('AI', 'Anguilla'), ('AL', 'Albania'), ('AM', 'Armenia'), ('AN', 'Netherlands Antilles'), ('AO', 'Angola'), ('AQ', 'Antarctica'), ('AR', 'Argentina'), ('AS', 'American Samoa'), ('AT', 'Austria'), ('AU', 'Australia'), ('AW', 'Aruba'), ('AZ', 'Azerbaijan'), ('BA', 'Bosnia and Herzegovina'), ('BB', 'Barbados'), ('BD', 'Bangladesh'), ('BE', 'Belgium'), ('BF', 'Burkina Faso'), ('BG', 'Bulgaria'), ('BH', 'Bahrain'), ('BI', 'Burundi'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BN', 'Brunei Darussalam'), ('BO', 'Bolivia'), ('BR', 'Brazil'), ('BS', 'Bahama'), ('BT', 'Bhutan'), ('BV', 'Bouvet Island'), ('BW', 'Botswana'), ('BY', 'Belarus'), ('BZ', 'Belize'), ('CA', 'Canada'), ('CC', 'Cocos (Keeling) Islands'), ('CF', 'Central African Republic'), ('CG', 'Congo'), ('CH', 'Switzerland'), ('CI', 'Ivory Coast'), ('CK', 'Cook Iislands'), ('CL', 'Chile'), ('CM', 'Cameroon'), ('CN', 'China'), ('CO', 'Colombia'), ('CR', 'Costa Rica'), ('CU', 'Cuba'), ('CV', 'Cape Verde'), ('CX', 'Christmas Island'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DE', 'Germany'), ('DJ', 'Djibouti'), ('DK', 'Denmark'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('DZ', 'Algeria'), ('EC', 'Ecuador'), ('EE', 'Estonia'), ('EG', 'Egypt'), ('EH', 'Western Sahara'), ('ER', 'Eritrea'), ('ES', 'Spain'), ('ET', 'Ethiopia'), ('FI', 'Finland'), ('FJ', 'Fiji'), ('FK', 'Falkland Islands (Malvinas)'), ('FM', 'Micronesia'), ('FO', 'Faroe Islands'), ('FR', 'France'), ('FX', 'France, Metropolitan'), ('GA', 'Gabon'), ('GB', 'United Kingdom (Great Britain)'), ('GD', 'Grenada'), ('GE', 'Georgia'), ('GF', 'French Guiana'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GL', 'Greenland'), ('GM', 'Gambia'), ('GN', 'Guinea'), ('GP', 'Guadeloupe'), ('GQ', 'Equatorial Guinea'), ('GR', 'Greece'), ('GS', 'South Georgia and the South Sandwich Islands'), ('GT', 'Guatemala'), ('GU', 'Guam'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HK', 'Hong Kong'), ('HM', 'Heard & McDonald Islands'), ('HN', 'Honduras'), ('HR', 'Croatia'), ('HT', 'Haiti'), ('HU', 'Hungary'), ('ID', 'Indonesia'), ('IE', 'Ireland'), ('IL', 'Israel'), ('IN', 'India'), ('IO', 'British Indian Ocean Territory'), ('IQ', 'Iraq'), ('IR', 'Islamic Republic of Iran'), ('IS', 'Iceland'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JO', 'Jordan'), ('JP', 'Japan'), ('KE', 'Kenya'), ('KG', 'Kyrgyzstan'), ('KH', 'Cambodia'), ('KI', 'Kiribati'), ('KM', 'Comoros'), ('KN', 'St. Kitts and Nevis'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KY', 'Cayman Islands'), ('KZ', 'Kazakhstan'), ('LA', "Lao People's Democratic Republic"), ('LB', 'Lebanon'), ('LC', 'Saint Lucia'), ('LI', 'Liechtenstein'), ('LK', 'Sri Lanka'), ('LR', 'Liberia'), ('LS', 'Lesotho'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('LV', 'Latvia'), ('LY', 'Libyan Arab Jamahiriya'), ('MA', 'Morocco'), ('MC', 'Monaco'), ('MD', 'Moldova, Republic of'), ('MG', 'Madagascar'), ('MH', 'Marshall Islands'), ('ML', 'Mali'), ('MN', 'Mongolia'), ('MM', 'Myanmar'), ('MO', 'Macau'), ('MP', 'Northern Mariana Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MS', 'Monserrat'), ('MT', 'Malta'), ('MU', 'Mauritius'), ('MV', 'Maldives'), ('MW', 'Malawi'), ('MX', 'Mexico'), ('MY', 'Malaysia'), ('MZ', 'Mozambique'), ('NA', 'Namibia'), ('NC', 'New Caledonia'), ('NE', 'Niger'), ('NF', 'Norfolk Island'), ('NG', 'Nigeria'), ('NI', 'Nicaragua'), ('NL', 'Netherlands'), ('NO', 'Norway'), ('NP', 'Nepal'), ('NR', 'Nauru'), ('NU', 'Niue'), ('NZ', 'New Zealand'), ('OM', 'Oman'), ('PA', 'Panama'), ('PE', 'Peru'), ('PF', 'French Polynesia'), ('PG', 'Papua New Guinea'), ('PH', 'Philippines'), ('PK', 'Pakistan'), ('PL', 'Poland'), ('PM', 'St. Pierre & Miquelon'), ('PN', 'Pitcairn'), ('PR', 'Puerto Rico'), ('PT', 'Portugal'), ('PW', 'Palau'), ('PY', 'Paraguay'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('SA', 'Saudi Arabia'), ('SB', 'Solomon Islands'), ('SC', 'Seychelles'), ('SD', 'Sudan'), ('SE', 'Sweden'), ('SG', 'Singapore'), ('SH', 'St. Helena'), ('SI', 'Slovenia'), ('SJ', 'Svalbard & Jan Mayen Islands'), ('SK', 'Slovakia'), ('SL', 'Sierra Leone'), ('SM', 'San Marino'), ('SN', 'Senegal'), ('SO', 'Somalia'), ('SR', 'Suriname'), ('ST', 'Sao Tome & Principe'), ('SV', 'El Salvador'), ('SY', 'Syrian Arab Republic'), ('SZ', 'Swaziland'), ('TC', 'Turks & Caicos Islands'), ('TD', 'Chad'), ('TF', 'French Southern Territories'), ('TG', 'Togo'), ('TH', 'Thailand'), ('TJ', 'Tajikistan'), ('TK', 'Tokelau'), ('TM', 'Turkmenistan'), ('TN', 'Tunisia'), ('TO', 'Tonga'), ('TP', 'East Timor'), ('TR', 'Turkey'), ('TT', 'Trinidad & Tobago'), ('TV', 'Tuvalu'), ('TW', 'Taiwan, Province of China'), ('TZ', 'Tanzania, United Republic of'), ('UA', 'Ukraine'), ('UG', 'Uganda'), ('UM', 'United States Minor Outlying Islands'), ('US', 'United States of America'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VA', 'Vatican City State (Holy See)'), ('VC', 'St. Vincent & the Grenadines'), ('VE', 'Venezuela'), ('VG', 'British Virgin Islands'), ('VI', 'United States Virgin Islands'), ('VN', 'Viet Nam'), ('VU', 'Vanuatu'), ('WF', 'Wallis & Futuna Islands'), ('WS', 'Samoa'), ('YE', 'Yemen'), ('YT', 'Mayotte'), ('YU', 'Yugoslavia'), ('ZA', 'South Africa'), ('ZM', 'Zambia'), ('ZR', 'Zaire'), ('ZW', 'Zimbabwe')], default='CH', max_length=2),
- ),
- migrations.AddField(
- model_name='uncloudprovider',
- name='organization',
- field=models.CharField(blank=True, max_length=256, null=True),
- ),
- migrations.AddField(
- model_name='uncloudprovider',
- name='postal_code',
- field=models.CharField(default='', max_length=64),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='uncloudprovider',
- name='street',
- field=models.CharField(default='', max_length=256),
- preserve_default=False,
- ),
- ]
diff --git a/uncloud/migrations/0005_uncloudprovider_coupon_network.py b/uncloud/migrations/0005_uncloudprovider_coupon_network.py
deleted file mode 100644
index b74b878..0000000
--- a/uncloud/migrations/0005_uncloudprovider_coupon_network.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 3.1 on 2020-10-12 17:32
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('uncloud_net', '0010_auto_20201011_2009'),
- ('uncloud', '0004_auto_20201011_2031'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='uncloudprovider',
- name='coupon_network',
- field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='uncloudprovidercoupon', to='uncloud_net.uncloudnetwork'),
- preserve_default=False,
- ),
- ]
diff --git a/uncloud/migrations/0006_auto_20201025_1931.py b/uncloud/migrations/0006_auto_20201025_1931.py
deleted file mode 100644
index d1162ef..0000000
--- a/uncloud/migrations/0006_auto_20201025_1931.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Generated by Django 3.1 on 2020-10-25 19:31
-
-import django.core.validators
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('uncloud', '0005_uncloudprovider_coupon_network'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='UncloudNetwork',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('network_address', models.GenericIPAddressField(unique=True)),
- ('network_mask', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(128)])),
- ('description', models.CharField(max_length=256)),
- ],
- ),
- migrations.AlterField(
- model_name='uncloudprovider',
- name='billing_network',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='uncloudproviderbill', to='uncloud.uncloudnetwork'),
- ),
- migrations.AlterField(
- model_name='uncloudprovider',
- name='coupon_network',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='uncloudprovidercoupon', to='uncloud.uncloudnetwork'),
- ),
- migrations.AlterField(
- model_name='uncloudprovider',
- name='referral_network',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='uncloudproviderreferral', to='uncloud.uncloudnetwork'),
- ),
- ]
-2
diff --git a/uncloud/models.py b/uncloud/models.py
deleted file mode 100644
index 5a65f1c..0000000
--- a/uncloud/models.py
+++ /dev/null
@@ -1,163 +0,0 @@
-from django.db import models
-from django.db.models import JSONField, Q
-from django.utils import timezone
-from django.utils.translation import gettext_lazy as _
-from django.core.validators import MinValueValidator, MaxValueValidator
-
-from uncloud import COUNTRIES
-
-class UncloudModel(models.Model):
- """
- This class extends the standard model with an
- extra_data field that can be used to include public,
- but internal information.
-
- For instance if you migrate from an existing virtualisation
- framework to uncloud.
-
- The extra_data attribute should be considered a hack and whenever
- data is necessary for running uncloud, it should **not** be stored
- in there.
-
- """
-
- extra_data = JSONField(editable=False, blank=True, null=True)
-
- class Meta:
- abstract = True
-
-# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
-class UncloudStatus(models.TextChoices):
- PENDING = 'PENDING', _('Pending')
- AWAITING_PAYMENT = 'AWAITING_PAYMENT', _('Awaiting payment')
- BEING_CREATED = 'BEING_CREATED', _('Being created')
- SCHEDULED = 'SCHEDULED', _('Scheduled') # resource selected, waiting for dispatching
- ACTIVE = 'ACTIVE', _('Active')
- MODIFYING = 'MODIFYING', _('Modifying') # Resource is being changed
- DELETED = 'DELETED', _('Deleted') # Resource has been deleted
- DISABLED = 'DISABLED', _('Disabled') # Is usable, but cannot be used for new things
- UNUSABLE = 'UNUSABLE', _('Unusable'), # Has some kind of error
-
-
-
-###
-# General address handling
-class CountryField(models.CharField):
- def __init__(self, *args, **kwargs):
- kwargs.setdefault('choices', COUNTRIES)
- kwargs.setdefault('default', 'CH')
- kwargs.setdefault('max_length', 2)
-
- super().__init__(*args, **kwargs)
-
- def get_internal_type(self):
- return "CharField"
-
-
-class UncloudAddress(models.Model):
- full_name = models.CharField(max_length=256)
- organization = models.CharField(max_length=256, blank=True, null=True)
- street = models.CharField(max_length=256)
- city = models.CharField(max_length=256)
- postal_code = models.CharField(max_length=64)
- country = CountryField(blank=True)
-
- class Meta:
- abstract = True
-
-
-###
-# UncloudNetworks are used as identifiers - such they are a base of uncloud
-
-class UncloudNetwork(models.Model):
- """
- Storing IP networks
- """
-
- network_address = models.GenericIPAddressField(null=False, unique=True)
- network_mask = models.IntegerField(null=False,
- validators=[MinValueValidator(0),
- MaxValueValidator(128)]
- )
-
- description = models.CharField(max_length=256)
-
- @classmethod
- def populate_db_defaults(cls):
- for net, desc in [
- ( "2a0a:e5c0:11::", "uncloud Billing" ),
- ( "2a0a:e5c0:11:1::", "uncloud Referral" ),
- ( "2a0a:e5c0:11:2::", "uncloud Coupon" )
- ]:
- obj, created = cls.objects.get_or_create(network_address=net,
- defaults= {
- 'network_mask': 64,
- 'description': desc
- }
- )
-
-
- def save(self, *args, **kwargs):
- if not ':' in self.network_address and self.network_mask > 32:
- raise FieldError("Mask cannot exceed 32 for IPv4")
-
- super().save(*args, **kwargs)
-
-
- def __str__(self):
- return f"{self.network_address}/{self.network_mask} {self.description}"
-
-###
-# Who is running / providing this instance of uncloud?
-
-class UncloudProvider(UncloudAddress):
- """
- A class resembling who is running this uncloud instance.
- This might change over time so we allow starting/ending dates
-
- This also defines the taxation rules.
-
- starting/ending date define from when to when this is valid. This way
- we can model address changes and have it correct in the bills.
- """
-
- # Meta:
- # FIXMe: only allow non overlapping time frames -- how to define this as a constraint?
- starting_date = models.DateField()
- ending_date = models.DateField(blank=True, null=True)
-
- billing_network = models.ForeignKey(UncloudNetwork, related_name="uncloudproviderbill", on_delete=models.CASCADE)
- referral_network = models.ForeignKey(UncloudNetwork, related_name="uncloudproviderreferral", on_delete=models.CASCADE)
- coupon_network = models.ForeignKey(UncloudNetwork, related_name="uncloudprovidercoupon", on_delete=models.CASCADE)
-
-
- @classmethod
- def get_provider(cls, when=None):
- """
- Find active provide at a certain time - if there was any
- """
-
- if not when:
- when = timezone.now()
-
-
- return cls.objects.get(Q(starting_date__gte=when, ending_date__lte=when) |
- Q(starting_date__gte=when, ending_date__isnull=True))
-
-
- @classmethod
- def populate_db_defaults(cls):
- obj, created = cls.objects.get_or_create(full_name="ungleich glarus ag",
- street="Bahnhofstrasse 1",
- postal_code="8783",
- city="Linthal",
- country="CH",
- starting_date=timezone.now(),
- billing_network=UncloudNetwork.objects.get(description="uncloud Billing"),
- referral_network=UncloudNetwork.objects.get(description="uncloud Referral"),
- coupon_network=UncloudNetwork.objects.get(description="uncloud Coupon")
- )
-
-
- def __str__(self):
- return f"{self.full_name} {self.country}"
diff --git a/uncloud/templates/uncloud/base.html b/uncloud/templates/uncloud/base.html
deleted file mode 100644
index 034fa7c..0000000
--- a/uncloud/templates/uncloud/base.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- {% block title %}Welcome to uncloud{% endblock %}
- {% block header %}{% endblock %}
-
-
- {% block body %}{% endblock %}
-
-
diff --git a/uncloud/urls.py b/uncloud/urls.py
deleted file mode 100644
index ef950a0..0000000
--- a/uncloud/urls.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""uncloud URL Configuration
-
-The `urlpatterns` list routes URLs to views. For more information please see:
- https://docs.djangoproject.com/en/3.0/topics/http/urls/
-"""
-
-from django.contrib import admin
-from django.urls import path, include
-from django.conf import settings
-from django.conf.urls.static import static
-
-from rest_framework import routers
-from rest_framework.schemas import get_schema_view
-
-#from opennebula import views as oneviews
-from uncloud_auth import views as authviews
-from uncloud_net import views as netviews
-from uncloud_pay import views as payviews
-from uncloud_vm import views as vmviews
-from uncloud_service import views as serviceviews
-
-router = routers.DefaultRouter()
-
-# Beta endpoints
-router.register(r'beta/vm', vmviews.NicoVMProductViewSet, basename='nicovmproduct')
-
-# VM
-router.register(r'v1/vm/snapshot', vmviews.VMSnapshotProductViewSet, basename='vmsnapshotproduct')
-router.register(r'v1/vm/diskimage', vmviews.VMDiskImageProductViewSet, basename='vmdiskimageproduct')
-router.register(r'v1/vm/disk', vmviews.VMDiskProductViewSet, basename='vmdiskproduct')
-router.register(r'v1/vm/vm', vmviews.VMProductViewSet, basename='vmproduct')
-
-
-# creates VM from os image
-#router.register(r'vm/ipv6onlyvm', vmviews.VMProductViewSet, basename='vmproduct')
-# ... AND adds IPv4 mapping
-#router.register(r'vm/dualstackvm', vmviews.VMProductViewSet, basename='vmproduct')
-
-# Services
-router.register(r'v1/service/matrix', serviceviews.MatrixServiceProductViewSet, basename='matrixserviceproduct')
-router.register(r'v1/service/generic', serviceviews.GenericServiceProductViewSet, basename='genericserviceproduct')
-
-
-# Net
-router.register(r'v1/net/vpn', netviews.VPNNetworkViewSet, basename='vpnnetwork')
-router.register(r'v1/admin/vpnreservation', netviews.VPNNetworkReservationViewSet, basename='vpnnetreservation')
-
-
-# Pay
-router.register(r'v1/my/address', payviews.BillingAddressViewSet, basename='billingaddress')
-router.register(r'v1/my/bill', payviews.BillViewSet, basename='bill')
-router.register(r'v1/my/order', payviews.OrderViewSet, basename='order')
-router.register(r'v1/my/payment', payviews.PaymentViewSet, basename='payment')
-router.register(r'v1/my/payment-method', payviews.PaymentMethodViewSet, basename='payment-method')
-
-# admin/staff urls
-router.register(r'v1/admin/bill', payviews.AdminBillViewSet, basename='admin/bill')
-router.register(r'v1/admin/payment', payviews.AdminPaymentViewSet, basename='admin/payment')
-router.register(r'v1/admin/order', payviews.AdminOrderViewSet, basename='admin/order')
-router.register(r'v1/admin/vmhost', vmviews.VMHostViewSet)
-router.register(r'v1/admin/vmcluster', vmviews.VMClusterViewSet)
-router.register(r'v1/admin/vpnpool', netviews.VPNPoolViewSet)
-#router.register(r'v1/admin/opennebula', oneviews.VMViewSet, basename='opennebula')
-
-# User/Account
-router.register(r'v1/my/user', authviews.UserViewSet, basename='user')
-router.register(r'v1/admin/user', authviews.AdminUserViewSet, basename='useradmin')
-
-urlpatterns = [
- path(r'api/', include(router.urls)),
- # web/ = stuff to view in the browser
-
- path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), # for login to REST API
- path('openapi', get_schema_view(
- title="uncloud",
- description="uncloud API",
- version="1.0.0"
- ), name='openapi-schema'),
- path('admin/', admin.site.urls),
-]
diff --git a/uncloud_auth/management/commands/make-admin.py b/uncloud_auth/management/commands/make-admin.py
deleted file mode 100644
index 9157439..0000000
--- a/uncloud_auth/management/commands/make-admin.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from django.core.management.base import BaseCommand
-from django.contrib.auth import get_user_model
-import sys
-
-class Command(BaseCommand):
- help = 'Give Admin rights to existing user'
-
- def add_arguments(self, parser):
- parser.add_argument('username', type=str)
- parser.add_argument('--superuser', action='store_true')
-
- def handle(self, *args, **options):
- user = get_user_model().objects.get(username=options['username'])
- user.is_staff = True
-
- if options['superuser']:
- user.is_superuser = True
-
- user.save()
-
- print(f"{user.username} is now admin (superuser={user.is_superuser})")
diff --git a/uncloud_auth/migrations/0002_auto_20200808_1953.py b/uncloud_auth/migrations/0002_auto_20200808_1953.py
deleted file mode 100644
index 234af95..0000000
--- a/uncloud_auth/migrations/0002_auto_20200808_1953.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-08-08 19:53
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('uncloud_auth', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='user',
- name='first_name',
- field=models.CharField(blank=True, max_length=150, verbose_name='first name'),
- ),
- ]
diff --git a/uncloud_auth/serializers.py b/uncloud_auth/serializers.py
deleted file mode 100644
index 92bbf01..0000000
--- a/uncloud_auth/serializers.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from django.contrib.auth import get_user_model
-from rest_framework import serializers
-
-from uncloud_pay import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS
-from uncloud_pay.models import BillingAddress
-
-class UserSerializer(serializers.ModelSerializer):
- class Meta:
- model = get_user_model()
- read_only_fields = [ 'username', 'balance', 'maximum_credit' ]
- fields = read_only_fields + [ 'email', 'primary_billing_address' ]
-
- def validate(self, data):
- """
- Ensure that the primary billing address belongs to the user
- """
-
- if 'primary_billing_address' in data:
- if not data['primary_billing_address'].owner == self.instance:
- raise serializers.ValidationError("Invalid data")
-
- return data
-
-class ImportUserSerializer(serializers.Serializer):
- username = serializers.CharField()
diff --git a/uncloud_auth/views.py b/uncloud_auth/views.py
deleted file mode 100644
index 77f0a0f..0000000
--- a/uncloud_auth/views.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from rest_framework import viewsets, permissions, status
-from .serializers import *
-from django_auth_ldap.backend import LDAPBackend
-from rest_framework.decorators import action
-from rest_framework.response import Response
-
-class UserViewSet(viewsets.GenericViewSet):
- permission_classes = [permissions.IsAuthenticated]
- serializer_class = UserSerializer
-
- def get_queryset(self):
- return self.request.user
-
- def list(self, request, format=None):
- # This is a bit stupid: we have a user, we create a queryset by
- # matching on the username. But I don't know a "nicer" way.
- # Nico, 2020-03-18
- user = request.user
- serializer = self.get_serializer(user, context = {'request': request})
- return Response(serializer.data)
-
- def create(self, request):
- """
- Modify existing user data
- """
-
- user = request.user
- serializer = self.get_serializer(user,
- context = {'request': request},
- data=request.data)
- serializer.is_valid(raise_exception=True)
- serializer.save()
- return Response(serializer.data)
-
-class AdminUserViewSet(viewsets.ReadOnlyModelViewSet):
- permission_classes = [permissions.IsAdminUser]
-
- def get_serializer_class(self):
- if self.action == 'import_from_ldap':
- return ImportUserSerializer
- else:
- return UserSerializer
-
- def get_queryset(self):
- return get_user_model().objects.all()
-
- @action(detail=False, methods=['post'], url_path='import_from_ldap')
- def import_from_ldap(self, request, pk=None):
- serializer = self.get_serializer(data=request.data)
- serializer.is_valid(raise_exception=True)
- ldap_username = serializer.validated_data.pop("username")
- user = LDAPBackend().populate_user(ldap_username)
-
- return Response(UserSerializer(user, context = {'request': request}).data)
diff --git a/archive/uncloud_django_based/hacks/abk-hacks.py b/uncloud_django_based/abk-hacks.py
similarity index 100%
rename from archive/uncloud_django_based/hacks/abk-hacks.py
rename to uncloud_django_based/abk-hacks.py
diff --git a/archive/uncloud_django_based/hacks/abkhack/opennebula_hacks.py b/uncloud_django_based/abkhack/opennebula_hacks.py
similarity index 100%
rename from archive/uncloud_django_based/hacks/abkhack/opennebula_hacks.py
rename to uncloud_django_based/abkhack/opennebula_hacks.py
diff --git a/archive/uncloud_django_based/meow-payv1/README.md b/uncloud_django_based/meow-payv1/README.md
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/README.md
rename to uncloud_django_based/meow-payv1/README.md
diff --git a/archive/uncloud_django_based/meow-payv1/config.py b/uncloud_django_based/meow-payv1/config.py
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/config.py
rename to uncloud_django_based/meow-payv1/config.py
diff --git a/archive/uncloud_django_based/meow-payv1/hack-a-vpn.py b/uncloud_django_based/meow-payv1/hack-a-vpn.py
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/hack-a-vpn.py
rename to uncloud_django_based/meow-payv1/hack-a-vpn.py
diff --git a/archive/uncloud_django_based/meow-payv1/helper.py b/uncloud_django_based/meow-payv1/helper.py
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/helper.py
rename to uncloud_django_based/meow-payv1/helper.py
diff --git a/archive/uncloud_django_based/meow-payv1/products/ipv6-only-django.json b/uncloud_django_based/meow-payv1/products/ipv6-only-django.json
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/products/ipv6-only-django.json
rename to uncloud_django_based/meow-payv1/products/ipv6-only-django.json
diff --git a/archive/uncloud_django_based/meow-payv1/products/ipv6-only-vm.json b/uncloud_django_based/meow-payv1/products/ipv6-only-vm.json
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/products/ipv6-only-vm.json
rename to uncloud_django_based/meow-payv1/products/ipv6-only-vm.json
diff --git a/archive/uncloud_django_based/meow-payv1/products/ipv6-only-vpn.json b/uncloud_django_based/meow-payv1/products/ipv6-only-vpn.json
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/products/ipv6-only-vpn.json
rename to uncloud_django_based/meow-payv1/products/ipv6-only-vpn.json
diff --git a/archive/uncloud_django_based/meow-payv1/products/ipv6box.json b/uncloud_django_based/meow-payv1/products/ipv6box.json
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/products/ipv6box.json
rename to uncloud_django_based/meow-payv1/products/ipv6box.json
diff --git a/archive/uncloud_django_based/meow-payv1/products/membership.json b/uncloud_django_based/meow-payv1/products/membership.json
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/products/membership.json
rename to uncloud_django_based/meow-payv1/products/membership.json
diff --git a/archive/uncloud_django_based/meow-payv1/requirements.txt b/uncloud_django_based/meow-payv1/requirements.txt
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/requirements.txt
rename to uncloud_django_based/meow-payv1/requirements.txt
diff --git a/archive/uncloud_django_based/meow-payv1/sample-pay.conf b/uncloud_django_based/meow-payv1/sample-pay.conf
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/sample-pay.conf
rename to uncloud_django_based/meow-payv1/sample-pay.conf
diff --git a/archive/uncloud_django_based/meow-payv1/schemas.py b/uncloud_django_based/meow-payv1/schemas.py
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/schemas.py
rename to uncloud_django_based/meow-payv1/schemas.py
diff --git a/archive/uncloud_django_based/meow-payv1/stripe_hack.py b/uncloud_django_based/meow-payv1/stripe_hack.py
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/stripe_hack.py
rename to uncloud_django_based/meow-payv1/stripe_hack.py
diff --git a/archive/uncloud_django_based/meow-payv1/stripe_utils.py b/uncloud_django_based/meow-payv1/stripe_utils.py
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/stripe_utils.py
rename to uncloud_django_based/meow-payv1/stripe_utils.py
diff --git a/archive/uncloud_django_based/meow-payv1/ucloud_pay.py b/uncloud_django_based/meow-payv1/ucloud_pay.py
similarity index 100%
rename from archive/uncloud_django_based/meow-payv1/ucloud_pay.py
rename to uncloud_django_based/meow-payv1/ucloud_pay.py
diff --git a/archive/uncloud_django_based/notes-abk.md b/uncloud_django_based/notes-abk.md
similarity index 100%
rename from archive/uncloud_django_based/notes-abk.md
rename to uncloud_django_based/notes-abk.md
diff --git a/archive/uncloud_django_based/notes-nico.org b/uncloud_django_based/notes-nico.org
similarity index 100%
rename from archive/uncloud_django_based/notes-nico.org
rename to uncloud_django_based/notes-nico.org
diff --git a/archive/uncloud_django_based/plan.org b/uncloud_django_based/plan.org
similarity index 100%
rename from archive/uncloud_django_based/plan.org
rename to uncloud_django_based/plan.org
diff --git a/archive/uncloud_django_based/uncloud/.gitignore b/uncloud_django_based/uncloud/.gitignore
similarity index 100%
rename from archive/uncloud_django_based/uncloud/.gitignore
rename to uncloud_django_based/uncloud/.gitignore
diff --git a/doc/README-how-to-configure-remote-uncloud-clients.org b/uncloud_django_based/uncloud/doc/README-how-to-configure-remote-uncloud-clients.org
similarity index 80%
rename from doc/README-how-to-configure-remote-uncloud-clients.org
rename to uncloud_django_based/uncloud/doc/README-how-to-configure-remote-uncloud-clients.org
index b48886b..7217e1f 100644
--- a/doc/README-how-to-configure-remote-uncloud-clients.org
+++ b/uncloud_django_based/uncloud/doc/README-how-to-configure-remote-uncloud-clients.org
@@ -10,7 +10,6 @@
| SSH -L tunnel | All nodes can use [::1]:5432 | SSH setup can be fragile |
| ssh djangohost manage.py | All DB ops locally | Code is only executed on django host |
| https + token | Rest alike / consistent access | Code is only executed on django host |
-| from_django | Everything is on the django host | main host can become bottleneck |
** remote vs. local Django code execution
- If manage.py is executed locally (= on the client), it can
check/modify local configs
@@ -20,9 +19,3 @@
- Remote execution (= on the primary django host) can acess the db
via unix socket
- However remote execution cannot check local state
-** from_django
- - might reuse existing methods like celery
- - reduces the amount of things to be installed on the client to
- almost zero
- - follows the opennebula model
- - has a single point of failurebin
diff --git a/uncloud_django_based/uncloud/doc/README-object-relations.md b/uncloud_django_based/uncloud/doc/README-object-relations.md
new file mode 100644
index 0000000..58f2413
--- /dev/null
+++ b/uncloud_django_based/uncloud/doc/README-object-relations.md
@@ -0,0 +1,82 @@
+## Introduction
+
+This article describes how models relate to each other and what the
+design ideas are. It is meant to prevent us from double implementing
+something or changing something that is already solved.
+
+
+## Products
+
+A product is something someone can order. We might have "low level"
+products that need to be composed (= higher degree of flexibility, but
+more amount of details necessary) and "composed products" that present
+some defaults or select other products automatically (f.i. a "dual
+stack VM" can be a VM + a disk + an IPv4 address).
+
+
+## Bills
+
+Bills represent active orders of a month. Bills can be shown during a
+month but only become definitive at the end of the month.
+
+## Orders
+
+When customer X order a (set) of product, it generates an order for billing
+purposes. The ordered products point to that order and register an Order Record
+at creation.
+
+Orders and Order Records are assumed immutable => they are used to generate
+bills and should not be mutated. If a product is updated (e.g. adding RAM to
+VM), a new order should be generated.
+
+The order MUST NOT be deleted when a product is deleted, as it is used for
+billing (including past bills).
+
+### Order record
+
+Used to store billing details of a product at creation: will stay there even if
+the product change (e.g. new pricing, updated) and act as some kind of archive.
+Used to generate bills.
+
+## Payment Methods
+
+Users/customers can register payment methods.
+
+## Sample flows / products
+
+### A VM snapshot
+
+A VM snapshot creates a snapshot of all disks attached to a VM to be
+able to rollback the VM to a previous state.
+
+Creating a VM snapshot (-product) creates a related order. Deleting a
+VMSnapshotproduct sets the order to deleted.
+
+### Object Storage
+
+(tbd by Balazs)
+
+### A "raw" VM
+
+(tbd by Ahmed)
+
+### An IPv6 only VM
+
+(tbd by Ahmed)
+
+### A dual stack VM
+
+(tbd by Ahmed)
+
+### A managed service (e.g. Matrix-as-a-Service)
+
+Customer orders service with:
+ * Service-specific configuration: e.g. domain name for matrix
+ * VM configuration:
+ - CPU
+ - Memory
+ - Disk (soon)
+
+It creates a new Order with two products/records:
+ * Service itself (= management)
+ * Underlying VM
diff --git a/uncloud_django_based/uncloud/doc/README-postgresql.org b/uncloud_django_based/uncloud/doc/README-postgresql.org
new file mode 100644
index 0000000..9e5cc10
--- /dev/null
+++ b/uncloud_django_based/uncloud/doc/README-postgresql.org
@@ -0,0 +1,8 @@
+* uncloud clients access the data base from a variety of outside hosts
+* So the postgresql data base needs to be remotely accessible
+* Instead of exposing the tcp socket, we make postgresql bind to localhost via IPv6
+** ::1, port 5432
+* Then we remotely connect to the database server with ssh tunneling
+** ssh -L5432:localhost:5432 uncloud-database-host
+* Configuring your database for SSH based remote access
+** host all all ::1/128 trust
diff --git a/uncloud_django_based/uncloud/doc/README-products.md b/uncloud_django_based/uncloud/doc/README-products.md
new file mode 100644
index 0000000..1b1190d
--- /dev/null
+++ b/uncloud_django_based/uncloud/doc/README-products.md
@@ -0,0 +1,34 @@
+## Introduction
+
+This document describes how to create, modify or
+delete a product and use it.
+
+A product (like a VMSnapshotproduct) creates an order when ordered.
+The "order" is used to combine products together.
+
+Sub-products or related products link to the same order.
+Each product has one (?) orderrecord
+
+
+## How to delete a product (logic 1)
+
+If a user want so delete (=cancel) a product, the following steps
+should be taken:
+
+* the associated order is set to cancelled
+* the product itself is deleted
+
+[above steps to be reviewed]
+
+## How to delete a product (rest api)
+
+http -a nicoschottelius:$(pass
+ungleich.ch/nico.schottelius@ungleich.ch)
+http://localhost:8000/net/vpn/43c83088-f4d6-49b9-86c7-40251ac07ada/
+
+-> does not delete the reservation.
+
+
+### Deleting a VPN
+
+When the product is deleted, the network must be marked as free.
diff --git a/uncloud_django_based/uncloud/doc/README-vpn.org b/uncloud_django_based/uncloud/doc/README-vpn.org
new file mode 100644
index 0000000..7d041cb
--- /dev/null
+++ b/uncloud_django_based/uncloud/doc/README-vpn.org
@@ -0,0 +1,34 @@
+* How to add a new VPN Host
+** Install wireguard to the host
+** Install uncloud to the host
+** Add `python manage.py vpn --hostname fqdn-of-this-host` to the crontab
+** Use the CLI to configure one or more VPN Networks for this host
+* Example of adding a VPN host at ungleich
+** Create a new dual stack alpine VM
+** Add it to DNS as vpn-XXX.ungleich.ch
+** Route a /40 network to its IPv6 address
+** Install wireguard on it
+** TODO Enable wireguard on boot
+** TODO Create a new VPNPool on uncloud with
+*** the network address (selecting from our existing pool)
+*** the network size (/...)
+*** the vpn host that provides the network (selecting the created VM)
+*** the wireguard private key of the vpn host (using wg genkey)
+*** http command
+```
+http -a nicoschottelius:$(pass
+ ungleich.ch/nico.schottelius@ungleich.ch)
+ http://localhost:8000/admin/vpnpool/ network=2a0a:e5c1:200:: \
+ network_size=40 subnetwork_size=48
+ vpn_hostname=vpn-2a0ae5c1200.ungleich.ch
+ wireguard_private_key=...
+```
+* Example http commands / REST calls
+** creating a new vpn pool
+ http -a nicoschottelius:$(pass
+ ungleich.ch/nico.schottelius@ungleich.ch)
+ http://localhost:8000/admin/vpnpool/ network_size=40
+ subnetwork_size=48 network=2a0a:e5c1:200::
+ vpn_hostname=vpn-2a0ae5c1200.ungleich.ch wireguard_private_key=$(wg
+ genkey)
+** Creating a new vpn network
diff --git a/uncloud_django_based/uncloud/doc/README.md b/uncloud_django_based/uncloud/doc/README.md
new file mode 100644
index 0000000..390a3af
--- /dev/null
+++ b/uncloud_django_based/uncloud/doc/README.md
@@ -0,0 +1,95 @@
+## Install
+
+### OS package requirements
+
+Alpine:
+
+```
+apk add openldap-dev postgresql-dev
+```
+
+Debian/Devuan:
+
+```
+apt install postgresql-server-dev-all
+```
+
+
+### Python requirements
+
+If you prefer using a venv, use:
+
+```
+python -m venv venv
+. ./venv/bin/activate
+```
+
+Then install the requirements
+
+```
+pip install -r requirements.txt
+```
+
+### Database requirements
+
+Due to the use of the JSONField, postgresql is required.
+
+First create a role to be used:
+
+```
+postgres=# create role nico login;
+```
+
+Then create the database owner by the new role:
+
+```
+postgres=# create database uncloud owner nico;
+```
+
+Installing the postgresql service is os dependent, but some hints:
+
+* Alpine: `apk add postgresql-server && rc-update add postgresql && rc-service postgresql start`
+* Debian/Devuan: `apt install postgresql`
+
+After postresql is started, apply the migrations:
+
+```
+python manage.py migrate
+```
+
+### Secrets
+
+cp `uncloud/secrets_sample.py` to `uncloud/secrets.py` and replace the
+sample values with real values.
+
+
+## Flows / Orders
+
+### Creating a VMHost
+
+
+
+### Creating a VM
+
+* Create a VMHost
+* Create a VM on a VMHost
+
+
+### Creating a VM Snapshot
+
+
+## Working Beta APIs
+
+These APIs can be used for internal testing.
+
+### URL Overview
+
+```
+http -a nicoschottelius:$(pass ungleich.ch/nico.schottelius@ungleich.ch) http://localhost:8000
+```
+
+### Snapshotting
+
+```
+http -a nicoschottelius:$(pass ungleich.ch/nico.schottelius@ungleich.ch) http://localhost:8000/vm/snapshot/ vm_uuid=$(uuidgen)
+```
diff --git a/manage.py b/uncloud_django_based/uncloud/manage.py
similarity index 100%
rename from manage.py
rename to uncloud_django_based/uncloud/manage.py
diff --git a/models.dot b/uncloud_django_based/uncloud/models.dot
similarity index 100%
rename from models.dot
rename to uncloud_django_based/uncloud/models.dot
diff --git a/models.png b/uncloud_django_based/uncloud/models.png
similarity index 100%
rename from models.png
rename to uncloud_django_based/uncloud/models.png
diff --git a/archive/uncloud_etcd_based/docs/__init__.py b/uncloud_django_based/uncloud/opennebula/__init__.py
similarity index 100%
rename from archive/uncloud_etcd_based/docs/__init__.py
rename to uncloud_django_based/uncloud/opennebula/__init__.py
diff --git a/opennebula/admin.py b/uncloud_django_based/uncloud/opennebula/admin.py
similarity index 100%
rename from opennebula/admin.py
rename to uncloud_django_based/uncloud/opennebula/admin.py
diff --git a/opennebula/apps.py b/uncloud_django_based/uncloud/opennebula/apps.py
similarity index 100%
rename from opennebula/apps.py
rename to uncloud_django_based/uncloud/opennebula/apps.py
diff --git a/opennebula/management/commands/opennebula-synchosts.py b/uncloud_django_based/uncloud/opennebula/management/commands/opennebula-synchosts.py
similarity index 100%
rename from opennebula/management/commands/opennebula-synchosts.py
rename to uncloud_django_based/uncloud/opennebula/management/commands/opennebula-synchosts.py
diff --git a/opennebula/management/commands/opennebula-syncvms.py b/uncloud_django_based/uncloud/opennebula/management/commands/opennebula-syncvms.py
similarity index 88%
rename from opennebula/management/commands/opennebula-syncvms.py
rename to uncloud_django_based/uncloud/opennebula/management/commands/opennebula-syncvms.py
index 3c12fa9..458528b 100644
--- a/opennebula/management/commands/opennebula-syncvms.py
+++ b/uncloud_django_based/uncloud/opennebula/management/commands/opennebula-syncvms.py
@@ -1,9 +1,12 @@
import json
+import uncloud.secrets as secrets
+
+
from xmlrpc.client import ServerProxy as RPCClient
+
from django_auth_ldap.backend import LDAPBackend
from django.core.management.base import BaseCommand
-from django.conf import settings
from xmltodict import parse
from opennebula.models import VM as VMModel
@@ -16,9 +19,9 @@ class Command(BaseCommand):
pass
def handle(self, *args, **options):
- with RPCClient(settings.OPENNEBULA_URL) as rpc_client:
+ with RPCClient(secrets.OPENNEBULA_URL) as rpc_client:
success, response, *_ = rpc_client.one.vmpool.infoextended(
- settings.OPENNEBULA_USER_PASS, -2, -1, -1, -1
+ secrets.OPENNEBULA_USER_PASS, -2, -1, -1, -1
)
if success:
vms = json.loads(json.dumps(parse(response)))['VM_POOL']['VM']
diff --git a/opennebula/management/commands/opennebula-to-uncloud.py b/uncloud_django_based/uncloud/opennebula/management/commands/opennebula-to-uncloud.py
similarity index 100%
rename from opennebula/management/commands/opennebula-to-uncloud.py
rename to uncloud_django_based/uncloud/opennebula/management/commands/opennebula-to-uncloud.py
diff --git a/uncloud_django_based/uncloud/opennebula/migrations/0001_initial.py b/uncloud_django_based/uncloud/opennebula/migrations/0001_initial.py
new file mode 100644
index 0000000..4c0527a
--- /dev/null
+++ b/uncloud_django_based/uncloud/opennebula/migrations/0001_initial.py
@@ -0,0 +1,28 @@
+# Generated by Django 3.0.3 on 2020-02-23 17:12
+
+from django.conf import settings
+import django.contrib.postgres.fields.jsonb
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='VM',
+ fields=[
+ ('vmid', models.IntegerField(primary_key=True, serialize=False)),
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('data', django.contrib.postgres.fields.jsonb.JSONField()),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/opennebula/migrations/0002_auto_20200225_1335.py b/uncloud_django_based/uncloud/opennebula/migrations/0002_auto_20200225_1335.py
new file mode 100644
index 0000000..1554aa6
--- /dev/null
+++ b/uncloud_django_based/uncloud/opennebula/migrations/0002_auto_20200225_1335.py
@@ -0,0 +1,27 @@
+# Generated by Django 3.0.3 on 2020-02-25 13:35
+
+from django.db import migrations, models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('opennebula', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='vm',
+ name='uuid',
+ ),
+ migrations.RemoveField(
+ model_name='vm',
+ name='vmid',
+ ),
+ migrations.AddField(
+ model_name='vm',
+ name='id',
+ field=models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, unique=True),
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/opennebula/migrations/0003_auto_20200225_1428.py b/uncloud_django_based/uncloud/opennebula/migrations/0003_auto_20200225_1428.py
new file mode 100644
index 0000000..8bb3d8d
--- /dev/null
+++ b/uncloud_django_based/uncloud/opennebula/migrations/0003_auto_20200225_1428.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.0.3 on 2020-02-25 14:28
+
+from django.db import migrations, models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('opennebula', '0002_auto_20200225_1335'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='vm',
+ name='id',
+ field=models.CharField(default=uuid.uuid4, max_length=64, primary_key=True, serialize=False, unique=True),
+ ),
+ ]
diff --git a/opennebula/migrations/0004_auto_20200809_1237.py b/uncloud_django_based/uncloud/opennebula/migrations/0004_auto_20200225_1816.py
similarity index 50%
rename from opennebula/migrations/0004_auto_20200809_1237.py
rename to uncloud_django_based/uncloud/opennebula/migrations/0004_auto_20200225_1816.py
index ac4ac86..5b39f26 100644
--- a/opennebula/migrations/0004_auto_20200809_1237.py
+++ b/uncloud_django_based/uncloud/opennebula/migrations/0004_auto_20200225_1816.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.1 on 2020-08-09 12:37
+# Generated by Django 3.0.3 on 2020-02-25 18:16
from django.db import migrations, models
@@ -6,18 +6,18 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
- ('uncloud_pay', '0013_auto_20200809_1237'),
- ('opennebula', '0003_auto_20200808_1953'),
+ ('opennebula', '0003_auto_20200225_1428'),
]
operations = [
migrations.RemoveField(
model_name='vm',
- name='order',
+ name='id',
),
migrations.AddField(
model_name='vm',
- name='orders',
- field=models.ManyToManyField(to='uncloud_pay.Order'),
+ name='vmid',
+ field=models.IntegerField(default=42, primary_key=True, serialize=False),
+ preserve_default=False,
),
]
diff --git a/archive/uncloud_etcd_based/docs/source/__init__.py b/uncloud_django_based/uncloud/opennebula/migrations/__init__.py
similarity index 100%
rename from archive/uncloud_etcd_based/docs/source/__init__.py
rename to uncloud_django_based/uncloud/opennebula/migrations/__init__.py
diff --git a/opennebula/models.py b/uncloud_django_based/uncloud/opennebula/models.py
similarity index 94%
rename from opennebula/models.py
rename to uncloud_django_based/uncloud/opennebula/models.py
index f15b845..826b615 100644
--- a/opennebula/models.py
+++ b/uncloud_django_based/uncloud/opennebula/models.py
@@ -1,7 +1,7 @@
import uuid
from django.db import models
from django.contrib.auth import get_user_model
-from uncloud_pay.models import Product
+from django.contrib.postgres.fields import JSONField
# ungleich specific
storage_class_mapping = {
@@ -12,7 +12,8 @@ storage_class_mapping = {
class VM(models.Model):
vmid = models.IntegerField(primary_key=True)
- data = models.JSONField()
+ owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
+ data = JSONField()
@property
def uncloud_name(self):
diff --git a/opennebula/serializers.py b/uncloud_django_based/uncloud/opennebula/serializers.py
similarity index 100%
rename from opennebula/serializers.py
rename to uncloud_django_based/uncloud/opennebula/serializers.py
diff --git a/opennebula/tests.py b/uncloud_django_based/uncloud/opennebula/tests.py
similarity index 100%
rename from opennebula/tests.py
rename to uncloud_django_based/uncloud/opennebula/tests.py
diff --git a/uncloud_django_based/uncloud/opennebula/views.py b/uncloud_django_based/uncloud/opennebula/views.py
new file mode 100644
index 0000000..89b1a52
--- /dev/null
+++ b/uncloud_django_based/uncloud/opennebula/views.py
@@ -0,0 +1,16 @@
+from rest_framework import viewsets, permissions
+
+from .models import VM
+from .serializers import OpenNebulaVMSerializer
+
+class VMViewSet(viewsets.ModelViewSet):
+ permission_classes = [permissions.IsAuthenticated]
+ serializer_class = OpenNebulaVMSerializer
+
+ def get_queryset(self):
+ if self.request.user.is_superuser:
+ obj = VM.objects.all()
+ else:
+ obj = VM.objects.filter(owner=self.request.user)
+
+ return obj
diff --git a/requirements.txt b/uncloud_django_based/uncloud/requirements.txt
similarity index 100%
rename from requirements.txt
rename to uncloud_django_based/uncloud/requirements.txt
diff --git a/uncloud_django_based/uncloud/uncloud/.gitignore b/uncloud_django_based/uncloud/uncloud/.gitignore
new file mode 100644
index 0000000..ef418f5
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud/.gitignore
@@ -0,0 +1 @@
+secrets.py
diff --git a/uncloud_pay/__init__.py b/uncloud_django_based/uncloud/uncloud/__init__.py
similarity index 63%
rename from uncloud_pay/__init__.py
rename to uncloud_django_based/uncloud/uncloud/__init__.py
index 810fd3e..9e2545a 100644
--- a/uncloud_pay/__init__.py
+++ b/uncloud_django_based/uncloud/uncloud/__init__.py
@@ -1,8 +1,4 @@
-
-import decimal
-
# Define DecimalField properties, used to represent amounts of money.
+# Used in pay and auth
AMOUNT_MAX_DIGITS=10
AMOUNT_DECIMALS=2
-
-decimal.getcontext().prec = AMOUNT_DECIMALS
diff --git a/uncloud/asgi.py b/uncloud_django_based/uncloud/uncloud/asgi.py
similarity index 100%
rename from uncloud/asgi.py
rename to uncloud_django_based/uncloud/uncloud/asgi.py
diff --git a/uncloud/management/commands/uncloud.py b/uncloud_django_based/uncloud/uncloud/management/commands/uncloud.py
similarity index 100%
rename from uncloud/management/commands/uncloud.py
rename to uncloud_django_based/uncloud/uncloud/management/commands/uncloud.py
diff --git a/uncloud_django_based/uncloud/uncloud/models.py b/uncloud_django_based/uncloud/uncloud/models.py
new file mode 100644
index 0000000..bd7a931
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud/models.py
@@ -0,0 +1,35 @@
+from django.db import models
+from django.contrib.postgres.fields import JSONField
+from django.utils.translation import gettext_lazy as _
+
+class UncloudModel(models.Model):
+ """
+ This class extends the standard model with an
+ extra_data field that can be used to include public,
+ but internal information.
+
+ For instance if you migrate from an existing virtualisation
+ framework to uncloud.
+
+ The extra_data attribute should be considered a hack and whenever
+ data is necessary for running uncloud, it should **not** be stored
+ in there.
+
+ """
+
+ extra_data = JSONField(editable=False, blank=True, null=True)
+
+ class Meta:
+ abstract = True
+
+# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
+class UncloudStatus(models.TextChoices):
+ PENDING = 'PENDING', _('Pending')
+ AWAITING_PAYMENT = 'AWAITING_PAYMENT', _('Awaiting payment')
+ BEING_CREATED = 'BEING_CREATED', _('Being created')
+ SCHEDULED = 'SCHEDULED', _('Scheduled') # resource selected, waiting for dispatching
+ ACTIVE = 'ACTIVE', _('Active')
+ MODIFYING = 'MODIFYING', _('Modifying') # Resource is being changed
+ DELETED = 'DELETED', _('Deleted') # Resource has been deleted
+ DISABLED = 'DISABLED', _('Disabled') # Is usable, but cannot be used for new things
+ UNUSABLE = 'UNUSABLE', _('Unusable'), # Has some kind of error
diff --git a/uncloud_django_based/uncloud/uncloud/secrets_sample.py b/uncloud_django_based/uncloud/uncloud/secrets_sample.py
new file mode 100644
index 0000000..150fefb
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud/secrets_sample.py
@@ -0,0 +1,21 @@
+from django.core.management.utils import get_random_secret_key
+
+# XML-RPC interface of opennebula
+OPENNEBULA_URL = 'https://opennebula.ungleich.ch:2634/RPC2'
+
+# user:pass for accessing opennebula
+OPENNEBULA_USER_PASS = 'user:password'
+
+POSTGRESQL_DB_NAME="uncloud"
+
+# See https://django-auth-ldap.readthedocs.io/en/latest/authentication.html
+LDAP_ADMIN_DN=""
+LDAP_ADMIN_PASSWORD=""
+LDAP_SERVER_URI = ""
+
+# Stripe (Credit Card payments)
+STRIPE_KEY=""
+STRIPE_PUBLIC_KEY=""
+
+# The django secret key
+SECRET_KEY=get_random_secret_key()
diff --git a/uncloud/settings.py b/uncloud_django_based/uncloud/uncloud/settings.py
similarity index 76%
rename from uncloud/settings.py
rename to uncloud_django_based/uncloud/uncloud/settings.py
index 17f5200..b525073 100644
--- a/uncloud/settings.py
+++ b/uncloud_django_based/uncloud/uncloud/settings.py
@@ -13,30 +13,41 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
import os
import ldap
-from django.core.management.utils import get_random_secret_key
+# Uncommitted file with secrets
+import uncloud.secrets
+
from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
+# Uncommitted file with local settings i.e logging
+try:
+ from uncloud.local_settings import LOGGING, DATABASES
+except ModuleNotFoundError:
+ LOGGING = {}
+ # https://docs.djangoproject.com/en/3.0/ref/settings/#databases
+ DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.postgresql',
+ 'NAME': uncloud.secrets.POSTGRESQL_DB_NAME,
+ 'HOST': os.environ.get('DATABASE_HOST', '::1'),
+ 'USER': os.environ.get('DATABASE_USER', 'postgres'),
+ }
+ }
-LOGGING = {}
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
- }
-}
-
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = uncloud.secrets.SECRET_KEY
+
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
+ALLOWED_HOSTS = []
# Application definition
@@ -112,12 +123,7 @@ AUTH_PASSWORD_VALIDATORS = [
################################################################################
# AUTH/LDAP
-AUTH_LDAP_SERVER_URI = ""
-AUTH_LDAP_BIND_DN = ""
-AUTH_LDAP_BIND_PASSWORD = ""
-AUTH_LDAP_USER_SEARCH = LDAPSearch("dc=example,dc=com",
- ldap.SCOPE_SUBTREE,
- "(uid=%(user)s)")
+AUTH_LDAP_SERVER_URI = uncloud.secrets.LDAP_SERVER_URI
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
@@ -125,6 +131,13 @@ AUTH_LDAP_USER_ATTR_MAP = {
"email": "mail"
}
+
+AUTH_LDAP_BIND_DN = uncloud.secrets.LDAP_ADMIN_DN
+AUTH_LDAP_BIND_PASSWORD = uncloud.secrets.LDAP_ADMIN_PASSWORD
+
+AUTH_LDAP_USER_SEARCH = LDAPSearch("dc=ungleich,dc=ch", ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
+
+
################################################################################
# AUTH/Django
AUTHENTICATION_BACKENDS = [
@@ -145,6 +158,7 @@ REST_FRAMEWORK = {
}
+
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
@@ -163,31 +177,3 @@ USE_TZ = True
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static") ]
-
-# XML-RPC interface of opennebula
-OPENNEBULA_URL = 'https://opennebula.example.com:2634/RPC2'
-
-# user:pass for accessing opennebula
-OPENNEBULA_USER_PASS = 'user:password'
-
-
-# Stripe (Credit Card payments)
-STRIPE_KEY=""
-STRIPE_PUBLIC_KEY=""
-
-# The django secret key
-SECRET_KEY=get_random_secret_key()
-
-ALLOWED_HOSTS = []
-
-# required for hardcopy / pdf rendering: https://github.com/loftylabs/django-hardcopy
-CHROME_PATH = '/usr/bin/chromium-browser'
-
-# Username that is created by default and owns the configuration objects
-UNCLOUD_ADMIN_NAME = "uncloud-admin"
-
-# Overwrite settings with local settings, if existing
-try:
- from uncloud.local_settings import *
-except (ModuleNotFoundError, ImportError):
- pass
diff --git a/uncloud_django_based/uncloud/uncloud/urls.py b/uncloud_django_based/uncloud/uncloud/urls.py
new file mode 100644
index 0000000..14a87e8
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud/urls.py
@@ -0,0 +1,88 @@
+"""uncloud URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/3.0/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+
+from django.contrib import admin
+from django.urls import path, include
+from django.conf import settings
+from django.conf.urls.static import static
+
+from rest_framework import routers
+from rest_framework.schemas import get_schema_view
+
+from opennebula import views as oneviews
+from uncloud_auth import views as authviews
+from uncloud_net import views as netviews
+from uncloud_pay import views as payviews
+from uncloud_vm import views as vmviews
+from uncloud_service import views as serviceviews
+
+router = routers.DefaultRouter()
+
+# VM
+router.register(r'vm/snapshot', vmviews.VMSnapshotProductViewSet, basename='vmsnapshotproduct')
+router.register(r'vm/diskimage', vmviews.VMDiskImageProductViewSet, basename='vmdiskimageproduct')
+router.register(r'vm/disk', vmviews.VMDiskProductViewSet, basename='vmdiskproduct')
+router.register(r'vm/vm', vmviews.VMProductViewSet, basename='vmproduct')
+
+# creates VM from os image
+#router.register(r'vm/ipv6onlyvm', vmviews.VMProductViewSet, basename='vmproduct')
+# ... AND adds IPv4 mapping
+#router.register(r'vm/dualstackvm', vmviews.VMProductViewSet, basename='vmproduct')
+
+# Services
+router.register(r'service/matrix', serviceviews.MatrixServiceProductViewSet, basename='matrixserviceproduct')
+router.register(r'service/generic', serviceviews.GenericServiceProductViewSet, basename='genericserviceproduct')
+
+
+# Net
+router.register(r'net/vpn', netviews.VPNNetworkViewSet, basename='vpnnet')
+router.register(r'net/vpnreservation', netviews.VPNNetworkReservationViewSet, basename='vpnnetreservation')
+
+
+# Pay
+router.register(r'address', payviews.BillingAddressViewSet, basename='address')
+router.register(r'bill', payviews.BillViewSet, basename='bill')
+router.register(r'order', payviews.OrderViewSet, basename='order')
+router.register(r'payment', payviews.PaymentViewSet, basename='payment')
+router.register(r'payment-method', payviews.PaymentMethodViewSet, basename='payment-method')
+
+
+# admin/staff urls
+router.register(r'admin/bill', payviews.AdminBillViewSet, basename='admin/bill')
+router.register(r'admin/payment', payviews.AdminPaymentViewSet, basename='admin/payment')
+router.register(r'admin/order', payviews.AdminOrderViewSet, basename='admin/order')
+router.register(r'admin/vmhost', vmviews.VMHostViewSet)
+router.register(r'admin/vmcluster', vmviews.VMClusterViewSet)
+router.register(r'admin/vpnpool', netviews.VPNPoolViewSet)
+
+router.register(r'admin/opennebula', oneviews.VMViewSet, basename='opennebula')
+
+# User/Account
+router.register(r'user', authviews.UserViewSet, basename='user')
+
+
+urlpatterns = [
+ path('', include(router.urls)),
+ # web/ = stuff to view in the browser
+
+ path('web/pdf/', payviews.MyPDFView.as_view(), name='pdf'),
+ path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), # for login to REST API
+ path('openapi', get_schema_view(
+ title="uncloud",
+ description="uncloud API",
+ version="1.0.0"
+ ), name='openapi-schema'),
+]
diff --git a/uncloud/wsgi.py b/uncloud_django_based/uncloud/uncloud/wsgi.py
similarity index 100%
rename from uncloud/wsgi.py
rename to uncloud_django_based/uncloud/uncloud/wsgi.py
diff --git a/archive/uncloud_etcd_based/test/__init__.py b/uncloud_django_based/uncloud/uncloud_auth/__init__.py
similarity index 100%
rename from archive/uncloud_etcd_based/test/__init__.py
rename to uncloud_django_based/uncloud/uncloud_auth/__init__.py
diff --git a/uncloud_auth/admin.py b/uncloud_django_based/uncloud/uncloud_auth/admin.py
similarity index 100%
rename from uncloud_auth/admin.py
rename to uncloud_django_based/uncloud/uncloud_auth/admin.py
diff --git a/uncloud_auth/apps.py b/uncloud_django_based/uncloud/uncloud_auth/apps.py
similarity index 100%
rename from uncloud_auth/apps.py
rename to uncloud_django_based/uncloud/uncloud_auth/apps.py
diff --git a/uncloud_auth/migrations/0001_initial.py b/uncloud_django_based/uncloud/uncloud_auth/migrations/0001_initial.py
similarity index 92%
rename from uncloud_auth/migrations/0001_initial.py
rename to uncloud_django_based/uncloud/uncloud_auth/migrations/0001_initial.py
index ebb14ae..a1f8d00 100644
--- a/uncloud_auth/migrations/0001_initial.py
+++ b/uncloud_django_based/uncloud/uncloud_auth/migrations/0001_initial.py
@@ -1,8 +1,7 @@
-# Generated by Django 3.0.6 on 2020-08-01 16:38
+# Generated by Django 3.0.3 on 2020-03-03 16:49
import django.contrib.auth.models
import django.contrib.auth.validators
-import django.core.validators
from django.db import migrations, models
import django.utils.timezone
@@ -30,7 +29,6 @@ class Migration(migrations.Migration):
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
- ('maximum_credit', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
diff --git a/uncloud_pay/migrations/0024_auto_20200928_1945.py b/uncloud_django_based/uncloud/uncloud_auth/migrations/0002_auto_20200318_1343.py
similarity index 51%
rename from uncloud_pay/migrations/0024_auto_20200928_1945.py
rename to uncloud_django_based/uncloud/uncloud_auth/migrations/0002_auto_20200318_1343.py
index 6792049..ad2654f 100644
--- a/uncloud_pay/migrations/0024_auto_20200928_1945.py
+++ b/uncloud_django_based/uncloud/uncloud_auth/migrations/0002_auto_20200318_1343.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.1 on 2020-09-28 19:45
+# Generated by Django 3.0.3 on 2020-03-18 13:43
import django.core.validators
from django.db import migrations, models
@@ -7,18 +7,19 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
- ('uncloud_pay', '0023_auto_20200928_1944'),
+ ('uncloud_auth', '0001_initial'),
]
operations = [
migrations.AddField(
- model_name='order',
- name='currency',
- field=models.CharField(choices=[('CHF', 'Swiss Franc'), ('EUR', 'Euro'), ('USD', 'US Dollar')], default='CHF', max_length=32),
- ),
- migrations.AddField(
- model_name='order',
- name='recurring_price',
+ model_name='user',
+ name='amount',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]),
),
+ migrations.AddField(
+ model_name='user',
+ name='maximum_credit',
+ field=models.FloatField(default=0),
+ preserve_default=False,
+ ),
]
diff --git a/uncloud_django_based/uncloud/uncloud_auth/migrations/0003_auto_20200318_1345.py b/uncloud_django_based/uncloud/uncloud_auth/migrations/0003_auto_20200318_1345.py
new file mode 100644
index 0000000..31b1717
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_auth/migrations/0003_auto_20200318_1345.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.0.3 on 2020-03-18 13:45
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_auth', '0002_auto_20200318_1343'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='user',
+ name='amount',
+ ),
+ migrations.AlterField(
+ model_name='user',
+ name='maximum_credit',
+ field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]),
+ ),
+ ]
diff --git a/archive/uncloud_etcd_based/uncloud/cli/__init__.py b/uncloud_django_based/uncloud/uncloud_auth/migrations/__init__.py
similarity index 100%
rename from archive/uncloud_etcd_based/uncloud/cli/__init__.py
rename to uncloud_django_based/uncloud/uncloud_auth/migrations/__init__.py
diff --git a/uncloud_auth/models.py b/uncloud_django_based/uncloud/uncloud_auth/models.py
similarity index 85%
rename from uncloud_auth/models.py
rename to uncloud_django_based/uncloud/uncloud_auth/models.py
index 9132f96..c3a0912 100644
--- a/uncloud_auth/models.py
+++ b/uncloud_django_based/uncloud/uncloud_auth/models.py
@@ -2,7 +2,8 @@ from django.contrib.auth.models import AbstractUser
from django.db import models
from django.core.validators import MinValueValidator
-from uncloud_pay import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS
+from uncloud import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS
+
from uncloud_pay.models import get_balance_for_user
class User(AbstractUser):
@@ -17,9 +18,6 @@ class User(AbstractUser):
decimal_places=AMOUNT_DECIMALS,
validators=[MinValueValidator(0)])
- # @property
- # def primary_billing_address(self):
-
@property
def balance(self):
return get_balance_for_user(self)
diff --git a/uncloud_django_based/uncloud/uncloud_auth/serializers.py b/uncloud_django_based/uncloud/uncloud_auth/serializers.py
new file mode 100644
index 0000000..de369c3
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_auth/serializers.py
@@ -0,0 +1,15 @@
+from django.contrib.auth import get_user_model
+from rest_framework import serializers
+
+from uncloud import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS
+
+class UserSerializer(serializers.ModelSerializer):
+
+ class Meta:
+ model = get_user_model()
+ fields = ['username', 'email', 'balance', 'maximum_credit' ]
+
+
+
+ balance = serializers.DecimalField(max_digits=AMOUNT_MAX_DIGITS,
+ decimal_places=AMOUNT_DECIMALS)
diff --git a/uncloud_django_based/uncloud/uncloud_auth/views.py b/uncloud_django_based/uncloud/uncloud_auth/views.py
new file mode 100644
index 0000000..2f78e1f
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_auth/views.py
@@ -0,0 +1,17 @@
+from rest_framework import viewsets, permissions, status
+from .serializers import *
+
+class UserViewSet(viewsets.ReadOnlyModelViewSet):
+ serializer_class = UserSerializer
+ permission_classes = [permissions.IsAuthenticated]
+
+ def get_queryset(self):
+ if self.request.user.is_superuser:
+ obj = get_user_model().objects.all()
+ else:
+ # This is a bit stupid: we have a user, we create a queryset by
+ # matching on the username. But I don't know a "nicer" way.
+ # Nico, 2020-03-18
+ obj = get_user_model().objects.filter(username=self.request.user.username)
+
+ return obj
diff --git a/archive/uncloud_etcd_based/uncloud/client/__init__.py b/uncloud_django_based/uncloud/uncloud_net/__init__.py
similarity index 100%
rename from archive/uncloud_etcd_based/uncloud/client/__init__.py
rename to uncloud_django_based/uncloud/uncloud_net/__init__.py
diff --git a/uncloud_service/admin.py b/uncloud_django_based/uncloud/uncloud_net/admin.py
similarity index 100%
rename from uncloud_service/admin.py
rename to uncloud_django_based/uncloud/uncloud_net/admin.py
diff --git a/uncloud_net/apps.py b/uncloud_django_based/uncloud/uncloud_net/apps.py
similarity index 100%
rename from uncloud_net/apps.py
rename to uncloud_django_based/uncloud/uncloud_net/apps.py
diff --git a/uncloud_net/management/commands/vpn.py b/uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py
similarity index 100%
rename from uncloud_net/management/commands/vpn.py
rename to uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py
diff --git a/uncloud_net/migrations/0001_initial.py b/uncloud_django_based/uncloud/uncloud_net/migrations/0001_initial.py
similarity index 83%
rename from uncloud_net/migrations/0001_initial.py
rename to uncloud_django_based/uncloud/uncloud_net/migrations/0001_initial.py
index 7e018b2..940d63f 100644
--- a/uncloud_net/migrations/0001_initial.py
+++ b/uncloud_django_based/uncloud/uncloud_net/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.0.6 on 2020-08-01 16:38
+# Generated by Django 3.0.5 on 2020-04-06 21:38
from django.conf import settings
import django.contrib.postgres.fields.jsonb
@@ -14,7 +14,7 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('uncloud_pay', '__first__'),
+ ('uncloud_pay', '0001_initial'),
]
operations = [
@@ -44,7 +44,6 @@ class Migration(migrations.Migration):
fields=[
('extra_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, editable=False, null=True)),
('address', models.GenericIPAddressField(primary_key=True, serialize=False)),
- ('status', models.CharField(choices=[('used', 'used'), ('free', 'free')], default='used', max_length=256)),
('vpnpool', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_net.VPNPool')),
],
options={
@@ -54,11 +53,11 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='VPNNetwork',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extra_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, editable=False, null=True)),
- ('status', models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('SCHEDULED', 'Scheduled'), ('ACTIVE', 'Active'), ('MODIFYING', 'Modifying'), ('DELETED', 'Deleted'), ('DISABLED', 'Disabled'), ('UNUSABLE', 'Unusable')], default='AWAITING_PAYMENT', max_length=32)),
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ ('status', models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('SCHEDULED', 'Scheduled'), ('ACTIVE', 'Active'), ('MODIFYING', 'Modifying'), ('DELETED', 'Deleted'), ('DISABLED', 'Disabled'), ('UNUSABLE', 'Unusable')], default='PENDING', max_length=32)),
('wireguard_public_key', models.CharField(max_length=48)),
- ('network', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='uncloud_net.VPNNetworkReservation')),
+ ('network', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_net.VPNNetworkReservation')),
('order', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order')),
('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
diff --git a/uncloud_django_based/uncloud/uncloud_net/migrations/0002_auto_20200409_1225.py b/uncloud_django_based/uncloud/uncloud_net/migrations/0002_auto_20200409_1225.py
new file mode 100644
index 0000000..fcc2374
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_net/migrations/0002_auto_20200409_1225.py
@@ -0,0 +1,24 @@
+# Generated by Django 3.0.5 on 2020-04-09 12:25
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_net', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='vpnnetworkreservation',
+ name='status',
+ field=models.CharField(choices=[('used', 'used'), ('free', 'free')], default='used', max_length=256),
+ ),
+ migrations.AlterField(
+ model_name='vpnnetwork',
+ name='network',
+ field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='uncloud_net.VPNNetworkReservation'),
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/uncloud_net/migrations/0003_auto_20200417_0551.py b/uncloud_django_based/uncloud/uncloud_net/migrations/0003_auto_20200417_0551.py
new file mode 100644
index 0000000..24f4a7f
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_net/migrations/0003_auto_20200417_0551.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.5 on 2020-04-17 05:51
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_net', '0002_auto_20200409_1225'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='vpnnetwork',
+ name='status',
+ field=models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('SCHEDULED', 'Scheduled'), ('ACTIVE', 'Active'), ('MODIFYING', 'Modifying'), ('DELETED', 'Deleted'), ('DISABLED', 'Disabled'), ('UNUSABLE', 'Unusable')], default='AWAITING_PAYMENT', max_length=32),
+ ),
+ ]
diff --git a/archive/uncloud_etcd_based/uncloud/configure/__init__.py b/uncloud_django_based/uncloud/uncloud_net/migrations/__init__.py
similarity index 100%
rename from archive/uncloud_etcd_based/uncloud/configure/__init__.py
rename to uncloud_django_based/uncloud/uncloud_net/migrations/__init__.py
diff --git a/uncloud_net/models.py b/uncloud_django_based/uncloud/uncloud_net/models.py
similarity index 76%
rename from uncloud_net/models.py
rename to uncloud_django_based/uncloud/uncloud_net/models.py
index c9c8bc3..26a6eb8 100644
--- a/uncloud_net/models.py
+++ b/uncloud_django_based/uncloud/uncloud_net/models.py
@@ -4,14 +4,16 @@ import ipaddress
from django.db import models
from django.contrib.auth import get_user_model
from django.core.validators import MinValueValidator, MaxValueValidator
-from django.core.exceptions import FieldError, ValidationError
-from uncloud_pay.models import Order
+
+from uncloud_pay.models import Product, RecurringPeriod
+from uncloud.models import UncloudModel, UncloudStatus
+
class MACAdress(models.Model):
default_prefix = 0x420000000000
-class VPNPool(models.Model):
+class VPNPool(UncloudModel):
"""
Network address pools from which VPNs can be created
"""
@@ -143,7 +145,7 @@ AllowedIPs = {peer_network}
pass
-class VPNNetworkReservation(models.Model):
+class VPNNetworkReservation(UncloudModel):
"""
This class tracks the used VPN networks. It will be deleted, when the product is cancelled.
"""
@@ -161,7 +163,7 @@ class VPNNetworkReservation(models.Model):
)
-class VPNNetwork(models.Model):
+class VPNNetwork(Product):
"""
A selected network. Used for tracking reservations / used networks
"""
@@ -171,54 +173,11 @@ class VPNNetwork(models.Model):
wireguard_public_key = models.CharField(max_length=48)
-# default_recurring_period = RecurringPeriod.PER_365D
-
- @property
- def recurring_price(self):
- return 120
-
-
def delete(self, *args, **kwargs):
self.network.status = 'free'
self.network.save()
super().save(*args, **kwargs)
print("deleted {}".format(self))
-
-class ReverseDNSEntry(models.Model):
- """
- A reverse DNS entry
- """
- owner = models.ForeignKey(get_user_model(),
- on_delete=models.CASCADE)
-
- ip_address = models.GenericIPAddressField(null=False, unique=True)
-
- name = models.CharField(max_length=253, null=False)
-
- def save(self, *args, **kwargs):
- # Product.objects.filter(config__parameters__contains='reverse_dns_network')
- # FIXME: check if order is still active / not replaced
-
- allowed = False
-
- for order in Order.objects.filter(config__parameters__reverse_dns_network__isnull=False,
- owner=self.owner):
- network = order.config['parameters']['reverse_dns_network']
-
- net = ipaddress.ip_network(network)
- addr = ipaddress.ip_address(self.ip_address)
-
- if addr in net:
- allowed = True
- break
-
-
- if not allowed:
- raise ValidationError(f"User {self.owner} does not have the right to create reverse DNS entry for {self.ip_address}")
-
- super().save(*args, **kwargs)
-
-
- def __str__(self):
- return f"{self.ip_address} - {self.name}"
+# managing deletion
+# - record free network (?)
diff --git a/uncloud_net/serializers.py b/uncloud_django_based/uncloud/uncloud_net/serializers.py
similarity index 100%
rename from uncloud_net/serializers.py
rename to uncloud_django_based/uncloud/uncloud_net/serializers.py
diff --git a/uncloud_service/tests.py b/uncloud_django_based/uncloud/uncloud_net/tests.py
similarity index 100%
rename from uncloud_service/tests.py
rename to uncloud_django_based/uncloud/uncloud_net/tests.py
diff --git a/uncloud_net/views.py b/uncloud_django_based/uncloud/uncloud_net/views.py
similarity index 88%
rename from uncloud_net/views.py
rename to uncloud_django_based/uncloud/uncloud_net/views.py
index dc86959..1f7cf4a 100644
--- a/uncloud_net/views.py
+++ b/uncloud_django_based/uncloud/uncloud_net/views.py
@@ -21,8 +21,7 @@ class VPNNetworkReservationViewSet(viewsets.ModelViewSet):
class VPNNetworkViewSet(viewsets.ModelViewSet):
serializer_class = VPNNetworkSerializer
-# permission_classes = [permissions.IsAdminUser]
- permission_classes = [permissions.IsAuthenticated]
+ permission_classes = [permissions.IsAdminUser]
def get_queryset(self):
if self.request.user.is_superuser:
diff --git a/archive/uncloud_etcd_based/uncloud/network/__init__.py b/uncloud_django_based/uncloud/uncloud_pay/__init__.py
similarity index 100%
rename from archive/uncloud_etcd_based/uncloud/network/__init__.py
rename to uncloud_django_based/uncloud/uncloud_pay/__init__.py
diff --git a/uncloud_storage/admin.py b/uncloud_django_based/uncloud/uncloud_pay/admin.py
similarity index 100%
rename from uncloud_storage/admin.py
rename to uncloud_django_based/uncloud/uncloud_pay/admin.py
diff --git a/uncloud_pay/apps.py b/uncloud_django_based/uncloud/uncloud_pay/apps.py
similarity index 100%
rename from uncloud_pay/apps.py
rename to uncloud_django_based/uncloud/uncloud_pay/apps.py
diff --git a/uncloud_pay/helpers.py b/uncloud_django_based/uncloud/uncloud_pay/helpers.py
similarity index 100%
rename from uncloud_pay/helpers.py
rename to uncloud_django_based/uncloud/uncloud_pay/helpers.py
diff --git a/uncloud_pay/management/commands/charge-negative-balance.py b/uncloud_django_based/uncloud/uncloud_pay/management/commands/charge-negative-balance.py
similarity index 100%
rename from uncloud_pay/management/commands/charge-negative-balance.py
rename to uncloud_django_based/uncloud/uncloud_pay/management/commands/charge-negative-balance.py
diff --git a/uncloud_pay/management/commands/generate-bills.py b/uncloud_django_based/uncloud/uncloud_pay/management/commands/generate-bills.py
similarity index 100%
rename from uncloud_pay/management/commands/generate-bills.py
rename to uncloud_django_based/uncloud/uncloud_pay/management/commands/generate-bills.py
diff --git a/uncloud_pay/management/commands/handle-overdue-bills.py b/uncloud_django_based/uncloud/uncloud_pay/management/commands/handle-overdue-bills.py
similarity index 100%
rename from uncloud_pay/management/commands/handle-overdue-bills.py
rename to uncloud_django_based/uncloud/uncloud_pay/management/commands/handle-overdue-bills.py
diff --git a/uncloud_django_based/uncloud/uncloud_pay/management/commands/import-vat-rates.py b/uncloud_django_based/uncloud/uncloud_pay/management/commands/import-vat-rates.py
new file mode 100644
index 0000000..32938e4
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_pay/management/commands/import-vat-rates.py
@@ -0,0 +1,44 @@
+from django.core.management.base import BaseCommand
+from uncloud_pay.models import VATRate
+import csv
+
+
+class Command(BaseCommand):
+ help = '''Imports VAT Rates. Assume vat rates of format https://github.com/kdeldycke/vat-rates/blob/master/vat_rates.csv'''
+
+ def add_arguments(self, parser):
+ parser.add_argument('csv_file', nargs='+', type=str)
+
+ def handle(self, *args, **options):
+ try:
+ for c_file in options['csv_file']:
+ print("c_file = %s" % c_file)
+ with open(c_file, mode='r') as csv_file:
+ csv_reader = csv.DictReader(csv_file)
+ line_count = 0
+ for row in csv_reader:
+ if line_count == 0:
+ line_count += 1
+ obj, created = VATRate.objects.get_or_create(
+ start_date=row["start_date"],
+ stop_date=row["stop_date"] if row["stop_date"] is not "" else None,
+ territory_codes=row["territory_codes"],
+ currency_code=row["currency_code"],
+ rate=row["rate"],
+ rate_type=row["rate_type"],
+ description=row["description"]
+ )
+ if created:
+ self.stdout.write(self.style.SUCCESS(
+ '%s. %s - %s - %s - %s' % (
+ line_count,
+ obj.start_date,
+ obj.stop_date,
+ obj.territory_codes,
+ obj.rate
+ )
+ ))
+ line_count+=1
+
+ except Exception as e:
+ print(" *** Error occurred. Details {}".format(str(e)))
diff --git a/uncloud_django_based/uncloud/uncloud_pay/migrations/0001_initial.py b/uncloud_django_based/uncloud/uncloud_pay/migrations/0001_initial.py
new file mode 100644
index 0000000..89fa586
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_pay/migrations/0001_initial.py
@@ -0,0 +1,85 @@
+# Generated by Django 3.0.3 on 2020-03-05 10:17
+
+from django.conf import settings
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('uncloud_auth', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Bill',
+ fields=[
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ ('creation_date', models.DateTimeField(auto_now_add=True)),
+ ('starting_date', models.DateTimeField()),
+ ('ending_date', models.DateTimeField()),
+ ('due_date', models.DateField()),
+ ('valid', models.BooleanField(default=True)),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Order',
+ fields=[
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ ('creation_date', models.DateTimeField(auto_now_add=True)),
+ ('starting_date', models.DateTimeField(auto_now_add=True)),
+ ('ending_date', models.DateTimeField(blank=True, null=True)),
+ ('recurring_period', models.CharField(choices=[('ONCE', 'Onetime'), ('YEAR', 'Per Year'), ('MONTH', 'Per Month'), ('MINUTE', 'Per Minute'), ('DAY', 'Per Day'), ('HOUR', 'Per Hour'), ('SECOND', 'Per Second')], default='MONTH', max_length=32)),
+ ('bill', models.ManyToManyField(blank=True, editable=False, to='uncloud_pay.Bill')),
+ ('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='StripeCustomer',
+ fields=[
+ ('owner', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
+ ('stripe_id', models.CharField(max_length=32)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Payment',
+ fields=[
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ ('amount', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])),
+ ('source', models.CharField(choices=[('wire', 'Wire Transfer'), ('stripe', 'Stripe'), ('voucher', 'Voucher'), ('referral', 'Referral'), ('unknown', 'Unknown')], default='unknown', max_length=256)),
+ ('timestamp', models.DateTimeField(auto_now_add=True)),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='OrderRecord',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('one_time_price', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])),
+ ('recurring_price', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])),
+ ('description', models.TextField()),
+ ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='PaymentMethod',
+ fields=[
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ ('source', models.CharField(choices=[('stripe', 'Stripe'), ('unknown', 'Unknown')], default='stripe', max_length=256)),
+ ('description', models.TextField()),
+ ('primary', models.BooleanField(default=True)),
+ ('stripe_card_id', models.CharField(blank=True, max_length=32, null=True)),
+ ('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'unique_together': {('owner', 'primary')},
+ },
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/uncloud_pay/migrations/0002_auto_20200305_1524.py b/uncloud_django_based/uncloud/uncloud_pay/migrations/0002_auto_20200305_1524.py
new file mode 100644
index 0000000..0768dd0
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_pay/migrations/0002_auto_20200305_1524.py
@@ -0,0 +1,27 @@
+# Generated by Django 3.0.3 on 2020-03-05 15:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_pay', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RenameField(
+ model_name='paymentmethod',
+ old_name='stripe_card_id',
+ new_name='stripe_payment_method_id',
+ ),
+ migrations.AddField(
+ model_name='paymentmethod',
+ name='stripe_setup_intent_id',
+ field=models.CharField(blank=True, max_length=32, null=True),
+ ),
+ migrations.AlterUniqueTogether(
+ name='paymentmethod',
+ unique_together=set(),
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/uncloud_pay/migrations/0003_auto_20200305_1354.py b/uncloud_django_based/uncloud/uncloud_pay/migrations/0003_auto_20200305_1354.py
new file mode 100644
index 0000000..4157732
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_pay/migrations/0003_auto_20200305_1354.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.3 on 2020-03-05 13:54
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_pay', '0002_auto_20200305_1524'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='paymentmethod',
+ name='primary',
+ field=models.BooleanField(default=False, editable=False),
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/uncloud_pay/migrations/0004_auto_20200409_1225.py b/uncloud_django_based/uncloud/uncloud_pay/migrations/0004_auto_20200409_1225.py
new file mode 100644
index 0000000..32aac87
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_pay/migrations/0004_auto_20200409_1225.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.0.5 on 2020-04-09 12:25
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_pay', '0003_auto_20200305_1354'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='order',
+ name='recurring_period',
+ field=models.CharField(choices=[('ONCE', 'Onetime'), ('YEAR', 'Per Year'), ('MONTH', 'Per Month'), ('MINUTE', 'Per Minute'), ('WEEK', 'Per Week'), ('DAY', 'Per Day'), ('HOUR', 'Per Hour'), ('SECOND', 'Per Second')], default='MONTH', max_length=32),
+ ),
+ migrations.AlterField(
+ model_name='order',
+ name='starting_date',
+ field=models.DateTimeField(),
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/uncloud_pay/migrations/0005_auto_20200413_0924.py b/uncloud_django_based/uncloud/uncloud_pay/migrations/0005_auto_20200413_0924.py
new file mode 100644
index 0000000..3f6a646
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_pay/migrations/0005_auto_20200413_0924.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.5 on 2020-04-13 09:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_pay', '0004_auto_20200409_1225'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='order',
+ name='recurring_period',
+ field=models.CharField(choices=[('ONCE', 'Onetime'), ('YEAR', 'Per Year'), ('MONTH', 'Per Month'), ('WEEK', 'Per Week'), ('DAY', 'Per Day'), ('HOUR', 'Per Hour'), ('MINUTE', 'Per Minute'), ('SECOND', 'Per Second')], default='MONTH', max_length=32),
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/uncloud_pay/migrations/0006_auto_20200415_1003.py b/uncloud_django_based/uncloud/uncloud_pay/migrations/0006_auto_20200415_1003.py
new file mode 100644
index 0000000..1f37eae
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_pay/migrations/0006_auto_20200415_1003.py
@@ -0,0 +1,31 @@
+# Generated by Django 3.0.5 on 2020-04-15 10:03
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_pay', '0005_auto_20200413_0924'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='VATRate',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('start_date', models.DateField(blank=True, null=True)),
+ ('stop_date', models.DateField(blank=True, null=True)),
+ ('territory_codes', models.TextField(blank=True, default='')),
+ ('currency_code', models.CharField(max_length=10)),
+ ('rate', models.FloatField()),
+ ('rate_type', models.TextField(blank=True, default='')),
+ ('description', models.TextField(blank=True, default='')),
+ ],
+ ),
+ migrations.AlterField(
+ model_name='order',
+ name='recurring_period',
+ field=models.CharField(choices=[('ONCE', 'Onetime'), ('YEAR', 'Per Year'), ('MONTH', 'Per Month'), ('WEEK', 'Per Week'), ('DAY', 'Per Day'), ('HOUR', 'Per Hour'), ('MINUTE', 'Per Minute'), ('SECOND', 'Per Second')], default='MONTH', max_length=32),
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/uncloud_pay/migrations/0006_billingaddress.py b/uncloud_django_based/uncloud/uncloud_pay/migrations/0006_billingaddress.py
new file mode 100644
index 0000000..79b25ab
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_pay/migrations/0006_billingaddress.py
@@ -0,0 +1,29 @@
+# Generated by Django 3.0.5 on 2020-04-15 12:29
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uncloud_pay.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('uncloud_pay', '0006_auto_20200415_1003'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='BillingAddress',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('street', models.CharField(max_length=100)),
+ ('city', models.CharField(max_length=50)),
+ ('postal_code', models.CharField(max_length=50)),
+ ('country', uncloud_pay.models.CountryField(choices=[('AD', 'Andorra'), ('AE', 'United Arab Emirates'), ('AF', 'Afghanistan'), ('AG', 'Antigua & Barbuda'), ('AI', 'Anguilla'), ('AL', 'Albania'), ('AM', 'Armenia'), ('AN', 'Netherlands Antilles'), ('AO', 'Angola'), ('AQ', 'Antarctica'), ('AR', 'Argentina'), ('AS', 'American Samoa'), ('AT', 'Austria'), ('AU', 'Australia'), ('AW', 'Aruba'), ('AZ', 'Azerbaijan'), ('BA', 'Bosnia and Herzegovina'), ('BB', 'Barbados'), ('BD', 'Bangladesh'), ('BE', 'Belgium'), ('BF', 'Burkina Faso'), ('BG', 'Bulgaria'), ('BH', 'Bahrain'), ('BI', 'Burundi'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BN', 'Brunei Darussalam'), ('BO', 'Bolivia'), ('BR', 'Brazil'), ('BS', 'Bahama'), ('BT', 'Bhutan'), ('BV', 'Bouvet Island'), ('BW', 'Botswana'), ('BY', 'Belarus'), ('BZ', 'Belize'), ('CA', 'Canada'), ('CC', 'Cocos (Keeling) Islands'), ('CF', 'Central African Republic'), ('CG', 'Congo'), ('CH', 'Switzerland'), ('CI', 'Ivory Coast'), ('CK', 'Cook Iislands'), ('CL', 'Chile'), ('CM', 'Cameroon'), ('CN', 'China'), ('CO', 'Colombia'), ('CR', 'Costa Rica'), ('CU', 'Cuba'), ('CV', 'Cape Verde'), ('CX', 'Christmas Island'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DE', 'Germany'), ('DJ', 'Djibouti'), ('DK', 'Denmark'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('DZ', 'Algeria'), ('EC', 'Ecuador'), ('EE', 'Estonia'), ('EG', 'Egypt'), ('EH', 'Western Sahara'), ('ER', 'Eritrea'), ('ES', 'Spain'), ('ET', 'Ethiopia'), ('FI', 'Finland'), ('FJ', 'Fiji'), ('FK', 'Falkland Islands (Malvinas)'), ('FM', 'Micronesia'), ('FO', 'Faroe Islands'), ('FR', 'France'), ('FX', 'France, Metropolitan'), ('GA', 'Gabon'), ('GB', 'United Kingdom (Great Britain)'), ('GD', 'Grenada'), ('GE', 'Georgia'), ('GF', 'French Guiana'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GL', 'Greenland'), ('GM', 'Gambia'), ('GN', 'Guinea'), ('GP', 'Guadeloupe'), ('GQ', 'Equatorial Guinea'), ('GR', 'Greece'), ('GS', 'South Georgia and the South Sandwich Islands'), ('GT', 'Guatemala'), ('GU', 'Guam'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HK', 'Hong Kong'), ('HM', 'Heard & McDonald Islands'), ('HN', 'Honduras'), ('HR', 'Croatia'), ('HT', 'Haiti'), ('HU', 'Hungary'), ('ID', 'Indonesia'), ('IE', 'Ireland'), ('IL', 'Israel'), ('IN', 'India'), ('IO', 'British Indian Ocean Territory'), ('IQ', 'Iraq'), ('IR', 'Islamic Republic of Iran'), ('IS', 'Iceland'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JO', 'Jordan'), ('JP', 'Japan'), ('KE', 'Kenya'), ('KG', 'Kyrgyzstan'), ('KH', 'Cambodia'), ('KI', 'Kiribati'), ('KM', 'Comoros'), ('KN', 'St. Kitts and Nevis'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KY', 'Cayman Islands'), ('KZ', 'Kazakhstan'), ('LA', "Lao People's Democratic Republic"), ('LB', 'Lebanon'), ('LC', 'Saint Lucia'), ('LI', 'Liechtenstein'), ('LK', 'Sri Lanka'), ('LR', 'Liberia'), ('LS', 'Lesotho'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('LV', 'Latvia'), ('LY', 'Libyan Arab Jamahiriya'), ('MA', 'Morocco'), ('MC', 'Monaco'), ('MD', 'Moldova, Republic of'), ('MG', 'Madagascar'), ('MH', 'Marshall Islands'), ('ML', 'Mali'), ('MN', 'Mongolia'), ('MM', 'Myanmar'), ('MO', 'Macau'), ('MP', 'Northern Mariana Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MS', 'Monserrat'), ('MT', 'Malta'), ('MU', 'Mauritius'), ('MV', 'Maldives'), ('MW', 'Malawi'), ('MX', 'Mexico'), ('MY', 'Malaysia'), ('MZ', 'Mozambique'), ('NA', 'Namibia'), ('NC', 'New Caledonia'), ('NE', 'Niger'), ('NF', 'Norfolk Island'), ('NG', 'Nigeria'), ('NI', 'Nicaragua'), ('NL', 'Netherlands'), ('NO', 'Norway'), ('NP', 'Nepal'), ('NR', 'Nauru'), ('NU', 'Niue'), ('NZ', 'New Zealand'), ('OM', 'Oman'), ('PA', 'Panama'), ('PE', 'Peru'), ('PF', 'French Polynesia'), ('PG', 'Papua New Guinea'), ('PH', 'Philippines'), ('PK', 'Pakistan'), ('PL', 'Poland'), ('PM', 'St. Pierre & Miquelon'), ('PN', 'Pitcairn'), ('PR', 'Puerto Rico'), ('PT', 'Portugal'), ('PW', 'Palau'), ('PY', 'Paraguay'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('SA', 'Saudi Arabia'), ('SB', 'Solomon Islands'), ('SC', 'Seychelles'), ('SD', 'Sudan'), ('SE', 'Sweden'), ('SG', 'Singapore'), ('SH', 'St. Helena'), ('SI', 'Slovenia'), ('SJ', 'Svalbard & Jan Mayen Islands'), ('SK', 'Slovakia'), ('SL', 'Sierra Leone'), ('SM', 'San Marino'), ('SN', 'Senegal'), ('SO', 'Somalia'), ('SR', 'Suriname'), ('ST', 'Sao Tome & Principe'), ('SV', 'El Salvador'), ('SY', 'Syrian Arab Republic'), ('SZ', 'Swaziland'), ('TC', 'Turks & Caicos Islands'), ('TD', 'Chad'), ('TF', 'French Southern Territories'), ('TG', 'Togo'), ('TH', 'Thailand'), ('TJ', 'Tajikistan'), ('TK', 'Tokelau'), ('TM', 'Turkmenistan'), ('TN', 'Tunisia'), ('TO', 'Tonga'), ('TP', 'East Timor'), ('TR', 'Turkey'), ('TT', 'Trinidad & Tobago'), ('TV', 'Tuvalu'), ('TW', 'Taiwan, Province of China'), ('TZ', 'Tanzania, United Republic of'), ('UA', 'Ukraine'), ('UG', 'Uganda'), ('UM', 'United States Minor Outlying Islands'), ('US', 'United States of America'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VA', 'Vatican City State (Holy See)'), ('VC', 'St. Vincent & the Grenadines'), ('VE', 'Venezuela'), ('VG', 'British Virgin Islands'), ('VI', 'United States Virgin Islands'), ('VN', 'Viet Nam'), ('VU', 'Vanuatu'), ('WF', 'Wallis & Futuna Islands'), ('WS', 'Samoa'), ('YE', 'Yemen'), ('YT', 'Mayotte'), ('YU', 'Yugoslavia'), ('ZA', 'South Africa'), ('ZM', 'Zambia'), ('ZR', 'Zaire'), ('ZW', 'Zimbabwe')], default='CH', max_length=2)),
+ ('vat_number', models.CharField(blank=True, default='', max_length=100)),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ ]
diff --git a/uncloud_django_based/uncloud/uncloud_pay/migrations/0007_auto_20200418_0737.py b/uncloud_django_based/uncloud/uncloud_pay/migrations/0007_auto_20200418_0737.py
new file mode 100644
index 0000000..c9c2342
--- /dev/null
+++ b/uncloud_django_based/uncloud/uncloud_pay/migrations/0007_auto_20200418_0737.py
@@ -0,0 +1,42 @@
+# Generated by Django 3.0.5 on 2020-04-18 07:37
+
+from django.db import migrations, models
+import django.db.models.deletion
+import uncloud_pay.models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_pay', '0006_billingaddress'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='billingaddress',
+ name='id',
+ ),
+ migrations.AddField(
+ model_name='billingaddress',
+ name='name',
+ field=models.CharField(default='unknown', max_length=100),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='billingaddress',
+ name='uuid',
+ field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
+ ),
+ migrations.AddField(
+ model_name='order',
+ name='billing_address',
+ field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.BillingAddress'),
+ preserve_default=False,
+ ),
+ migrations.AlterField(
+ model_name='billingaddress',
+ name='country',
+ field=uncloud_pay.models.CountryField(blank=True, choices=[('AD', 'Andorra'), ('AE', 'United Arab Emirates'), ('AF', 'Afghanistan'), ('AG', 'Antigua & Barbuda'), ('AI', 'Anguilla'), ('AL', 'Albania'), ('AM', 'Armenia'), ('AN', 'Netherlands Antilles'), ('AO', 'Angola'), ('AQ', 'Antarctica'), ('AR', 'Argentina'), ('AS', 'American Samoa'), ('AT', 'Austria'), ('AU', 'Australia'), ('AW', 'Aruba'), ('AZ', 'Azerbaijan'), ('BA', 'Bosnia and Herzegovina'), ('BB', 'Barbados'), ('BD', 'Bangladesh'), ('BE', 'Belgium'), ('BF', 'Burkina Faso'), ('BG', 'Bulgaria'), ('BH', 'Bahrain'), ('BI', 'Burundi'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BN', 'Brunei Darussalam'), ('BO', 'Bolivia'), ('BR', 'Brazil'), ('BS', 'Bahama'), ('BT', 'Bhutan'), ('BV', 'Bouvet Island'), ('BW', 'Botswana'), ('BY', 'Belarus'), ('BZ', 'Belize'), ('CA', 'Canada'), ('CC', 'Cocos (Keeling) Islands'), ('CF', 'Central African Republic'), ('CG', 'Congo'), ('CH', 'Switzerland'), ('CI', 'Ivory Coast'), ('CK', 'Cook Iislands'), ('CL', 'Chile'), ('CM', 'Cameroon'), ('CN', 'China'), ('CO', 'Colombia'), ('CR', 'Costa Rica'), ('CU', 'Cuba'), ('CV', 'Cape Verde'), ('CX', 'Christmas Island'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DE', 'Germany'), ('DJ', 'Djibouti'), ('DK', 'Denmark'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('DZ', 'Algeria'), ('EC', 'Ecuador'), ('EE', 'Estonia'), ('EG', 'Egypt'), ('EH', 'Western Sahara'), ('ER', 'Eritrea'), ('ES', 'Spain'), ('ET', 'Ethiopia'), ('FI', 'Finland'), ('FJ', 'Fiji'), ('FK', 'Falkland Islands (Malvinas)'), ('FM', 'Micronesia'), ('FO', 'Faroe Islands'), ('FR', 'France'), ('FX', 'France, Metropolitan'), ('GA', 'Gabon'), ('GB', 'United Kingdom (Great Britain)'), ('GD', 'Grenada'), ('GE', 'Georgia'), ('GF', 'French Guiana'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GL', 'Greenland'), ('GM', 'Gambia'), ('GN', 'Guinea'), ('GP', 'Guadeloupe'), ('GQ', 'Equatorial Guinea'), ('GR', 'Greece'), ('GS', 'South Georgia and the South Sandwich Islands'), ('GT', 'Guatemala'), ('GU', 'Guam'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HK', 'Hong Kong'), ('HM', 'Heard & McDonald Islands'), ('HN', 'Honduras'), ('HR', 'Croatia'), ('HT', 'Haiti'), ('HU', 'Hungary'), ('ID', 'Indonesia'), ('IE', 'Ireland'), ('IL', 'Israel'), ('IN', 'India'), ('IO', 'British Indian Ocean Territory'), ('IQ', 'Iraq'), ('IR', 'Islamic Republic of Iran'), ('IS', 'Iceland'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JO', 'Jordan'), ('JP', 'Japan'), ('KE', 'Kenya'), ('KG', 'Kyrgyzstan'), ('KH', 'Cambodia'), ('KI', 'Kiribati'), ('KM', 'Comoros'), ('KN', 'St. Kitts and Nevis'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KY', 'Cayman Islands'), ('KZ', 'Kazakhstan'), ('LA', "Lao People's Democratic Republic"), ('LB', 'Lebanon'), ('LC', 'Saint Lucia'), ('LI', 'Liechtenstein'), ('LK', 'Sri Lanka'), ('LR', 'Liberia'), ('LS', 'Lesotho'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('LV', 'Latvia'), ('LY', 'Libyan Arab Jamahiriya'), ('MA', 'Morocco'), ('MC', 'Monaco'), ('MD', 'Moldova, Republic of'), ('MG', 'Madagascar'), ('MH', 'Marshall Islands'), ('ML', 'Mali'), ('MN', 'Mongolia'), ('MM', 'Myanmar'), ('MO', 'Macau'), ('MP', 'Northern Mariana Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MS', 'Monserrat'), ('MT', 'Malta'), ('MU', 'Mauritius'), ('MV', 'Maldives'), ('MW', 'Malawi'), ('MX', 'Mexico'), ('MY', 'Malaysia'), ('MZ', 'Mozambique'), ('NA', 'Namibia'), ('NC', 'New Caledonia'), ('NE', 'Niger'), ('NF', 'Norfolk Island'), ('NG', 'Nigeria'), ('NI', 'Nicaragua'), ('NL', 'Netherlands'), ('NO', 'Norway'), ('NP', 'Nepal'), ('NR', 'Nauru'), ('NU', 'Niue'), ('NZ', 'New Zealand'), ('OM', 'Oman'), ('PA', 'Panama'), ('PE', 'Peru'), ('PF', 'French Polynesia'), ('PG', 'Papua New Guinea'), ('PH', 'Philippines'), ('PK', 'Pakistan'), ('PL', 'Poland'), ('PM', 'St. Pierre & Miquelon'), ('PN', 'Pitcairn'), ('PR', 'Puerto Rico'), ('PT', 'Portugal'), ('PW', 'Palau'), ('PY', 'Paraguay'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('SA', 'Saudi Arabia'), ('SB', 'Solomon Islands'), ('SC', 'Seychelles'), ('SD', 'Sudan'), ('SE', 'Sweden'), ('SG', 'Singapore'), ('SH', 'St. Helena'), ('SI', 'Slovenia'), ('SJ', 'Svalbard & Jan Mayen Islands'), ('SK', 'Slovakia'), ('SL', 'Sierra Leone'), ('SM', 'San Marino'), ('SN', 'Senegal'), ('SO', 'Somalia'), ('SR', 'Suriname'), ('ST', 'Sao Tome & Principe'), ('SV', 'El Salvador'), ('SY', 'Syrian Arab Republic'), ('SZ', 'Swaziland'), ('TC', 'Turks & Caicos Islands'), ('TD', 'Chad'), ('TF', 'French Southern Territories'), ('TG', 'Togo'), ('TH', 'Thailand'), ('TJ', 'Tajikistan'), ('TK', 'Tokelau'), ('TM', 'Turkmenistan'), ('TN', 'Tunisia'), ('TO', 'Tonga'), ('TP', 'East Timor'), ('TR', 'Turkey'), ('TT', 'Trinidad & Tobago'), ('TV', 'Tuvalu'), ('TW', 'Taiwan, Province of China'), ('TZ', 'Tanzania, United Republic of'), ('UA', 'Ukraine'), ('UG', 'Uganda'), ('UM', 'United States Minor Outlying Islands'), ('US', 'United States of America'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VA', 'Vatican City State (Holy See)'), ('VC', 'St. Vincent & the Grenadines'), ('VE', 'Venezuela'), ('VG', 'British Virgin Islands'), ('VI', 'United States Virgin Islands'), ('VN', 'Viet Nam'), ('VU', 'Vanuatu'), ('WF', 'Wallis & Futuna Islands'), ('WS', 'Samoa'), ('YE', 'Yemen'), ('YT', 'Mayotte'), ('YU', 'Yugoslavia'), ('ZA', 'South Africa'), ('ZM', 'Zambia'), ('ZR', 'Zaire'), ('ZW', 'Zimbabwe')], default='CH', max_length=2),
+ ),
+ ]
diff --git a/archive/uncloud_etcd_based/uncloud/scheduler/tests/__init__.py b/uncloud_django_based/uncloud/uncloud_pay/migrations/__init__.py
similarity index 100%
rename from archive/uncloud_etcd_based/uncloud/scheduler/tests/__init__.py
rename to uncloud_django_based/uncloud/uncloud_pay/migrations/__init__.py
diff --git a/uncloud_pay/models_prior_to_cleanup.py b/uncloud_django_based/uncloud/uncloud_pay/models.py
similarity index 63%
rename from uncloud_pay/models_prior_to_cleanup.py
rename to uncloud_django_based/uncloud/uncloud_pay/models.py
index 55ccffb..bcce598 100644
--- a/uncloud_pay/models_prior_to_cleanup.py
+++ b/uncloud_django_based/uncloud/uncloud_pay/models.py
@@ -1,10 +1,10 @@
from django.db import models
from django.db.models import Q
from django.contrib.auth import get_user_model
-from django.utils.translation import gettext_lazy as _
from django.core.validators import MinValueValidator
+from django.utils.translation import gettext_lazy as _
from django.utils import timezone
-from django.core.exceptions import ObjectDoesNotExist, ValidationError
+from django.core.exceptions import ObjectDoesNotExist
import uuid
import logging
@@ -17,29 +17,277 @@ from decimal import Decimal
import uncloud_pay.stripe
from uncloud_pay.helpers import beginning_of_month, end_of_month
-from uncloud_pay import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS, COUNTRIES
+from uncloud import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS
from uncloud.models import UncloudModel, UncloudStatus
from decimal import Decimal
import decimal
+# Define DecimalField properties, used to represent amounts of money.
+AMOUNT_MAX_DIGITS=10
+AMOUNT_DECIMALS=2
+
+# FIXME: check why we need +1 here.
+decimal.getcontext().prec = AMOUNT_DECIMALS + 1
+
# Used to generate bill due dates.
BILL_PAYMENT_DELAY=timedelta(days=10)
+# http://xml.coverpages.org/country3166.html
+COUNTRIES = (
+ ('AD', _('Andorra')),
+ ('AE', _('United Arab Emirates')),
+ ('AF', _('Afghanistan')),
+ ('AG', _('Antigua & Barbuda')),
+ ('AI', _('Anguilla')),
+ ('AL', _('Albania')),
+ ('AM', _('Armenia')),
+ ('AN', _('Netherlands Antilles')),
+ ('AO', _('Angola')),
+ ('AQ', _('Antarctica')),
+ ('AR', _('Argentina')),
+ ('AS', _('American Samoa')),
+ ('AT', _('Austria')),
+ ('AU', _('Australia')),
+ ('AW', _('Aruba')),
+ ('AZ', _('Azerbaijan')),
+ ('BA', _('Bosnia and Herzegovina')),
+ ('BB', _('Barbados')),
+ ('BD', _('Bangladesh')),
+ ('BE', _('Belgium')),
+ ('BF', _('Burkina Faso')),
+ ('BG', _('Bulgaria')),
+ ('BH', _('Bahrain')),
+ ('BI', _('Burundi')),
+ ('BJ', _('Benin')),
+ ('BM', _('Bermuda')),
+ ('BN', _('Brunei Darussalam')),
+ ('BO', _('Bolivia')),
+ ('BR', _('Brazil')),
+ ('BS', _('Bahama')),
+ ('BT', _('Bhutan')),
+ ('BV', _('Bouvet Island')),
+ ('BW', _('Botswana')),
+ ('BY', _('Belarus')),
+ ('BZ', _('Belize')),
+ ('CA', _('Canada')),
+ ('CC', _('Cocos (Keeling) Islands')),
+ ('CF', _('Central African Republic')),
+ ('CG', _('Congo')),
+ ('CH', _('Switzerland')),
+ ('CI', _('Ivory Coast')),
+ ('CK', _('Cook Iislands')),
+ ('CL', _('Chile')),
+ ('CM', _('Cameroon')),
+ ('CN', _('China')),
+ ('CO', _('Colombia')),
+ ('CR', _('Costa Rica')),
+ ('CU', _('Cuba')),
+ ('CV', _('Cape Verde')),
+ ('CX', _('Christmas Island')),
+ ('CY', _('Cyprus')),
+ ('CZ', _('Czech Republic')),
+ ('DE', _('Germany')),
+ ('DJ', _('Djibouti')),
+ ('DK', _('Denmark')),
+ ('DM', _('Dominica')),
+ ('DO', _('Dominican Republic')),
+ ('DZ', _('Algeria')),
+ ('EC', _('Ecuador')),
+ ('EE', _('Estonia')),
+ ('EG', _('Egypt')),
+ ('EH', _('Western Sahara')),
+ ('ER', _('Eritrea')),
+ ('ES', _('Spain')),
+ ('ET', _('Ethiopia')),
+ ('FI', _('Finland')),
+ ('FJ', _('Fiji')),
+ ('FK', _('Falkland Islands (Malvinas)')),
+ ('FM', _('Micronesia')),
+ ('FO', _('Faroe Islands')),
+ ('FR', _('France')),
+ ('FX', _('France, Metropolitan')),
+ ('GA', _('Gabon')),
+ ('GB', _('United Kingdom (Great Britain)')),
+ ('GD', _('Grenada')),
+ ('GE', _('Georgia')),
+ ('GF', _('French Guiana')),
+ ('GH', _('Ghana')),
+ ('GI', _('Gibraltar')),
+ ('GL', _('Greenland')),
+ ('GM', _('Gambia')),
+ ('GN', _('Guinea')),
+ ('GP', _('Guadeloupe')),
+ ('GQ', _('Equatorial Guinea')),
+ ('GR', _('Greece')),
+ ('GS', _('South Georgia and the South Sandwich Islands')),
+ ('GT', _('Guatemala')),
+ ('GU', _('Guam')),
+ ('GW', _('Guinea-Bissau')),
+ ('GY', _('Guyana')),
+ ('HK', _('Hong Kong')),
+ ('HM', _('Heard & McDonald Islands')),
+ ('HN', _('Honduras')),
+ ('HR', _('Croatia')),
+ ('HT', _('Haiti')),
+ ('HU', _('Hungary')),
+ ('ID', _('Indonesia')),
+ ('IE', _('Ireland')),
+ ('IL', _('Israel')),
+ ('IN', _('India')),
+ ('IO', _('British Indian Ocean Territory')),
+ ('IQ', _('Iraq')),
+ ('IR', _('Islamic Republic of Iran')),
+ ('IS', _('Iceland')),
+ ('IT', _('Italy')),
+ ('JM', _('Jamaica')),
+ ('JO', _('Jordan')),
+ ('JP', _('Japan')),
+ ('KE', _('Kenya')),
+ ('KG', _('Kyrgyzstan')),
+ ('KH', _('Cambodia')),
+ ('KI', _('Kiribati')),
+ ('KM', _('Comoros')),
+ ('KN', _('St. Kitts and Nevis')),
+ ('KP', _('Korea, Democratic People\'s Republic of')),
+ ('KR', _('Korea, Republic of')),
+ ('KW', _('Kuwait')),
+ ('KY', _('Cayman Islands')),
+ ('KZ', _('Kazakhstan')),
+ ('LA', _('Lao People\'s Democratic Republic')),
+ ('LB', _('Lebanon')),
+ ('LC', _('Saint Lucia')),
+ ('LI', _('Liechtenstein')),
+ ('LK', _('Sri Lanka')),
+ ('LR', _('Liberia')),
+ ('LS', _('Lesotho')),
+ ('LT', _('Lithuania')),
+ ('LU', _('Luxembourg')),
+ ('LV', _('Latvia')),
+ ('LY', _('Libyan Arab Jamahiriya')),
+ ('MA', _('Morocco')),
+ ('MC', _('Monaco')),
+ ('MD', _('Moldova, Republic of')),
+ ('MG', _('Madagascar')),
+ ('MH', _('Marshall Islands')),
+ ('ML', _('Mali')),
+ ('MN', _('Mongolia')),
+ ('MM', _('Myanmar')),
+ ('MO', _('Macau')),
+ ('MP', _('Northern Mariana Islands')),
+ ('MQ', _('Martinique')),
+ ('MR', _('Mauritania')),
+ ('MS', _('Monserrat')),
+ ('MT', _('Malta')),
+ ('MU', _('Mauritius')),
+ ('MV', _('Maldives')),
+ ('MW', _('Malawi')),
+ ('MX', _('Mexico')),
+ ('MY', _('Malaysia')),
+ ('MZ', _('Mozambique')),
+ ('NA', _('Namibia')),
+ ('NC', _('New Caledonia')),
+ ('NE', _('Niger')),
+ ('NF', _('Norfolk Island')),
+ ('NG', _('Nigeria')),
+ ('NI', _('Nicaragua')),
+ ('NL', _('Netherlands')),
+ ('NO', _('Norway')),
+ ('NP', _('Nepal')),
+ ('NR', _('Nauru')),
+ ('NU', _('Niue')),
+ ('NZ', _('New Zealand')),
+ ('OM', _('Oman')),
+ ('PA', _('Panama')),
+ ('PE', _('Peru')),
+ ('PF', _('French Polynesia')),
+ ('PG', _('Papua New Guinea')),
+ ('PH', _('Philippines')),
+ ('PK', _('Pakistan')),
+ ('PL', _('Poland')),
+ ('PM', _('St. Pierre & Miquelon')),
+ ('PN', _('Pitcairn')),
+ ('PR', _('Puerto Rico')),
+ ('PT', _('Portugal')),
+ ('PW', _('Palau')),
+ ('PY', _('Paraguay')),
+ ('QA', _('Qatar')),
+ ('RE', _('Reunion')),
+ ('RO', _('Romania')),
+ ('RU', _('Russian Federation')),
+ ('RW', _('Rwanda')),
+ ('SA', _('Saudi Arabia')),
+ ('SB', _('Solomon Islands')),
+ ('SC', _('Seychelles')),
+ ('SD', _('Sudan')),
+ ('SE', _('Sweden')),
+ ('SG', _('Singapore')),
+ ('SH', _('St. Helena')),
+ ('SI', _('Slovenia')),
+ ('SJ', _('Svalbard & Jan Mayen Islands')),
+ ('SK', _('Slovakia')),
+ ('SL', _('Sierra Leone')),
+ ('SM', _('San Marino')),
+ ('SN', _('Senegal')),
+ ('SO', _('Somalia')),
+ ('SR', _('Suriname')),
+ ('ST', _('Sao Tome & Principe')),
+ ('SV', _('El Salvador')),
+ ('SY', _('Syrian Arab Republic')),
+ ('SZ', _('Swaziland')),
+ ('TC', _('Turks & Caicos Islands')),
+ ('TD', _('Chad')),
+ ('TF', _('French Southern Territories')),
+ ('TG', _('Togo')),
+ ('TH', _('Thailand')),
+ ('TJ', _('Tajikistan')),
+ ('TK', _('Tokelau')),
+ ('TM', _('Turkmenistan')),
+ ('TN', _('Tunisia')),
+ ('TO', _('Tonga')),
+ ('TP', _('East Timor')),
+ ('TR', _('Turkey')),
+ ('TT', _('Trinidad & Tobago')),
+ ('TV', _('Tuvalu')),
+ ('TW', _('Taiwan, Province of China')),
+ ('TZ', _('Tanzania, United Republic of')),
+ ('UA', _('Ukraine')),
+ ('UG', _('Uganda')),
+ ('UM', _('United States Minor Outlying Islands')),
+ ('US', _('United States of America')),
+ ('UY', _('Uruguay')),
+ ('UZ', _('Uzbekistan')),
+ ('VA', _('Vatican City State (Holy See)')),
+ ('VC', _('St. Vincent & the Grenadines')),
+ ('VE', _('Venezuela')),
+ ('VG', _('British Virgin Islands')),
+ ('VI', _('United States Virgin Islands')),
+ ('VN', _('Viet Nam')),
+ ('VU', _('Vanuatu')),
+ ('WF', _('Wallis & Futuna Islands')),
+ ('WS', _('Samoa')),
+ ('YE', _('Yemen')),
+ ('YT', _('Mayotte')),
+ ('YU', _('Yugoslavia')),
+ ('ZA', _('South Africa')),
+ ('ZM', _('Zambia')),
+ ('ZR', _('Zaire')),
+ ('ZW', _('Zimbabwe')),
+)
+
# Initialize logger.
logger = logging.getLogger(__name__)
# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
-class RecurringPeriod(models.IntegerChoices):
- PER_365D = 365*24*3600, _('Per 365 days')
- PER_30D = 30*24*3600, _('Per 30 days')
- PER_WEEK = 7*24*3600, _('Per Week')
- PER_DAY = 24*3600, _('Per Day')
- PER_HOUR = 3600, _('Per Hour')
- PER_MINUTE = 60, _('Per Minute')
- PER_SECOND = 1, _('Per Second')
- ONE_TIME = 0, _('Onetime')
-
+class RecurringPeriod(models.TextChoices):
+ ONE_TIME = 'ONCE', _('Onetime')
+ PER_YEAR = 'YEAR', _('Per Year')
+ PER_MONTH = 'MONTH', _('Per Month')
+ PER_WEEK = 'WEEK', _('Per Week')
+ PER_DAY = 'DAY', _('Per Day')
+ PER_HOUR = 'HOUR', _('Per Hour')
+ PER_MINUTE = 'MINUTE', _('Per Minute')
+ PER_SECOND = 'SECOND', _('Per Second')
class CountryField(models.CharField):
def __init__(self, *args, **kwargs):
@@ -109,7 +357,6 @@ class Payment(models.Model):
for bill in newly_paid_bills:
bill.activate_products()
-
class PaymentMethod(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
owner = models.ForeignKey(get_user_model(),
@@ -197,7 +444,6 @@ class BillingAddress(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
- organization = models.CharField(max_length=100)
name = models.CharField(max_length=100)
street = models.CharField(max_length=100)
city = models.CharField(max_length=50)
@@ -209,9 +455,9 @@ class BillingAddress(models.Model):
def get_addresses_for(user):
return BillingAddress.objects.filter(owner=user)
- @classmethod
- def get_preferred_address_for(cls, user):
- addresses = cls.get_addresses_for(user)
+ @staticmethod
+ def get_preferred_address_for(user):
+ addresses = get_addresses_for(user)
if len(addresses) == 0:
return None
else:
@@ -246,94 +492,6 @@ class VATRate(models.Model):
logger.debug("Did not find VAT rate for %s, returning 0" % country_code)
return 0
-class BillNico(models.Model):
- """ FIXME:
- Bill needs to be unique in the triple (owner, year, month)
- """
-
- uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
- owner = models.ForeignKey(get_user_model(),
- on_delete=models.CASCADE)
-
- creation_date = models.DateTimeField(auto_now_add=True)
- starting_date = models.DateTimeField()
- ending_date = models.DateTimeField()
- due_date = models.DateField()
-
- valid = models.BooleanField(default=True)
-
- @staticmethod
- def create_all_bills():
- for owner in get_user_model().objects.all():
- # mintime = time of first order
- # maxtime = time of last order
- # iterate month based through it
- pass
-
- def assign_orders_to_bill(self, owner, year, month):
- """
- Generate a bill for the specific month of a user.
-
- First handle all one time orders
-
- FIXME:
-
- - limit this to active users in the future! (2020-05-23)
- """
-
- """
- Find all one time orders that have a starting date that falls into this month
- recurring_period=RecurringPeriod.ONE_TIME,
-
- Can we do this even for recurring / all of them
-
- """
-
- # FIXME: add something to check whether the order should be billed at all - i.e. a marker that
- # disables searching -> optimization for later
- for order in Order.objects.filter(Q(starting_date__gte=self.starting_date),
- Q(starting_date__lte=self.ending_date),
- owner=owner):
-
- order.bill.add(self)
-
-
- """
- Find all recurring orders that did not start in this time frame, but need
- to be billed in this time frame.
-
- This is:
- - order starting time before our starting time
- - order start time + (x * (the_period)) is inside our time frame, x must be integer
- test cases:
- + 365days:
- time_since_last_billed = self.starting_or_ending_date - order.last_bill_date
- periods =
- [ we could in theory add this as a property to the order: next
- """
- for order in Order.objects.filter(~Q(recurring_period=RecurringPeriod.ONE_TIME),
- Q(starting_date__lt=self.starting_date),
- owner=owner):
-
- if order.recurring_period > 0: # avoid div/0 - these are one time payments
-
- # How much time will have passed by the end of the billing cycle
- td = self.ending_date - order.starting_date
-
- # How MANY times it will have been used by then
- used_times = ceil(td / timedelta(seconds=order.recurring_period))
-
- billed_times = len(order.bills)
-
- # How many times it WAS billed -- can also be inferred from the bills that link to it!
- if used_times > billed_times:
- billing_times = used_times - billed_times
-
- # ALSO REGISTER THE TIME PERIOD!
-# order.
- pass
-
-
class Bill(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
owner = models.ForeignKey(get_user_model(),
@@ -363,8 +521,9 @@ class Bill(models.Model):
bill_records = []
orders = Order.objects.filter(bill=self)
for order in orders:
- bill_record = BillRecord(self, order)
- bill_records.append(bill_record)
+ for order_record in order.records:
+ bill_record = BillRecord(self, order_record)
+ bill_records.append(bill_record)
return bill_records
@@ -382,14 +541,8 @@ class Bill(models.Model):
@property
def final(self):
- # A bill is final when its ending date is passed, or when all of its
- # orders have been terminated.
- every_order_terminated = True
- billing_period_is_over = self.ending_date < timezone.now()
- for order in self.order_set.all():
- every_order_terminated = every_order_terminated and order.is_terminated
-
- return billing_period_is_over or every_order_terminated
+ # A bill is final when its ending date is passed.
+ return self.ending_date < timezone.now()
def activate_products(self):
for order in self.order_set.all():
@@ -405,10 +558,7 @@ class Bill(models.Model):
orders = Order.objects.filter(bill=self)
# The genrate_for method makes sure all the orders of a bill share the
# same billing address. TODO: It would be nice to enforce that somehow...
- if orders:
- return orders[0].billing_address
- else:
- return None
+ return orders[0].billing_address
# TODO: split this huuuge method!
@staticmethod
@@ -440,28 +590,28 @@ class Bill(models.Model):
previous_bill = None
# FIXME: control flow is confusing in this block.
- # if order.recurring_period == RecurringPeriod.PER_YEAR:
- # # We ignore anything smaller than a day in here.
- # next_yearly_bill_start_on = None
- # if previous_bill == None:
- # next_yearly_bill_start_on = order.starting_date
- # elif previous_bill.ending_date <= ending_date:
- # next_yearly_bill_start_on = (previous_bill.ending_date + timedelta(days=1))
+ if order.recurring_period == RecurringPeriod.PER_YEAR:
+ # We ignore anything smaller than a day in here.
+ next_yearly_bill_start_on = None
+ if previous_bill == None:
+ next_yearly_bill_start_on = order.starting_date
+ elif previous_bill.ending_date <= ending_date:
+ next_yearly_bill_start_on = (previous_bill.ending_date + timedelta(days=1))
- # # Store for bill generation. One bucket per day of month with a starting bill.
- # # bucket is a reference here, no need to reassign.
- # if next_yearly_bill_start_on:
- # # We want to group orders by date but keep using datetimes.
- # next_yearly_bill_start_on = next_yearly_bill_start_on.replace(
- # minute=0, hour=0, second=0, microsecond=0)
- # bucket = unpaid_orders['yearly'].get(next_yearly_bill_start_on)
- # if bucket == None:
- # unpaid_orders['yearly'][next_yearly_bill_start_on] = [order]
- # else:
- # unpaid_orders['yearly'][next_yearly_bill_start_on] = bucket + [order]
- # else:
- # if previous_bill == None or previous_bill.ending_date < ending_date:
- # unpaid_orders['monthly_or_less'].append(order)
+ # Store for bill generation. One bucket per day of month with a starting bill.
+ # bucket is a reference here, no need to reassign.
+ if next_yearly_bill_start_on:
+ # We want to group orders by date but keep using datetimes.
+ next_yearly_bill_start_on = next_yearly_bill_start_on.replace(
+ minute=0, hour=0, second=0, microsecond=0)
+ bucket = unpaid_orders['yearly'].get(next_yearly_bill_start_on)
+ if bucket == None:
+ unpaid_orders['yearly'][next_yearly_bill_start_on] = [order]
+ else:
+ unpaid_orders['yearly'][next_yearly_bill_start_on] = bucket + [order]
+ else:
+ if previous_bill == None or previous_bill.ending_date < ending_date:
+ unpaid_orders['monthly_or_less'].append(order)
# Handle working month's billing.
if len(unpaid_orders['monthly_or_less']) > 0:
@@ -560,25 +710,21 @@ class Bill(models.Model):
class BillRecord():
"""
- Entry of a bill, dynamically generated from an order.
+ Entry of a bill, dynamically generated from order records.
"""
- def __init__(self, bill, order):
+ def __init__(self, bill, order_record):
self.bill = bill
- self.order = order
- self.recurring_price = order.recurring_price
- self.recurring_period = order.recurring_period
- self.description = order.description
+ self.order = order_record.order
+ self.recurring_price = order_record.recurring_price
+ self.recurring_period = order_record.recurring_period
+ self.description = order_record.description
if self.order.starting_date >= self.bill.starting_date:
- self.one_time_price = order.one_time_price
+ self.one_time_price = order_record.one_time_price
else:
self.one_time_price = 0
- # Set decimal context for amount computations.
- # XXX: understand why we need +1 here.
- decimal.getcontext().prec = AMOUNT_DECIMALS + 1
-
@property
def recurring_count(self):
# Compute billing delta.
@@ -599,25 +745,25 @@ class BillRecord():
# TODO: refactor this thing?
# TODO: weekly
- # if self.recurring_period == RecurringPeriod.PER_YEAR:
- # # XXX: Should always be one => we do not bill for more than one year.
- # # TODO: check billed_delta is ~365 days.
- # return 1
- # elif self.recurring_period == RecurringPeriod.PER_MONTH:
- # days = ceil(billed_delta / timedelta(days=1))
+ if self.recurring_period == RecurringPeriod.PER_YEAR:
+ # XXX: Should always be one => we do not bill for more than one year.
+ # TODO: check billed_delta is ~365 days.
+ return 1
+ elif self.recurring_period == RecurringPeriod.PER_MONTH:
+ days = ceil(billed_delta / timedelta(days=1))
- # # Monthly bills always cover one single month.
- # if (self.bill.starting_date.year != self.bill.starting_date.year or
- # self.bill.starting_date.month != self.bill.ending_date.month):
- # raise Exception('Bill {} covers more than one month. Cannot bill PER_MONTH.'.
- # format(self.bill.uuid))
+ # Monthly bills always cover one single month.
+ if (self.bill.starting_date.year != self.bill.starting_date.year or
+ self.bill.starting_date.month != self.bill.ending_date.month):
+ raise Exception('Bill {} covers more than one month. Cannot bill PER_MONTH.'.
+ format(self.bill.uuid))
- # # XXX: minumal length of monthly order is to be enforced somewhere else.
- # (_, days_in_month) = monthrange(
- # self.bill.starting_date.year,
- # self.bill.starting_date.month)
- # return round(days / days_in_month, AMOUNT_DECIMALS)
- if self.recurring_period == RecurringPeriod.PER_WEEK:
+ # XXX: minumal length of monthly order is to be enforced somewhere else.
+ (_, days_in_month) = monthrange(
+ self.bill.starting_date.year,
+ self.bill.starting_date.month)
+ return days / days_in_month
+ elif self.recurring_period == RecurringPeriod.PER_WEEK:
weeks = ceil(billed_delta / timedelta(week=1))
return weeks
elif self.recurring_period == RecurringPeriod.PER_DAY:
@@ -633,7 +779,7 @@ class BillRecord():
return 0
else:
raise Exception('Unsupported recurring period: {}.'.
- format(self.order.recurring_period))
+ format(record.recurring_period))
@property
def vat_rate(self):
@@ -662,12 +808,10 @@ class Order(models.Model):
on_delete=models.CASCADE,
editable=False)
billing_address = models.ForeignKey(BillingAddress, on_delete=models.CASCADE)
- description = models.TextField()
- replaced_by = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True)
# TODO: enforce ending_date - starting_date to be larger than recurring_period.
creation_date = models.DateTimeField(auto_now_add=True)
- starting_date = models.DateTimeField(default=timezone.now)
+ starting_date = models.DateTimeField()
ending_date = models.DateTimeField(blank=True,
null=True)
@@ -675,120 +819,13 @@ class Order(models.Model):
editable=False,
blank=True)
- recurring_period = models.IntegerField(choices = RecurringPeriod.choices, default = RecurringPeriod.PER_30D)
-
- one_time_price = models.DecimalField(default=0.0,
- max_digits=AMOUNT_MAX_DIGITS,
- decimal_places=AMOUNT_DECIMALS,
- validators=[MinValueValidator(0)])
-
- recurring_price = models.DecimalField(default=0.0,
- max_digits=AMOUNT_MAX_DIGITS,
- decimal_places=AMOUNT_DECIMALS,
- validators=[MinValueValidator(0)])
-
- replaced_by = models.ForeignKey('self',
- related_name='supersede',
- on_delete=models.PROTECT,
- blank=True,
- null=True)
-
- depends_on = models.ForeignKey('self',
- related_name='parent_of',
- on_delete=models.PROTECT,
- blank=True,
- null=True)
-
- def active_before(self, ending_date):
- # Was this order started before the specified ending date?
- if self.starting_date <= ending_date:
- if self.ending_date:
- if self.ending_date > ending_date:
- pass
-
- @property
- def is_recurring(self):
- return not self.recurring_period == RecurringPeriod.ONE_TIME
-
- @property
- def is_terminated(self):
- return self.ending_date != None and self.ending_date < timezone.now()
-
- def is_terminated_at(self, a_date):
- return self.ending_date != None and self.ending_date < timezone.now()
-
- def terminate(self):
- if not self.is_terminated:
- self.ending_date = timezone.now()
- self.save()
-
- def is_to_be_charged_in(year, month):
- pass
+ recurring_period = models.CharField(max_length=32,
+ choices = RecurringPeriod.choices,
+ default = RecurringPeriod.PER_MONTH)
# Trigger initial bill generation at order creation.
def save(self, *args, **kwargs):
- if self.ending_date and self.ending_date < self.starting_date:
- raise ValidationError("End date cannot be before starting date")
-
- super().save(*args, **kwargs)
-
- def generate_initial_bill(self):
- return Bill.generate_for(self.starting_date.year, self.starting_date.month, self.owner)
-
- # Used by uncloud_pay tests.
- @property
- def bills(self):
- return Bill.objects.filter(order=self)
-
- @staticmethod
- def from_product(product, **kwargs):
- # FIXME: this is only a workaround.
- billing_address = BillingAddress.get_preferred_address_for(product.owner)
- if billing_address == None:
- raise Exception("Owner does not have a billing address!")
-
- return Order(description=product.description,
- one_time_price=product.one_time_price,
- recurring_price=product.recurring_price,
- billing_address=billing_address,
- owner=product.owner,
- **kwargs)
-
- def __str__(self):
- return "Order {} created at {}, {}->{}, recurring period {}. One time price {}, recurring price {}".format(
- self.uuid, self.creation_date,
- self.starting_date, self.ending_date,
- self.recurring_period,
- self.one_time_price,
- self.recurring_price)
-
-class OrderTimothee(models.Model):
- uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
- owner = models.ForeignKey(get_user_model(),
- on_delete=models.CASCADE,
- editable=False)
- billing_address = models.ForeignKey(BillingAddress, on_delete=models.CASCADE)
-
- # TODO: enforce ending_date - starting_date to be larger than recurring_period.
- creation_date = models.DateTimeField(auto_now_add=True)
- starting_date = models.DateTimeField(default=timezone.now)
- ending_date = models.DateTimeField(blank=True,
- null=True)
-
- bill = models.ManyToManyField(Bill,
- editable=False,
- blank=True)
-
- recurring_period = models.IntegerField(choices = RecurringPeriod.choices,
- default = RecurringPeriod.PER_30D)
-
- # Trigger initial bill generation at order creation.
- def save(self, *args, **kwargs):
- if self.ending_date and self.ending_date < self.starting_date:
- raise ValidationError("End date cannot be before starting date")
-
- super().save(*args, **kwargs)
-
+ super(Order, self).save(*args, **kwargs)
Bill.generate_for(self.starting_date.year, self.starting_date.month, self.owner)
@property
@@ -814,14 +851,6 @@ class OrderTimothee(models.Model):
recurring_price=recurring_price,
description=description)
- def __str__(self):
- return "Order {} created at {}, {}->{}, recurring period {}. Price one time {}, recurring {}".format(
- self.uuid, self.creation_date,
- self.starting_date, self.ending_date,
- self.recurring_period,
- self.one_time_price,
- self.recurring_price)
-
class OrderRecord(models.Model):
@@ -882,37 +911,22 @@ class Product(UncloudModel):
null=True)
# Default period for all products
- default_recurring_period = RecurringPeriod.PER_30D
+ default_recurring_period = RecurringPeriod.PER_MONTH
# Used to save records.
def save(self, *args, **kwargs):
# _state.adding is switched to false after super(...) call.
being_created = self._state.adding
- # First time saving - create an order
- if not self.order:
- billing_address = BillingAddress.get_preferred_address_for(self.owner)
- print(billing_address)
+ super(Product, self).save(*args, **kwargs)
- if not billing_address:
- raise ValidationError("Cannot order without a billing address")
-
- # FIXME: allow user to choose recurring_period
- self.order = Order.objects.create(owner=self.owner,
- billing_address=billing_address,
- one_time_price=self.one_time_price,
- recurring_period=self.default_recurring_period,
- recurring_price=self.recurring_price)
-
- super().save(*args, **kwargs)
-
- # # Make sure we only create records on creation.
- # if being_created:
- # record = OrderRecord(
- # one_time_price=self.one_time_price,
- # recurring_price=self.recurring_price,
- # description=self.description)
- # self.order.orderrecord_set.add(record, bulk=False)
+ # Make sure we only create records on creation.
+ if being_created:
+ record = OrderRecord(
+ one_time_price=self.one_time_price,
+ recurring_price=self.recurring_price,
+ description=self.description)
+ self.order.orderrecord_set.add(record, bulk=False)
@property
def recurring_price(self):
@@ -922,6 +936,10 @@ class Product(UncloudModel):
def one_time_price(self):
return 0
+ @property
+ def recurring_period(self):
+ return self.order.recurring_period
+
@property
def billing_address(self):
return self.order.billing_address
@@ -975,26 +993,26 @@ class Product(UncloudModel):
"""
- if self.default_recurring_period == RecurringPeriod.PER_365D:
- if requested_period == RecurringPeriod.PER_365D:
+ if self.default_recurring_period == RecurringPeriod.PER_YEAR:
+ if requested_period == RecurringPeriod.PER_YEAR:
return self.recurring_price
- if requested_period == RecurringPeriod.PER_30D:
+ if requested_period == RecurringPeriod.PER_MONTH:
return self.recurring_price/11.
if requested_period == RecurringPeriod.PER_DAY:
return self.recurring_price/11./28.
- elif self.default_recurring_period == RecurringPeriod.PER_30D:
- if requested_period == RecurringPeriod.PER_365D:
+ elif self.default_recurring_period == RecurringPeriod.PER_MONTH:
+ if requested_period == RecurringPeriod.PER_YEAR:
return self.recurring_price*11
- if requested_period == RecurringPeriod.PER_30D:
+ if requested_period == RecurringPeriod.PER_MONTH:
return self.recurring_price
if requested_period == RecurringPeriod.PER_DAY:
return self.recurring_price/28.
elif self.default_recurring_period == RecurringPeriod.PER_DAY:
- if requested_period == RecurringPeriod.PER_365D:
+ if requested_period == RecurringPeriod.PER_YEAR:
return self.recurring_price*11*28
- if requested_period == RecurringPeriod.PER_30D:
+ if requested_period == RecurringPeriod.PER_MONTH:
return self.recurring_price*28
if requested_period == RecurringPeriod.PER_DAY:
return self.recurring_price
diff --git a/uncloud_pay/serializers.py b/uncloud_django_based/uncloud/uncloud_pay/serializers.py
similarity index 64%
rename from uncloud_pay/serializers.py
rename to uncloud_django_based/uncloud/uncloud_pay/serializers.py
index 9214105..1b5db24 100644
--- a/uncloud_pay/serializers.py
+++ b/uncloud_django_based/uncloud/uncloud_pay/serializers.py
@@ -1,8 +1,5 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers
-from uncloud_auth.serializers import UserSerializer
-from django.utils.translation import gettext_lazy as _
-
from .models import *
###
@@ -37,38 +34,18 @@ class CreatePaymentMethodSerializer(serializers.ModelSerializer):
###
# Orders & Products.
+class OrderRecordSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = OrderRecord
+ fields = ['one_time_price', 'recurring_price', 'description']
+
+
class OrderSerializer(serializers.ModelSerializer):
- owner = serializers.PrimaryKeyRelatedField(queryset=get_user_model().objects.all())
-
- def __init__(self, *args, **kwargs):
- # Don't pass the 'fields' arg up to the superclass
- admin = kwargs.pop('admin', None)
-
- # Instantiate the superclass normally
- super(OrderSerializer, self).__init__(*args, **kwargs)
-
- # Only allows owner in admin mode.
- if not admin:
- self.fields.pop('owner')
-
- def create(self, validated_data):
- billing_address = BillingAddress.get_preferred_address_for(validated_data["owner"])
- instance = Order(billing_address=billing_address, **validated_data)
- instance.save()
-
- return instance
-
- def validate_owner(self, value):
- if BillingAddress.get_preferred_address_for(value) == None:
- raise serializers.ValidationError("Owner does not have a valid billing address.")
-
- return value
-
+ records = OrderRecordSerializer(many=True, read_only=True)
class Meta:
model = Order
- read_only_fields = ['replaced_by', 'depends_on']
- fields = ['uuid', 'owner', 'description', 'creation_date', 'starting_date', 'ending_date',
- 'bill', 'recurring_period', 'recurring_price', 'one_time_price'] + read_only_fields
+ fields = ['uuid', 'creation_date', 'starting_date', 'ending_date',
+ 'bill', 'recurring_period', 'records', 'recurring_price', 'one_time_price']
###
@@ -82,7 +59,7 @@ class BillRecordSerializer(serializers.Serializer):
description = serializers.CharField()
one_time_price = serializers.DecimalField(AMOUNT_MAX_DIGITS, AMOUNT_DECIMALS)
recurring_price = serializers.DecimalField(AMOUNT_MAX_DIGITS, AMOUNT_DECIMALS)
-# recurring_period = serializers.ChoiceField()
+ recurring_period = serializers.ChoiceField(choices=RecurringPeriod.choices)
recurring_count = serializers.DecimalField(AMOUNT_MAX_DIGITS, AMOUNT_DECIMALS)
vat_rate = serializers.DecimalField(AMOUNT_MAX_DIGITS, AMOUNT_DECIMALS)
vat_amount = serializers.DecimalField(AMOUNT_MAX_DIGITS, AMOUNT_DECIMALS)
@@ -92,7 +69,7 @@ class BillRecordSerializer(serializers.Serializer):
class BillingAddressSerializer(serializers.ModelSerializer):
class Meta:
model = BillingAddress
- fields = ['uuid', 'organization', 'name', 'street', 'city', 'postal_code', 'country', 'vat_number']
+ fields = ['uuid', 'name', 'street', 'city', 'postal_code', 'country', 'vat_number']
class BillSerializer(serializers.ModelSerializer):
billing_address = BillingAddressSerializer(read_only=True)
@@ -100,7 +77,7 @@ class BillSerializer(serializers.ModelSerializer):
class Meta:
model = Bill
- fields = ['uuid', 'reference', 'owner', 'amount', 'vat_amount', 'total',
+ fields = ['reference', 'owner', 'amount', 'vat_amount', 'total',
'due_date', 'creation_date', 'starting_date', 'ending_date',
'records', 'final', 'billing_address']
diff --git a/uncloud_pay/stripe.py b/uncloud_django_based/uncloud/uncloud_pay/stripe.py
similarity index 97%
rename from uncloud_pay/stripe.py
rename to uncloud_django_based/uncloud/uncloud_pay/stripe.py
index 2ed4ef2..f23002b 100644
--- a/uncloud_pay/stripe.py
+++ b/uncloud_django_based/uncloud/uncloud_pay/stripe.py
@@ -3,10 +3,10 @@ import stripe.error
import logging
from django.core.exceptions import ObjectDoesNotExist
-from django.conf import settings
-
import uncloud_pay.models
+import uncloud.secrets
+
# Static stripe configuration used below.
CURRENCY = 'chf'
@@ -14,7 +14,7 @@ CURRENCY = 'chf'
# https://stripe.com/docs/payments/save-and-reuse
# For internal use only.
-stripe.api_key = settings.STRIPE_KEY
+stripe.api_key = uncloud.secrets.STRIPE_KEY
# Helper (decorator) used to catch errors raised by stripe logic.
# Catch errors that should not be displayed to the end user, raise again.
@@ -64,7 +64,7 @@ def handle_stripe_error(f):
# Actual Stripe logic.
def public_api_key():
- return settings.STRIPE_PUBLIC_KEY
+ return uncloud.secrets.STRIPE_PUBLIC_KEY
def get_customer_id_for(user):
try:
diff --git a/uncloud_pay/templates/bill.html.j2 b/uncloud_django_based/uncloud/uncloud_pay/templates/bill.html
similarity index 96%
rename from uncloud_pay/templates/bill.html.j2
rename to uncloud_django_based/uncloud/uncloud_pay/templates/bill.html
index c227f43..8f6c217 100644
--- a/uncloud_pay/templates/bill.html.j2
+++ b/uncloud_django_based/uncloud/uncloud_pay/templates/bill.html
@@ -6,7 +6,7 @@
Icons, fonts, etc. are INLINED. This is rather ugly, but as the PDF
generation is based on a local snapshot of the HTML file, URLs are
- screwed if they are not absolute to the *local* filesystem.
+ screwed if they are not absolute.
As this document is used ONLY for bills and ONLY for downloading, I
decided that this is an acceptable uglyness.
@@ -26,7 +26,7 @@
- {{ bill }}
+ Bill name