diff --git a/danix/danixfs.py b/danix/danixfs.py index 31ea415..ad19198 100644 --- a/danix/danixfs.py +++ b/danix/danixfs.py @@ -1,38 +1,39 @@ import uuid, os, settings, app +from settings import MAIN_REPO from time import sleep class Danix(): + @staticmethod + def remove_snapshot(snapshot_name): + return os.system(f"rm -r {MAIN_REPO}.snapshots/{snapshot_name}") + @staticmethod def rm(filesystem_name): - return os.system(f"rm {filesystem_name}") + return os.system(f"rm -r {MAIN_REPO}{filesystem_name}") @staticmethod - def snapshot(filesystem_name): - return os.system(f"tar -czf /opt/danix/{filesystem_name}.tar.gz /opt/danix/{filesystem_name}") + def make_snapshot(filesystem_name, snapshot_name): - @staticmethod - def stop(filesystem_name): + os.system(f"mkdir {MAIN_REPO}.snapshots/{snapshot_name}") + resp = os.system(f"tar -czf {MAIN_REPO}.snapshots/{snapshot_name}/{filesystem_name}.tar.gz {MAIN_REPO}{filesystem_name}/") - print("Wait a minute: Stoping subsystem") - resp = Danix.snapshot(filesystem_name) - os.system(f"rm -r /opt/danix/{filesystem_name}") - return resp - + @staticmethod - def start(filesystem_name): + def back_snapshot(filesystem_name, snapshot_name): - print("Wait a minute: Starting subsystem") - os.system(f"mkdir /opt/danix/{filesystem_name}") - resp = os.system(f"tar -xf /opt/danix/{filesystem_name}.tar.gz -C /opt/danix/{filesystem_name} > /dev/null") - os.system(f"rm -r /opt/danix/{filesystem_name}.tar.gz > /dev/null") - return resp + os.system(f"rm -r {MAIN_REPO}{filesystem_name} > /dev/null") + resp1 = os.system(f"tar -xf {MAIN_REPO}.snapshots/{snapshot_name}/{filesystem_name}.tar.gz -C {MAIN_REPO}") + resp2 = os.system(f"mv {MAIN_REPO}/opt/danix/{filesystem_name} {MAIN_REPO}") + resp3 = os.system(f"rm -r {MAIN_REPO}opt") + return 0 if resp1+resp2+resp3 == 0 else 1 + @staticmethod def navigate(filesystem_uuid): - return os.system(f"chroot /opt/danix/{filesystem_uuid}/danixfs sh /opt/danix/init.d/init.sh") + return os.system(f"chroot {MAIN_REPO}{filesystem_uuid}/danixfs sh {MAIN_REPO}init.d/init.sh") @staticmethod def build_environment(packages, filesystem_uuid): @@ -42,7 +43,7 @@ class Danix(): os.system(f"curl --silent -LO --output-dir /tmp/{filesystem} {settings.REPO_NAME}/{settings.ROOT_FS}") os.system(f"tar -xf /tmp/{filesystem}/{settings.ROOT_FS} -C /tmp/{filesystem}") os.system(f"rm /tmp/{filesystem}/{settings.ROOT_FS}") - os.system(f"mv /tmp/{filesystem} /opt/danix/") + os.system(f"mv /tmp/{filesystem} {MAIN_REPO}") print("\nPlease! Wait a moment!!") @@ -50,11 +51,12 @@ class Danix(): print(f"Installing {len(packages)} packages\n") for package in packages: - os.system(f"chroot /opt/danix/{filesystem}/danixfs apk add {package}") + os.system(f"chroot {MAIN_REPO}{filesystem}/danixfs apk add {package}") - os.system(f"chroot /opt/danix/{filesystem}/danixfs apk add ruby") - os.system(f"chroot /opt/danix/{filesystem}/danixfs gem install lolcat") + os.system(f"chroot {MAIN_REPO}{filesystem}/danixfs apk add fish") + os.system(f"chroot {MAIN_REPO}{filesystem}/danixfs apk add ruby") + os.system(f"chroot {MAIN_REPO}{filesystem}/danixfs gem install lolcat") print(f"Environment builded succesfully!") print("0 erros reported!") diff --git a/danix/db/migrations/0002_alter_environment_created_snapshot.py b/danix/db/migrations/0002_alter_environment_created_snapshot.py new file mode 100644 index 0000000..3923fef --- /dev/null +++ b/danix/db/migrations/0002_alter_environment_created_snapshot.py @@ -0,0 +1,32 @@ +# Generated by Django 4.2.1 on 2023-05-15 17:34 + +import datetime +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='environment', + name='created', + field=models.DateField(default=datetime.datetime(2023, 5, 15, 17, 34, 29, 592476)), + ), + migrations.CreateModel( + name='Snapshot', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('snapshot_name', models.UUIDField()), + ('created', models.DateField(default=datetime.datetime(2023, 5, 15, 17, 34, 29, 592767))), + ('environment_id', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='db.environment')), + ], + options={ + 'db_table': 'snapshot', + }, + ), + ] diff --git a/danix/db/migrations/0003_alter_environment_created_alter_snapshot_created.py b/danix/db/migrations/0003_alter_environment_created_alter_snapshot_created.py new file mode 100644 index 0000000..9f7503e --- /dev/null +++ b/danix/db/migrations/0003_alter_environment_created_alter_snapshot_created.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.1 on 2023-05-15 18:47 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0002_alter_environment_created_snapshot'), + ] + + operations = [ + migrations.AlterField( + model_name='environment', + name='created', + field=models.DateField(default=datetime.datetime(2023, 5, 15, 18, 47, 7, 58667)), + ), + migrations.AlterField( + model_name='snapshot', + name='created', + field=models.DateField(default=datetime.datetime(2023, 5, 15, 18, 47, 7, 58959)), + ), + ] diff --git a/danix/db/migrations/0004_snapshot_last_alter_environment_created_and_more.py b/danix/db/migrations/0004_snapshot_last_alter_environment_created_and_more.py new file mode 100644 index 0000000..2d8baa5 --- /dev/null +++ b/danix/db/migrations/0004_snapshot_last_alter_environment_created_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.1 on 2023-05-15 18:56 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0003_alter_environment_created_alter_snapshot_created'), + ] + + operations = [ + migrations.AddField( + model_name='snapshot', + name='last', + field=models.BooleanField(default=True), + ), + migrations.AlterField( + model_name='environment', + name='created', + field=models.DateField(default=datetime.datetime(2023, 5, 15, 18, 56, 13, 154851)), + ), + migrations.AlterField( + model_name='snapshot', + name='created', + field=models.DateField(default=datetime.datetime(2023, 5, 15, 18, 56, 13, 155149)), + ), + ] diff --git a/danix/db/models.py b/danix/db/models.py index 87c6842..3f598fc 100644 --- a/danix/db/models.py +++ b/danix/db/models.py @@ -1,9 +1,10 @@ from django.db import models +import uuid from danixfs import Danix from datetime import datetime +from settings import SNAPSHOT_LIMIT from django.core.exceptions import ValidationError - class Environment(models.Model): filesystem_name = models.UUIDField() status = models.BooleanField(default=True) @@ -18,14 +19,20 @@ class Environment(models.Model): environment = Environment.objects.filter(filesystem_name=filesystem_name) if environment.count() == 0: - print("Environment doenst exist!") + print("Environment does exist!") exit(1) else: - resp = Danix.navigate(filesystem_name) + if environment.first().status: - if resp != 0: - print("Error!") + resp = Danix.navigate(filesystem_name) + + if resp != 0: + print("Error!") + exit(1) + else: + print("Error! The environment is spopped!") exit(1) + except ValidationError as error: print("Environment doenst exist!") exit(1) @@ -36,7 +43,7 @@ class Environment(models.Model): environment = Environment.objects.filter(filesystem_name=filesystem_name) if environment.count() == 0: - print("Environment doesnt exist!") + print("Environment does not exist!") exit(1) else: @@ -54,19 +61,14 @@ class Environment(models.Model): if environment.count() == 0: - print("Environment doesnt exist!") + print("Environment does not exist!") exit(1) else: - resp = Danix.start(filesystem_name) - if resp == 0: - env = environment.first() - env.status = True - env.save() - print("Environment started successfully!") - exit(0) - else: - print("Starting error!") - exit(1) + env = environment.first() + env.status = True + env.save() + print("Environment started successfully!") + exit(0) @staticmethod def set_active(filesystem_name): @@ -78,30 +80,26 @@ class Environment(models.Model): @staticmethod def stop_environment(filesystem_name): - resp = Danix.stop(filesystem_name) + + environment = Environment.objects.filter(filesystem_name=filesystem_name) - if resp == 0: - environment = Environment.objects.filter(filesystem_name=filesystem_name) - - if environment.count() == 0: - print("Environment doesnt exist!") - exit(1) - else: - env = environment.first() - - env.status = False - env.save() - - print("Environment stopped successfully!") - exit(0) + if environment.count() == 0: + print("Environment does not exist!") + exit(1) + else: + env = environment.first() + env.status = False + env.save() + print("Environment stopped successfully!") + exit(0) @staticmethod def list_environments(): environments = Environment.objects.all() - print("==================================================================================================================================") - print("ENVIRONMENT NAME | TEMPLATE | CREATED | CONTAINER NAME | IMAGE | STATUS") - print("==================================================================================================================================") + print("============================================================================================================================================") + print("| ENVIRONMENT NAME | TEMPLATE | CREATED | SUBSYSTEM NAME | IMAGE | STATUS | SIZE |") + print("=============================================================================================================================================") if environments.count() > 0: for environment in environments: @@ -113,11 +111,131 @@ class Environment(models.Model): repeat_template = (6-len(template)) * ' ' - print(f"{name[0:11]}{repeat} {template}{repeat_template} {environment.created} {environment.filesystem_name} Alpine {status_icon}") + print(f" {name[0:11]}{repeat} {template}{repeat_template} {environment.created} {environment.filesystem_name} Alpine {status_icon} 11 GB") - print("==================================================================================================================================") + print("=============================================================================================================================================") class Meta: db_table = "environment" + +class Snapshot(models.Model): + + snapshot_name = models.UUIDField() + environment_id = models.ForeignKey(Environment, null=True, on_delete=models.SET_NULL) + created = models.DateField(default=datetime.now()) + last = models.BooleanField(default=True) + + @staticmethod + def rm_snapshot(snapshot_name): + try: + snapshot = Snapshot.objects.filter(snapshot_name=snapshot_name) + + if snapshot.count() > 0: + + resp = Danix.remove_snapshot(snapshot_name) + + if resp == 0: + Snapshot.objects.get(snapshot_name=snapshot_name).delete() + + print("Snapshot removed successfully") + exit(0) + else: + print("Error: Snapshot can not remove") + exit(1) + else: + print("Snapshot does not exist!") + exit(1) + + except ValidationError: + print("Snapshot does not exist!") + exit(1) + + @staticmethod + def back_snapshot(snapshot_name): + try: + snapshot = Snapshot.objects.filter(snapshot_name=snapshot_name) + + if snapshot.count() > 0: + filesystem_name = Environment.objects.filter(id=snapshot.first().environment_id.id).first().filesystem_name + + resp = Danix.back_snapshot(filesystem_name, snapshot_name) + + if resp == 0: + print("Snapshot backed successfully") + exit(0) + else: + print("Error: Snapshot can not back") + exit(1) + else: + print("Snapshot does not exist!") + exit(1) + except ValidationError: + + print("Snapshot does not exist!") + exit(1) + + @staticmethod + def create(subsystem_name): + try: + environment = Environment.objects.get(filesystem_name=subsystem_name) + environment_id = environment.id + snapshots = Snapshot.objects.filter(environment_id=environment_id) + + if snapshots.count() > int(SNAPSHOT_LIMIT): + print("Snapshot limit exceeded! Please remove 1 snapshot to continue") + exit(1) + else: + + for snapshot in snapshots: + + snapshot.last = False + snapshot.save() + + snapshot_name = uuid.uuid4() + + snapshot = Snapshot.objects.create(snapshot_name=snapshot_name,environment_id=environment).save() + resp = Danix.make_snapshot(subsystem_name, snapshot_name) + + if resp == 0: + print("Snapshot created successfully") + print(f"Snapshot name {snapshot_name}") + exit(0) + else: + + Snapshot.objects.get(snapshot_name=snapshot_name,environment_id=environment).delete() + print("Snapshot create error!") + exit(1) + + except ValidationError: + + print("Snapshot create error: Environment does not exist!") + exit(1) + + @staticmethod + def list_snapshots(): + + snapshots = Snapshot.objects.all() + + print("=========================================================================================================================================") + print("| SNAPSHOT NAME | ENVIRONMENT NAME | CREATED | LAST SNAPSHOT | SIZE |") + print("=========================================================================================================================================") + + if snapshots.count() > 0: + for snapshot in snapshots: + + name = str(snapshot.snapshot_name) + lastsnapshot_icon = "🟢 Yes" if snapshot.last else "🟠 No" + + if snapshot.environment_id: + environment_name = Environment.objects.filter(id=snapshot.environment_id.id).first().filesystem_name + else: + repeated = 14*' ' + environment_name = f'Environment Removed 🔴{repeated}' + + print(f" {name} {environment_name} {snapshot.created} {lastsnapshot_icon} 11 GB") + print("==========================================================================================================================================") + + class Meta: + db_table = "snapshot" # Create your models here. \ No newline at end of file diff --git a/danix/main.py b/danix/main.py index 4f0d81a..45ee278 100644 --- a/danix/main.py +++ b/danix/main.py @@ -3,7 +3,7 @@ import app from utils import is_root from templates import Languanges -from db.models import Environment +from db.models import Environment, Snapshot if not is_root(): print("Please run this program with sudo!") @@ -13,15 +13,20 @@ parser = argparse.ArgumentParser(prog="Danix", add_help=True) usages = parser.add_argument_group("usages") -usages.add_argument("-l", "--list", action="store_true" , help="List all environments avaliable", required=False) -usages.add_argument("-S", "--start", help="Start system environment", required=False) -usages.add_argument("-s", "--stop", help="Stop system environment", required=False) -usages.add_argument("-r", "--rm", help="Remove system environment", required=False) -usages.add_argument("-n", "--navigate", help="Navigate inside the environment", required=False) +usages.add_argument("-l", "--list", action="store_true" , help="List all environments avaliable", required=False) +usages.add_argument("-sl", "--snapshotlist", action="store_true", help="List all subsystems snapshots", required=False) -createenv = parser.add_argument_group("createenv") +usages.add_argument("-S", "--start", help="Start system environment", required=False) +usages.add_argument("-s", "--stop", help="Stop system environment", required=False) +usages.add_argument("-r", "--rm", help="Remove system environment", required=False) +usages.add_argument("-n", "--navigate", help="Navigate inside the environment", required=False) + +usages.add_argument("-sr", "--snapshotremove", help="Remove snapshot", required=False) +usages.add_argument("-sc", "--snapshotcreate", help="Create snapshot", required=False) +usages.add_argument("-sb", "--snapshotback", help="Back snapshot", required=False) + +usages.add_argument("-o", "--option", choices=["clike", "java", "python", "ruby"], required=False) -createenv.add_argument("-o", "--option", choices=["clike", "java", "python", "ruby"], required=False) #fullstack_package = parser.add_argument_group("WebStack", "") #fullstack_package.add_argument("-d","--db", type=str, required=False) @@ -49,11 +54,35 @@ if args.navigate: if args.start: Environment.start_environment(args.start) -if args.list: - Environment.list_environments() - if args.rm: Environment.rm_environment(args.rm) if args.stop: - Environment.stop_environment(args.stop) \ No newline at end of file + Environment.stop_environment(args.stop) + +if args.snapshotcreate: + user_confirm = input("Type 'y' to continue: ") + + if user_confirm == 'y': + Snapshot.create(args.snapshotcreate) + print("[Danix]: System abort!") + +if args.list: + Environment.list_environments() + +if args.snapshotlist: + Snapshot.list_snapshots() + +if args.snapshotback: + user_confirm = input("Type 'y' to continue: ") + + if user_confirm == 'y': + Snapshot.back_snapshot(args.snapshotback) + print("[Danix]: System abort!") + +if args.snapshotremove: + user_confirm = input("Type y to continue: ") + + if user_confirm == 'y': + Snapshot.rm_snapshot(args.snapshotremove) + print("[Danix]: System abort!") \ No newline at end of file diff --git a/danix/requirements.txt b/danix/requirements.txt index 6763591..52eb013 100644 --- a/danix/requirements.txt +++ b/danix/requirements.txt @@ -1,3 +1,3 @@ django argparse -django-environ \ No newline at end of file +django-environ diff --git a/danix/settings.py b/danix/settings.py index 4fb2269..e6035c1 100644 --- a/danix/settings.py +++ b/danix/settings.py @@ -31,6 +31,8 @@ SECRET_KEY = 'django-insecure-emd#8q%fzc#hdb=$ursx36s=qd2)7)q#d@j*(lpzuzja6_39y4 REPO_NAME=env('REPO_NAME') ROOT_FS=env('ROOT_FS') +SNAPSHOT_LIMIT=env("SNAPSHOT_LIMIT") +MAIN_REPO=env('MAIN_REPO') # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -130,4 +132,4 @@ STATIC_URL = 'static/' # Default primary key field type # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' \ No newline at end of file +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/danix/templates.py b/danix/templates.py index 49b3827..9941a08 100644 --- a/danix/templates.py +++ b/danix/templates.py @@ -4,7 +4,7 @@ from danixfs import Danix class Essentials(): - packages = ["fish", "vim", "nano", "micro", "git"] + packages = ["vim", "nano", "micro", "git"] class Languanges(): @@ -13,6 +13,8 @@ class Languanges(): packages = ["python3", "py3-pip"] def install(self, environment_name, template): + + packages = input("") filesystem_name = uuid.uuid4() environment_count = Environment.objects.filter(filesystem_name=filesystem_name).count() @@ -29,6 +31,45 @@ class Languanges(): packages = ["gcc", "g++", "clang", "rust cargo"] + def select_packages(self): + + essentials_packages = Essentials().packages + + i = 1 + + for essential in essentials_packages: + + print(f'[{i}] - Package {essential}') + i += 1 + + print('Select essentials packages:') + print('Example: 1 2 3 to install vim, nano, and git') + essentials_packages_selected = input() + + list_essentials_packages = [] + + for pos in essentials_packages_selected.split(" "): + list_essentials_packages.append(essentials_packages[int(pos)-1]) + + i = 1 + + for package in self.packages: + print(f"[{i}] - Package {package}") + i += 1 + + print('Select essentials packages:') + print('Example: 1 2 to install gcc, g++') + clike_packages_selected = input() + + list_clike_packages = [] + + for pos in clike_packages_selected.split(" "): + list_clike_packages.append(self.packages[int(pos)-1]) + + return list_essentials_packages + list_clike_packages + + + def install(self, environment_name, template): filesystem_name = uuid.uuid4() @@ -38,7 +79,7 @@ class Languanges(): Environment.objects.create(filesystem_name=filesystem_name, template=template, name=environment_name).save() - joined_packages = self.packages + Essentials().packages + joined_packages = self.select_packages() Environment.set_active(filesystem_name) Danix.build_environment(joined_packages, filesystem_name)