First commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/matlabhub/config.yml
|
||||||
|
/build/
|
||||||
|
/matlabhub.egg-info/
|
||||||
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
27
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
27
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<value>
|
||||||
|
<list size="14">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="os" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="multiprocessing" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="sys" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="pickle" />
|
||||||
|
<item index="4" class="java.lang.String" itemvalue="re" />
|
||||||
|
<item index="5" class="java.lang.String" itemvalue="collections" />
|
||||||
|
<item index="6" class="java.lang.String" itemvalue="functools" />
|
||||||
|
<item index="7" class="java.lang.String" itemvalue="inspect" />
|
||||||
|
<item index="8" class="java.lang.String" itemvalue="json" />
|
||||||
|
<item index="9" class="java.lang.String" itemvalue="javabridge" />
|
||||||
|
<item index="10" class="java.lang.String" itemvalue="time" />
|
||||||
|
<item index="11" class="java.lang.String" itemvalue="skimage" />
|
||||||
|
<item index="12" class="java.lang.String" itemvalue="yaml" />
|
||||||
|
<item index="13" class="java.lang.String" itemvalue="pytest-runner" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
11
.idea/matlabhub.iml
generated
Normal file
11
.idea/matlabhub.iml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/matlabhub.egg-info" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
|
||||||
|
<component name="PyCharmProfessionalAdvertiser">
|
||||||
|
<option name="shown" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/matlabhub.iml" filepath="$PROJECT_DIR$/.idea/matlabhub.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
35
README.md
Normal file
35
README.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Matlab Hub
|
||||||
|
|
||||||
|
Start proxied matlab instances from a webpage using Nginx.
|
||||||
|
|
||||||
|
## Preequisites
|
||||||
|
|
||||||
|
Linux with Matlab and Python3
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
make matlabhub/config.py:
|
||||||
|
|
||||||
|
license_file: 'path_to_license.lic'
|
||||||
|
port_range: [10000, 65536]
|
||||||
|
|
||||||
|
install:
|
||||||
|
|
||||||
|
sudo apt install nginx screen
|
||||||
|
sudo pip install matlabhub
|
||||||
|
sudo cp matlab /etc/nginx/sites-available/
|
||||||
|
sudo ln -s /etc/nginx/sites-available/matlab /etc/nginx/sites-enabled/matlab
|
||||||
|
sudo cp matlab.service /lib/systemd/system/
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable nginx
|
||||||
|
sudo systemctl enable matlab
|
||||||
|
sudo service nginx start
|
||||||
|
sudo service matlab start
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Change the user, path to the socket file and the port (default: 8585) in matlab and matlab.service.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Browse to http://127.0.0.1:8585
|
||||||
23
matlab
Normal file
23
matlab
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# nginx configuration
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8585 default_server;
|
||||||
|
listen [::]:8585 default_server;
|
||||||
|
|
||||||
|
root /var/www/html;
|
||||||
|
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location ~ ^/([0-9]+) {
|
||||||
|
proxy_set_header Host '127.0.0.1';
|
||||||
|
proxy_pass http://127.0.0.1:$1;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
include proxy_params;
|
||||||
|
proxy_pass http://unix:/tmp/matlabhub.sock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
17
matlab.service
Normal file
17
matlab.service
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Matlab web server
|
||||||
|
After=network-online.target firewalld.service
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=matlab-user
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/gunicorn --bind unix:/tmp/matlabhub.sock matlabhub.wsgi:application
|
||||||
|
ExecStop=/bin/kill -s STOP $MAINPID
|
||||||
|
ExecReload=/bin/kill -s HUP $MAINPID
|
||||||
|
TimeoutSec=0
|
||||||
|
RestartSec=2
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
96
matlabhub/app.py
Executable file
96
matlabhub/app.py
Executable file
@@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env python3.10
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import random
|
||||||
|
import yaml
|
||||||
|
from flask import Flask, redirect, render_template, request
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), 'config.yml')) as conf_file:
|
||||||
|
config = yaml.safe_load(conf_file)
|
||||||
|
|
||||||
|
|
||||||
|
def is_port_in_use(port):
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
|
return s.connect_ex(('127.0.0.1', port)) == 0
|
||||||
|
|
||||||
|
|
||||||
|
class Matlab:
|
||||||
|
def __init__(self, port, group=None):
|
||||||
|
self.port = int(port)
|
||||||
|
self.group = group
|
||||||
|
self.screen_name = f'matlab_{port}' if group is None else f'matlab_{group}_{port}'
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
command = f'screen -S {self.screen_name} -d -m env MWI_BASE_URL="/{self.port}" ' \
|
||||||
|
f'MWI_APP_PORT={self.port} MLM_LICENSE_FILE={config["license_file"]} matlab-proxy-app'
|
||||||
|
subprocess.Popen(command, shell=True, start_new_session=True)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
subprocess.run(f"screen -S {self.screen_name} -X stuff '^C'", shell=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Hub(dict):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.update({matlab.port: matlab for matlab in self.find_running()})
|
||||||
|
|
||||||
|
|
||||||
|
def find_running(self):
|
||||||
|
p = subprocess.run('screen -list', shell=True, capture_output=True)
|
||||||
|
return [Matlab(port, group) for group, port in re.findall(r'\.matlab_([^_]+)_(\d+)', str(p.stdout))]
|
||||||
|
|
||||||
|
def new(self, group=None):
|
||||||
|
group = re.escape(re.sub(r'[^\w]|_', '', group))
|
||||||
|
ports = list(range(*config['port_range']))
|
||||||
|
random.shuffle(ports)
|
||||||
|
for port in ports:
|
||||||
|
if port not in self and not is_port_in_use(port):
|
||||||
|
self[port] = Matlab(port, group)
|
||||||
|
self[port].start()
|
||||||
|
sleep(5)
|
||||||
|
return redirect(f'/{port}')
|
||||||
|
|
||||||
|
def stop(self, port):
|
||||||
|
self.pop(port).stop()
|
||||||
|
|
||||||
|
def get_groups(self):
|
||||||
|
return list(set(matlab.group for matlab in self.values()))
|
||||||
|
|
||||||
|
def get_ports(self, group=None):
|
||||||
|
return [matlab.port for matlab in self.values() if matlab.group == group]
|
||||||
|
|
||||||
|
|
||||||
|
sys.argv = sys.argv[:1]
|
||||||
|
hub = Hub()
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return redirect('/default')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/<group>')
|
||||||
|
def group(group):
|
||||||
|
return render_template('index.html', ports=hub.get_ports(group), groups=hub.get_groups(), group=group)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/<group>/start/new')
|
||||||
|
def group_start_new(group):
|
||||||
|
return hub.new(group)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/<group>', methods=['POST'])
|
||||||
|
def start_new_post(group):
|
||||||
|
return hub.new(request.form['group'])
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/<group>/stop/<port>')
|
||||||
|
def stop(group, port):
|
||||||
|
hub.stop(int(port))
|
||||||
|
return redirect(f'/{group}')
|
||||||
37
matlabhub/templates/index.html
Normal file
37
matlabhub/templates/index.html
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
.block {
|
||||||
|
padding: 5px;
|
||||||
|
margin: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
border: 5px solid #193f6f;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>Matlab Hub</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="block" align="center">
|
||||||
|
<h1>Matlab Hub</h1>
|
||||||
|
<br />
|
||||||
|
<h4>Start new matlab in group:</h4>
|
||||||
|
<form method="POST" target="_blank">
|
||||||
|
<input name="group" value="{{group}}">
|
||||||
|
<input type="submit" value="start">
|
||||||
|
</form>
|
||||||
|
<br />
|
||||||
|
<h4>Groups:</h4>
|
||||||
|
{% for group in groups %}
|
||||||
|
<a href="/{{group}}">{{group}}</a><br />
|
||||||
|
{% endfor %}
|
||||||
|
<h4>Matlabs in group {{group}}:</h4>
|
||||||
|
{% for port in ports %}
|
||||||
|
<a href="/{{port}}" target="_blank">{{port}}</a> <a href="/{{group}}/stop/{{port}}">stop</a><br />
|
||||||
|
{% endfor %}
|
||||||
|
<br />
|
||||||
|
<a href="https://github.com/wimpomp/matlabhub">Matlab Hub</a> by Wim Pomp
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
matlabhub/wsgi.py
Normal file
3
matlabhub/wsgi.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from matlabhub.app import app as application
|
||||||
26
setup.py
Normal file
26
setup.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import setuptools
|
||||||
|
|
||||||
|
with open('README.md', 'r') as fh:
|
||||||
|
long_description = fh.read()
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
name='matlabhub',
|
||||||
|
version='2022.7.0',
|
||||||
|
author='Wim Pomp @ Lenstra lab NKI',
|
||||||
|
author_email='w.pomp@nki.nl',
|
||||||
|
description='matlabhub',
|
||||||
|
long_description=long_description,
|
||||||
|
long_description_content_type='text/markdown',
|
||||||
|
url='https://github.com/wimpomp/matlabhub',
|
||||||
|
packages=['matlabhub'],
|
||||||
|
classifiers=[
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
||||||
|
'Operating System :: OS Independent',
|
||||||
|
],
|
||||||
|
python_requires='>=3.8',
|
||||||
|
install_requires=['matlab-proxy', 'flask'],
|
||||||
|
entry_points={'console_scripts': ['matlabhub=matlabhub:main'], },
|
||||||
|
package_data={'': ['templates/*', 'config.yml']},
|
||||||
|
include_package_data=True,
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user