فهرست منبع

Added wip scheulder functions

TC pushbot 5 4 سال پیش
والد
کامیت
7f21e7da89

+ 23 - 5
mod/filesystem/filesystem.go

@@ -19,6 +19,7 @@ import (
 	"time"
 
 	db "imuslab.com/arozos/mod/database"
+	"imuslab.com/arozos/mod/filesystem/hybridBackup"
 )
 
 //Options for creating new file system handler
@@ -36,12 +37,20 @@ type FileSystemOpeningOptions struct{
 }
 */
 
+/*
+	An interface for storing data related to a specific hierarchy settings.
+	Example like the account information of network drive,
+	backup mode of backup drive etc
+*/
+type HierarchySpecificConfig interface{}
+
 //System Handler for returing
 type FileSystemHandler struct {
 	Name               string
 	UUID               string
 	Path               string
 	Hierarchy          string
+	HierarchyConfig    HierarchySpecificConfig
 	ReadOnly           bool
 	Parentuid          string
 	InitiationTime     int64
@@ -89,22 +98,30 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 			return &FileSystemHandler{}, errors.New("Mount point not exists!")
 		}
 
+		//Handle Hierarchy branching
+		var hierarchySpecificConfig interface{} = nil
+
 		if option.Hierarchy == "user" {
 			//Create user hierarchy for this virtual device
 			os.MkdirAll(filepath.ToSlash(filepath.Clean(option.Path))+"/users", 0755)
 		}
 
+		if option.Hierarchy == "backup" {
+			//Backup disk. Create an Hierarchy Config for this drive
+			hierarchySpecificConfig = hybridBackup.BackupConfig{
+				DiskUID:          option.Uuid,
+				ParentUID:        option.Parentuid,
+				ParentMountPoint: option.Mountpt,
+				Mode:             option.BackupMode,
+			}
+		}
+
 		//Create the fsdb for this handler
 		fsdb, err := db.NewDatabase(filepath.ToSlash(filepath.Join(filepath.Clean(option.Path), "aofs.db")), false)
 		if err != nil {
 			return &FileSystemHandler{}, errors.New("Unable to create fsdb inside the target path. Is the directory read only?")
 		}
 
-		if option.Hierarchy == "backup" {
-			//This is a backup drive.
-
-		}
-
 		return &FileSystemHandler{
 			Name:               option.Name,
 			UUID:               option.Uuid,
@@ -112,6 +129,7 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 			ReadOnly:           option.Access == "readonly",
 			Parentuid:          option.Parentuid,
 			Hierarchy:          option.Hierarchy,
+			HierarchyConfig:    hierarchySpecificConfig,
 			InitiationTime:     time.Now().Unix(),
 			FilesystemDatabase: fsdb,
 			Filesystem:         fstype,

+ 8 - 0
mod/filesystem/hybridBackup/datatype.go

@@ -0,0 +1,8 @@
+package hybridBackup
+
+type BackupConfig struct {
+	DiskUID          string //The UID of the target fshandler
+	ParentUID        string //Parent virtual disk UUID
+	ParentMountPoint string //Mount point of the parent disk
+	Mode             string //Backup mode
+}

+ 26 - 0
mod/filesystem/hybridBackup/hybridBackup.go

@@ -0,0 +1,26 @@
+package hybridBackup
+
+import "log"
+
+/*
+	Hybrid Backup
+
+	This module handle backup functions from the drive with Hieracchy labeled as "backup"
+	Backup modes suport in this module currently consists of
+
+	Denote P drive as parent drive and B drive as backup drive.
+	1. Smart (smart):
+		- Any new file created in P will be copied to B within 5 minutes
+		- Any file removed in P will be delete from backup after 24 hours
+	2. Nightly (nightly):
+		- The whole P drive will be copied to N drive every night
+	3. Append Only (append)
+		- Any new file created in P will be copied to B within 5 minutes
+		- No file will be removed from B unless drive is fulled (Similar to CCTV recorder)
+
+*/
+
+func HandleBackupProcess(backupConfig *BackupConfig) error {
+	log.Println(backupConfig)
+	return nil
+}

+ 1 - 1
mod/time/scheduler/handlers.go

@@ -48,7 +48,7 @@ func (a *Scheduler) HandleListJobs(w http.ResponseWriter, r *http.Request) {
 	}
 
 	//Return the values as json
-	js, _ := json.Marshal(userCreatedJobs)
+	js, err := json.Marshal(userCreatedJobs)
 	sendJSONResponse(w, string(js))
 }
 

+ 57 - 9
mod/time/scheduler/scheduler.go

@@ -2,6 +2,7 @@ package scheduler
 
 import (
 	"encoding/json"
+	"errors"
 	"io/ioutil"
 	"log"
 	"os"
@@ -16,13 +17,11 @@ import (
 )
 
 /*
-	ArOZ Emulated Crontab
+	ArozOS System Scheduler
 	author: tobychui
 
-	This is not actually a crontab but something similar that provide
-	timered operations for executing commands in agi or bash in an interval
-	bases
-
+	This module provide scheduling executable feature for ArozOS
+	Some feature was migrated from the v1.113 aecron module
 */
 
 type Job struct {
@@ -32,7 +31,9 @@ type Job struct {
 	Admin             bool   //If the creator has admin permission during the creation of this job. If this doesn't match with the runtime instance, this job wille be skipped
 	ExecutionInterval int64  //Execuation interval in seconds
 	BaseTime          int64  //Exeuction basetime. The next interval is calculated using (current time - base time ) % execution interval
+	JobType           string //Job type, accept {file/function}. If not set default to file
 	ScriptFile        string //The script file being called. Can be an agi script (.agi / .js) or shell script (.bat or .sh)
+	ScriptFunc        func() `json:"-"` //The target function to execute
 }
 
 type Scheduler struct {
@@ -65,7 +66,7 @@ func NewScheduler(userHandler *user.UserHandler, gateway *agi.Gateway, cronfile
 	}
 
 	//Create the ArOZ Emulated Crontask
-	aecron := Scheduler{
+	thisScheduler := Scheduler{
 		jobs:        jobs,
 		userHandler: userHandler,
 		gateway:     gateway,
@@ -81,13 +82,13 @@ func NewScheduler(userHandler *user.UserHandler, gateway *agi.Gateway, cronfile
 		for time.Now().Unix()%60 > 0 {
 			time.Sleep(500 * time.Millisecond)
 		}
-		stopChannel := aecron.createTicker(1 * time.Minute)
-		aecron.ticker = stopChannel
+		stopChannel := thisScheduler.createTicker(1 * time.Minute)
+		thisScheduler.ticker = stopChannel
 		log.Println("Emulated Crontab Started - Scheduling Tasks")
 	}()
 
 	//Return the crontask
-	return &aecron, nil
+	return &thisScheduler, nil
 }
 
 //Load a list of jobs from file
@@ -108,6 +109,7 @@ func loadJobsFromFile(cronfile string) ([]*Job, error) {
 	//Convert the json objets to pointer for easy changing by other process
 	jobsPointers := []*Job{}
 	for _, thisJob := range prevousJobs {
+		thisJob.JobType = "file"
 		var newJobPointer Job = thisJob
 		jobsPointers = append(jobsPointers, &newJobPointer)
 	}
@@ -190,6 +192,52 @@ func (a *Scheduler) Close() {
 	}
 }
 
+//Add an job object to system scheduler
+func (a *Scheduler) AddJobToScheduler(job *Job) error {
+	if job.JobType == "" {
+		if job.ScriptFunc == nil && job.ScriptFile == "" {
+			return errors.New("Invalid job file or function")
+		}
+
+		if job.ScriptFunc != nil {
+			job.JobType = "function"
+		} else if job.ScriptFile != "" {
+			job.JobType = "file"
+		}
+
+	}
+	a.jobs = append(a.jobs, job)
+	return nil
+}
+
+//Create a new scheduled function job in the scheduler
+func (a *Scheduler) CreateNewScheduledFunctionJob(name string, desc string, executionInterval int64, targetFunction func()) error {
+	if name == "" || desc == "" {
+		return errors.New("Name or description of a scheduled task cannot be empty")
+	}
+
+	if executionInterval < 60 {
+		return errors.New("The minimum execution interval is 60 seconds.")
+	}
+
+	//Create a new scehduled job
+	newJob := Job{
+		Name:              name,
+		Creator:           "system",
+		Description:       desc,
+		Admin:             true,
+		ExecutionInterval: executionInterval,
+		BaseTime:          time.Now().Unix(),
+		JobType:           "function",
+		ScriptFunc:        targetFunction,
+	}
+
+	//Add the new job to scheduler
+	a.AddJobToScheduler(&newJob)
+
+	return nil
+}
+
 func (a *Scheduler) GetScheduledJobByName(name string) *Job {
 	for _, thisJob := range a.jobs {
 		if thisJob.Name == name {

+ 5 - 4
startup.go

@@ -49,10 +49,11 @@ func RunStartup() {
 	PackagManagerInit() //Start APT service agent
 
 	//7. Kickstart the File System and Desktop
-	SchedulerInit()     //Start System Scheudler
-	FileSystemInit()    //Start FileSystem
-	DesktopInit()       //Start Desktop
-	HardwarePowerInit() //Start host power manager
+	SchedulerInit()        //Start System Scheudler
+	FileSystemInit()       //Start FileSystem
+	DesktopInit()          //Start Desktop
+	HardwarePowerInit()    //Start host power manager
+	FilesystemDaemonInit() //Start File System handler daemon (for backup and other sync process)
 
 	//8 Start AGI and Subservice modules (Must start after module)
 	AGIInit()        //ArOZ Javascript Gateway Interface, must start after fs

+ 25 - 0
storage.go

@@ -8,6 +8,7 @@ import (
 	"path/filepath"
 	"runtime"
 
+	"imuslab.com/arozos/mod/filesystem/hybridBackup"
 	"imuslab.com/arozos/mod/permission"
 
 	fs "imuslab.com/arozos/mod/filesystem"
@@ -103,6 +104,30 @@ func LoadBaseStoragePool() error {
 	return nil
 }
 
+/*
+	Initiate the backup handlers for backup drives
+
+	This function must be called after the scheduler initiated.
+*/
+func FilesystemDaemonInit() {
+	for _, thisHandler := range fsHandlers {
+		if thisHandler.Hierarchy == "backup" {
+			//This is a backup drive. Generate it handler
+			backupConfig := thisHandler.HierarchyConfig.(hybridBackup.BackupConfig)
+			systemScheduler.CreateNewScheduledFunctionJob(thisHandler.UUID+"_backup-daemon",
+				"Backup daemon from "+backupConfig.ParentUID+":/ to "+backupConfig.DiskUID+":/",
+				60*5,
+				func() {
+					err := hybridBackup.HandleBackupProcess(&backupConfig)
+					if err != nil {
+						log.Println(err)
+					}
+				},
+			)
+		}
+	}
+}
+
 //Initialize group storage pool
 func GroupStoragePoolInit() {
 	//Mount permission groups

+ 20 - 3
web/SystemAO/arsm/scheduler.html

@@ -341,11 +341,16 @@
                                     return
                                 }
                             }
+
+                            var scriptLocation = task.ScriptFile;
+                            if (task.JobType == "function"){
+                                scriptLocation = "[Internal Function]"
+                            }
                             $("#scheduleListAllContent").append(`<tr>
                                 <td class="collapsing">
                                     <i class="user icon"></i> ${task.Name} (${task.Creator})
                                 </td>
-                                <td>${task.ScriptFile}</td>
+                                <td>${scriptLocation}</td>
                                 <td>${task.Description}</td>
                                 <td class="right aligned collapsing">Every ${parseSecondsToHumanReadableFormat(task.ExecutionInterval)}</td>
                                 <td class="right aligned collapsing">${moment.unix(task.BaseTime).format('LLL')}</td>
@@ -478,15 +483,27 @@
                                     return
                                 }
                             }
+
+                            var scriptLocation = task.ScriptFile;
+                            if (task.JobType == "function"){
+                                scriptLocation = "[Internal Function]";
+                            }
+
+                            var removebutton = `<button class="ui negative mini button" name="${task.Name}" onclick="removeTask(this);">Remove</button>`;
+                            if (task.JobType == "function"){
+                                //Cannot be removed
+                                removebutton = `[READ ONLY]`;
+                            }
+
                             $("#removeScheduleList").append(`<tr>
                                 <td class="collapsing">
                                     <i class="user icon"></i> ${task.Name} (${task.Creator})
                                 </td>
-                                <td>${task.ScriptFile}</td>
+                                <td>${scriptLocation}</td>
                                 <td>${task.Description}</td>
                                 <td class="right aligned collapsing">Every ${parseSecondsToHumanReadableFormat(task.ExecutionInterval)}</td>
                                 <td class="right aligned collapsing">
-                                    <button class="ui negative mini button" name="${task.Name}" onclick="removeTask(this);">Remove</button>
+                                    ${removebutton}
                                 </td>
                             </tr>`);
                         });