First commit

This commit is contained in:
Wim Pomp
2022-08-30 13:04:14 +02:00
parent 359f330f11
commit e6acbd7c7f
15 changed files with 308 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/matlabhub/config.yml
/build/
/matlabhub.egg-info/

3
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View 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>

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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}')

View 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
View File

@@ -0,0 +1,3 @@
import os
import sys
from matlabhub.app import app as application

26
setup.py Normal file
View 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,
)