Skip to content
This repository was archived by the owner on Mar 2, 2022. It is now read-only.
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LICENSE.md
README.md
/system/
!/system/server.py
!/system/balena.id
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -105,3 +105,4 @@ system/server.ini
.remote-sync.json
system/osid.desktop
system/run_app.sh
#system/balena.id
30 changes: 30 additions & 0 deletions Dockerfile.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# base-image for python on any machine using a template variable,
# see more about dockerfile templates here: https://www.balena.io/docs/learn/develop/dockerfile/
FROM balenalib/%%BALENA_MACHINE_NAME%%-python:3-stretch-run

# use `install_packages` if you need to install dependencies,
# for instance if you need git, just uncomment the line below.
# RUN install_packages git

# Set our working directory
WORKDIR /usr/src/app

# Copy requirements.txt first for better cache on later pushes
COPY requirements.txt requirements.txt

# pip install python deps from requirements.txt on the resin.io build server
RUN pip install -r requirements.txt

# Install dd & partprobe
RUN apt-get update && apt-get install -y dcfldd parted

# This will copy all files in our root to the working directory in the container
COPY . ./

RUN chmod +x balena_startup.sh && ./balena_startup.sh

# Enable udevd so that plugged dynamic hardware devices show up in our container.
ENV UDEV=1

# main.py will run when container starts up on the device
CMD ["python", "system/server.py"]
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -108,6 +108,10 @@ Then proceed to modify those files to match the system paths for the items in th
* Just make sure the path for the run_app.sh script is defined properly.
* if you use OSID on headless Raspberry, this file is useless.

### Balena cloud deployment
change `balenaAppId` variable in system/balena.id to your `<balena_app_id>`
Deploy app to balena cloud

### Usage

#### Accepted image file
10 changes: 10 additions & 0 deletions balena_startup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
mkdir -p logs
printf "[DuplicatorSettings]
ImagePath = /data
Host = 0.0.0.0
SocketPort = 80
Logs = /usr/src/app/logs
SkeletonLocation = /usr/src/app/www/skeleton.min.css
" > system/server.ini
cat system/balena.id >> system/server.ini
1 change: 1 addition & 0 deletions system/balena.id
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
balenaAppId="<balena_app_id>"
31 changes: 20 additions & 11 deletions system/server.py
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@
class SDCardDupe(object):
@cherrypy.expose
def index(self):

# get host configs from server.ini
config_parse = configparser.ConfigParser()
config_parse.sections()
@@ -23,11 +22,17 @@ def index(self):
www_path = "/".join(os.path.dirname(os.path.realpath(__file__)).split("/")[:-1]) + "/www/"
html_string = open(www_path + 'index.html', 'r').read()
hostname_port = config_parse['DuplicatorSettings']['Host']+":"+config_parse['DuplicatorSettings']['SocketPort']
html_string = html_string.replace("replacewithhostnamehere",hostname_port)
html_string = html_string.replace("replacewithhostnamehere",'/')

css_string = '<style>' + open(config_parse['DuplicatorSettings']['SkeletonLocation'], 'r').read() + '</style>'
html_string = html_string.replace("<style></style>",css_string)
html_string = html_string.replace("$IMG_ROOT$", config_parse['DuplicatorSettings']['ImagePath'])
if 'BalenaAppId' in config_parse['DuplicatorSettings']:
balena_app_id = config_parse['DuplicatorSettings']['BalenaAppId']
else:
balena_app_id = "false"

html_string = html_string.replace("$BALENA_APP_ID$", balena_app_id)
return html_string


@@ -43,7 +48,7 @@ def monitor(self):
www_path = "/".join(os.path.dirname(os.path.realpath(__file__)).split("/")[:-1]) + "/www/"
html_string = open(www_path + 'monitor.html', 'r').read()
hostname_port = config_parse['DuplicatorSettings']['Host']+":"+config_parse['DuplicatorSettings']['SocketPort']
html_string = html_string.replace("replacewithhostnamehere",hostname_port)
html_string = html_string.replace("replacewithhostnamehere",'/')

css_string = '<style>' + open(config_parse['DuplicatorSettings']['SkeletonLocation'], 'r').read() + '</style>'
html_string = html_string.replace("<style></style>",css_string)
@@ -74,7 +79,7 @@ def posted(self,img_file,devices):
# assumptions made, there will be no collisions, dont have to pop element
# but to reduce the cost of loop, will pop element by creating new list
if dev_path in mounted_item:
umount_disk_cmd = "sudo umount %s"%mounted_item
umount_disk_cmd = "umount %s"%mounted_item
subprocess.call(umount_disk_cmd.split(" "))
else:
reduced_list.append(mounted_item)
@@ -97,11 +102,11 @@ def posted(self,img_file,devices):
out.close()

# Run dd command and output status into the progress.info file
dd_cmd = "sudo dcfldd bs=4M if=" + img_file
dd_cmd = "dcfldd bs=4M if=" + img_file
dd_cmd += " of=" + " of=".join(devices)
dd_cmd += " sizeprobe=if statusinterval=1 2>&1 | sudo tee "
dd_cmd += " sizeprobe=if statusinterval=1 2>&1 | tee "
dd_cmd += config_parse['DuplicatorSettings']['Logs'] + "/progress.info"
dd_cmd += " && echo \"osid_completed_task\" | sudo tee -a "
dd_cmd += " && echo \"osid_completed_task\" | tee -a "
dd_cmd += config_parse['DuplicatorSettings']['Logs'] + "/progress.info"

# Planned to run this in localhost only.
@@ -114,7 +119,7 @@ def posted(self,img_file,devices):
subprocess.Popen(['sudo', 'bash', dd_cmd_file], close_fds=True)

hostname_port = config_parse['DuplicatorSettings']['Host']+":"+config_parse['DuplicatorSettings']['SocketPort']
monitor_url = "http://" + hostname_port + "/monitor";
monitor_url = "/monitor";


html_string = "<html><head>"
@@ -140,7 +145,7 @@ def getStatus(self):
out.close()

# pull data from progress.info file and feed back to call
cat_cmd = "sudo cat "+ progress_file
cat_cmd = "cat "+ progress_file
cat_output = str(subprocess.check_output(cat_cmd, shell=True).decode("utf-8"))
if "records in" in cat_output and "records out" in cat_output and "osid_completed_task" in cat_output:
percentage = "100%"
@@ -163,7 +168,7 @@ def getDevices(self):
list_devices = []

# Refresh partition to discover all available medias
refresh_disk_cmd = "sudo /sbin/partprobe"
refresh_disk_cmd = "/sbin/partprobe"
subprocess.check_output(refresh_disk_cmd, shell=True)

# command to get a list of devices on OS
@@ -235,10 +240,14 @@ def getImages(self):
'log.access_file' : config_parse['DuplicatorSettings']['Logs']+"/access.log",
'log.screen': False,
'tools.sessions.on': True
},
'/favicon.ico':{
'tools.staticfile.on': True,
'tools.staticfile.filename': "/".join(os.path.dirname(os.path.realpath(__file__)).split("/")[:-1]) + "/www/favicon.ico"
}
}

# create a daemon for cherrpy so it will create a thread when started
cherrypy.process.plugins.Daemonizer(cherrypy.engine).subscribe()
#cherrypy.process.plugins.Daemonizer(cherrypy.engine).subscribe()

cherrypy.quickstart(SDCardDupe(), '/', conf)
Binary file added www/favicon.ico
Binary file not shown.
Loading