Skip to content

Commit 609243e

Browse files
committed
Added tut 15
1 parent d7b1c4d commit 609243e

8 files changed

Lines changed: 92 additions & 33 deletions

File tree

README.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,17 @@ Later we will get onto more complicated topics such as reaching a consensus of w
1414

1515
Questions comments and suggestions can be raised the specific blog post or by using issues here.
1616

17-
## Tutorial 14 - A simple webserver
17+
## Tutorial 15 - A more complex webserver
1818

19-
This tutorial focuses on creating a simple webserver that displays
20-
the status of the master using the python library Bottle.
21-
This will form the basis of having a simple web interface to view
22-
the progress of the cluster and control it.
19+
This tutorial adds to the previous one by improving the display of the webserver
20+
by including information about the slaves attached. In addition to this the
21+
slaves are modified to use a UUID rather than a random integer to define them.
2322

24-
The next tutorial will focus on improving the webserver to display
25-
slave information in addition to the current information.
23+
The next tutorial will focus on cleaning up the codebase in preparation for new features.
2624

2725
The full details for
28-
[Tutorial 14 - A simple webserver](
29-
https://chewett.co.uk/blog/2127/raspberry-pi-cluster-node-14-a-simple-webserver/
26+
[Tutorial 15 - A more complex webserver](
27+
https://chewett.co.uk/blog/2179/raspberry-pi-cluster-node-15-a-more-complex-webserver/
3028
)
3129

3230
## Requirements

RpiCluster/RpiBasicSlaveThread.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class RpiBasicSlaveThread(threading.Thread):
1313

1414
def __init__(self, master_ip, socket_port):
1515
threading.Thread.__init__(self)
16-
self.client_number = random.randint(1, 100000)
16+
self.uuid = None
1717
self.server_address = (master_ip, socket_port)
1818
self.sock = None
1919

@@ -36,6 +36,11 @@ def run(self):
3636

3737
try:
3838
logger.info("Sending an initial hello to master")
39+
send_message(self.sock, create_payload("uuid", "info"))
40+
message = get_message(self.sock)
41+
self.uuid = message['payload']
42+
logger.info("My assigned UUID is " + self.uuid)
43+
3944
send_message(self.sock, create_payload(get_base_machine_info(), 'computer_details'))
4045
send_message(self.sock, create_payload("computer_details", "info"))
4146

@@ -51,7 +56,7 @@ def run(self):
5156

5257
def perform_action(self):
5358
logger.info("Now sending a keepalive to the master")
54-
send_message(self.sock, create_payload("I am still alive, client: {num}".format(num=self.client_number)))
59+
send_message(self.sock, create_payload("I am still alive, client: {num}".format(num=self.uuid)))
5560
time.sleep(5)
5661

5762

RpiCluster/RpiClusterClient.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import threading
33
import time
4+
import uuid
45
from DataPackager import get_message, create_payload, send_message
56
from RpiClusterExceptions import DisconnectionException
67
from MachineInfo import get_base_machine_info
@@ -9,10 +10,13 @@
910

1011
class RpiClusterClient(threading.Thread):
1112

12-
def __init__(self, clientsocket, address):
13+
def __init__(self, master, clientsocket, address):
1314
threading.Thread.__init__(self)
15+
self.uuid = uuid.uuid4().hex
16+
self.master = master
1417
self.clientsocket = clientsocket
1518
self.address = address
19+
self.node_specifications = None
1620

1721
def run(self):
1822
try:
@@ -22,13 +26,20 @@ def run(self):
2226
if message['type'] == 'message':
2327
logger.info("Received message: " + message['payload'])
2428
elif message['type'] == 'computer_details':
25-
logger.info("Computer specifications: " + json.dumps(message['payload']))
29+
self.node_specifications = message['payload']
30+
logger.info("Received Computer specifications: " + json.dumps(self.node_specifications))
2631
elif message['type'] == 'info':
2732
logger.info("Slave wants to know my info about " + message['payload'])
2833
if message['payload'] == 'computer_details':
2934
send_message(self.clientsocket, create_payload(get_base_machine_info(), "master_info"))
35+
elif message['payload'] == 'uuid':
36+
send_message(self.clientsocket, create_payload(self.uuid, "uuid"))
37+
elif message['payload'] == 'slave_details':
38+
slave_details = self.master.get_slave_details()
39+
send_message(self.clientsocket, create_payload(slave_details, "slave_details"))
3040
else:
3141
send_message(self.clientsocket, create_payload("unknown", "bad_message"))
3242
except DisconnectionException as e:
3343
logger.info("Got disconnection exception with message: " + e.message)
3444
logger.info("Shutting down slave connection handler")
45+
self.master.remove_client(self)

RpiCluster/RpiMaster.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import time
2+
import datetime
3+
import socket
4+
from MainLogger import logger
5+
6+
from RpiClusterClient import RpiClusterClient
7+
8+
9+
class RpiMaster:
10+
11+
def __init__(self, socket_bind_ip, socket_port):
12+
self.socket_bind_ip = socket_bind_ip
13+
self.socket_port = socket_port
14+
self.connected_clients = {}
15+
16+
def start(self):
17+
logger.info("Starting script...")
18+
19+
listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
20+
listening_socket.bind((self.socket_bind_ip, self.socket_port))
21+
22+
listening_socket.listen(10) # listen to 10 connects
23+
while True:
24+
(clientsocket, address) = listening_socket.accept()
25+
logger.info("Got client at {address}".format(address=address))
26+
27+
rpi_client = RpiClusterClient(self, clientsocket, address)
28+
self.connected_clients[rpi_client.uuid] = rpi_client
29+
rpi_client.start()
30+
31+
def remove_client(self, rpi_client):
32+
del self.connected_clients[rpi_client.uuid]
33+
34+
def get_slave_details(self):
35+
slave_details = {}
36+
for uuid in self.connected_clients:
37+
slave_details[uuid] = {
38+
"uuid": uuid,
39+
"address": str(self.connected_clients[uuid].address[0]) + ":" + str(self.connected_clients[uuid].address[1]),
40+
41+
}
42+
43+
return slave_details
44+

RpiCluster/RpiWebserverSlaveThread.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,27 @@
22
import datetime
33
from MainLogger import logger
44
from DataPackager import create_payload, get_message, send_message
5+
from MachineInfo import get_base_machine_info
6+
from RpiClusterExceptions import DisconnectionException
57
from RpiBasicSlaveThread import RpiBasicSlaveThread
68

79

810
class RpiWebserverSlaveThread(RpiBasicSlaveThread):
911

10-
current_webserver_data = None
12+
current_master_details = None
13+
current_slave_details = None
1114
webserver_data_updated = None
1215

1316
def perform_action(self):
1417
logger.info("Now sending a keepalive to the master")
15-
send_message(self.sock, create_payload("I am still alive, client: {num}".format(num=self.client_number)))
18+
send_message(self.sock, create_payload("I am still alive, client: {num}".format(num=self.uuid)))
1619
send_message(self.sock, create_payload("computer_details", "info"))
17-
message = get_message(self.sock)
18-
RpiWebserverSlaveThread.current_webserver_data = message['payload']
20+
master_details = get_message(self.sock)
21+
send_message(self.sock, create_payload("slave_details", "info"))
22+
slave_details = get_message(self.sock)
23+
24+
RpiWebserverSlaveThread.current_master_details = master_details['payload']
25+
RpiWebserverSlaveThread.current_slave_details = slave_details['payload']
1926
RpiWebserverSlaveThread.webserver_data_updated = datetime.datetime.now()
2027
time.sleep(5)
2128

basic_master.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import socket
55
import ConfigParser
66
from RpiCluster.MainLogger import add_file_logger, logger
7-
from RpiCluster.RpiClusterClient import RpiClusterClient
7+
from RpiCluster.RpiMaster import RpiMaster
88

99
config = ConfigParser.ConfigParser()
1010
config.read(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'rpicluster.cfg'))
@@ -13,18 +13,7 @@
1313
socket_bind_ip = config.get("master", "socket_bind_ip")
1414

1515
add_file_logger("master.log")
16-
logger.info("Starting script...")
17-
18-
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
19-
socket.bind((socket_bind_ip, socket_port))
20-
21-
socket.listen(10) #listen to 10 connects
22-
while True:
23-
(clientsocket, address) = socket.accept()
24-
logger.info("Got client at {address}".format(address=address))
25-
26-
rpiClient = RpiClusterClient(clientsocket, address)
27-
rpiClient.start()
28-
2916

17+
master = RpiMaster(socket_bind_ip, socket_port)
18+
master.start()
3019

basic_webserver_slave.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
@route('/')
2525
def index():
2626
return template("templates/ClusterHomepage.html",
27-
info=json.dumps(RpiWebserverSlaveThread.current_webserver_data, indent=4, sort_keys=True))
27+
masterinfo=json.dumps(RpiWebserverSlaveThread.current_master_details, indent=4, sort_keys=True),
28+
slaveinfo=json.dumps(RpiWebserverSlaveThread.current_slave_details, indent=4, sort_keys=True)
29+
)
2830

2931
run(host=webserver_host, port=webserver_port)

templates/ClusterHomepage.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ <h1>Raspberry Pi Cluster Homepage</h1>
1212
<p>This page shows the status of the Raspberry Pi Cluster</p>
1313

1414
<h2>Master Information</h2>
15-
<pre>{{info}}</pre>
15+
<pre>{{masterinfo}}</pre>
16+
17+
<h2>Slave Information</h2>
18+
<pre>{{slaveinfo}}</pre>
1619

1720
</body>
1821
</html>

0 commit comments

Comments
 (0)