Diseño procedimental

En este apartado se referencian los algoritmos implementados en el sistema que son considerados de mayor relevancia.

Detección del sistema operativo y descarga

if [ -e /bootfs/marco-bootstrap ];then
    echo "Getting marcopolo mirrors"
    marcomirror=`/bootfs/marco-bootstrap`
    if [ $marcomirror != "" ];then
        echo "Detected a mirror at $marcomirror"
        marcomirror="https://$marcomirror:1345/tar/"
        mirror=$marcomirror
    else
        echo "No mirror detected. Using fallback ${mirror}"
    fi
fi

if [ "$kernel_module" = true ] ; then
  if [ "$rootfstype" != "ext4" ] ; then
    mkdir -p /rootfs/etc/initramfs-tools
    echo $rootfstype >> /rootfs/etc/initramfs-tools/modules
  fi
fi

echo "Starting install process..."

if [ -z $OS_IMG ]; then
    echo "OS image not set. Using fallback"
    if [[ $rpi_hardware_version == "1" ]];then
        TARBALL="ArchLinuxARM-rpi-latest"
    else
        TARBALL="ArchLinuxARM-rpi-2-latest"
    fi

else
    echo "Using $OS_IMG as OS image"
    TARBALL=$OS_IMG
fi

mkdir -p /rootfs/aux
#echo "The mirror for marco would be ${marcomirror}"
echo "Downloading using mirror ${mirror}"
#wget --certificate=app.crt --private-key=app.key --ca-certificate=app.crt --no-check-certificate -P /rootfs/aux ${mirror}${TARBALL}.tar.gz || fail
#https://localhost:1345/tar/ArchLinuxARM-rpi-2-latest.tar.gz
#/bin/wget -P /rootfs/aux ${mirror}${TARBALL}.tar.gz || fail
/bin/wget --certificate=/bootfs/certs/app.crt --private-key=/bootfs/certs/app.key --ca-certificate=/bootfs/certs/app.crt --no-check-certificate -P /rootfs/aux ${mirror}${TARBALL}.tar.gz || fail

rm -f /rootfs/aux/${TARBALL}.tar
echo "Gunzipping"
gunzip -f /rootfs/aux/${TARBALL}.tar.gz > /rootfs/aux/${TARBALL}.tar

echo "Tar"
tar -xf /rootfs/aux/${TARBALL}.tar -C /rootfs

echo "syncing"
sync
echo "Copied"

mv /rootfs/boot/* /rootfs/boot_mnt

Programación de una operación de actualización

def update(self, ip, bootcode=None, image=''):
    """
    Executes the update operation
    """
    def unzip_bootcode(source, dest_dir):
        """
        Unzips the bootcode into the desired location
        """
        #http://stackoverflow.com/a/640033/2628463
        zip_file = zipfile.ZipFile(source)
        zip_file.extractall(path=dest_dir)

    def process_bootcode(bootcode, image):
        """
        Processes the given bootcode
        """
        path = None
        if bootcode is None:
            path = conf.BOOTCODE_FALLBACK
        else:
            logging.info("Downloading bootcode %s for operation" % bootcode)
            r = requests.get("https://"+ip+":"+str(conf.BACKEND_FILES_PORT)+"/bootcode/download/"+bootcode,
                             stream=True,
                             verify=False,
                             cert=(conf.APPCERT, conf.APPKEY))
            filename = bootcode
            with open(os.path.join(conf.TMP_DIR, filename), 'wb') as output_file:
                for chunk in r.iter_content(chunk_size=1024):
                    if chunk:
                        output_file.write(chunk)
                        output_file.flush()
            path = os.path.join(conf.TMP_DIR, filename)

        logging.debug(path)
        unzip_bootcode(path, conf.BOOTDIR)
        if image != '':
            with open(os.path.join(conf.BOOTDIR, "installer-config.txt"), 'w') as f_desc:
                f_desc.write("export OS_IMG=%s" % image.replace(".tar.gz", ""))

    process_bootcode(bootcode, image)
    command = "shutdown -r +2 %s" % conf.REBOOT_MSG
    subprocess.Popen(command, shell=True)
    logging.info("An update is scheduled")

Creación de una operación

@authenticated
def post(self):
    """
    Processes a POST request with the operation parameters
    """
    operation = self.get_argument('operation', None)
    schedule_time = self.get_argument('schedule', None)

    if operation is None or schedule_time is None:
        self.set_status(400)
        self.finish("Malformed request")
        return
    else:
        schedule_time = float(schedule_time)

    nodes = self.get_argument('nodes', None)
    try:
        nodes_list = nodes.split(",")[:-1]
    except Exception as e:
        logging.debug(e)
        self.set_status(400)
        self.finish("Malformed request")

    if len(nodes_list) > 0:
        for nodes in nodes_list:
            pass

    logging.debug("I shall schedule a %s on %f for %s" % (operation, schedule_time, ",".join('{}: {}'.format(*k) for k in enumerate(nodes_list))))

    for node in nodes_list:
        try:
            socket.inet_aton(node)
            if operation == "reboot":
                url = "https://"+node+":"+str(conf.SLAVE_PORT)+"/reboot"
                commands = {"type":"reboot","time":schedule_time}
            elif operation == "update":
                url = "https://"+node+":"+str(conf.SLAVE_PORT)+"/update"
                commands = {"type":"update",
                            "time":schedule_time,
                            "image": self.get_argument('image', ''),
                            "bootcode": chosen_bootcode}
                logging.debug(self.get_argument('image', ''))
                print(self.get_argument('image', ''))
            logging.debug(url)
            future = futures_session.post(url,
                                        files={},
                                        data=commands,
                                        verify=conf.RECEIVERCERT,
                                        cert=(conf.APPCERT, conf.APPKEY))

            add_callback(future=future, callback=self.deployed, node=node)

        except socket.error as se:
            logging.debug(se)

    self.add_to_db(operation, schedule_time, nodes, self.get_argument('image', ''))