Пример #1
0
def get_latest_mission_from_github():
    """
    Downloads the latest mission from a Github repository

    The repository needs to have releases (tagged)
    The function will download the first MIZ file found in the latest release
    """
    if core.CTX.dcs_auto_mission:
        LOGGER.debug('getting latest mission from Github')
        commands.DCS.block_start('loading mission')
        if DCSConfig.DCS_AUTO_MISSION_GH_OWNER(
        ) and DCSConfig.DCS_AUTO_MISSION_GH_REPO():
            LOGGER.debug('looking for newer mission file')
            latest_version, asset_name, download_url = utils.get_latest_release(
                DCSConfig.DCS_AUTO_MISSION_GH_OWNER(),
                DCSConfig.DCS_AUTO_MISSION_GH_REPO())
            LOGGER.debug('latest release: %s', latest_version)
            local_file = MissionPath(Path(_get_mission_folder(), asset_name))
            if not local_file:
                LOGGER.info('downloading new mission: %s', asset_name)
                req = requests.get(download_url)
                if req.ok:
                    local_file.path.write_bytes(req.content)
                    local_file.set_as_active()
                else:
                    LOGGER.error('failed to download latest mission')
        else:
            LOGGER.warning('no config values given for [auto mission]')
        commands.DCS.unblock_start('loading mission')
    else:
        LOGGER.debug('skipping mission update')
Пример #2
0
    def set_priority(self):
        """
        Sets the DCS process CPU priority to the CFG value
        """
        def _command():
            if self.app.nice() != self.valid_priorities[
                    DCSConfig.DCS_CPU_PRIORITY()]:
                LOGGER.debug('setting DCS process priority to: %s',
                             DCSConfig.DCS_CPU_PRIORITY())
                self.app.nice(
                    self.valid_priorities[DCSConfig.DCS_CPU_PRIORITY()])

        time.sleep(15)
        while True:
            if DCSConfig.DCS_CPU_PRIORITY():
                if core.CTX.exit:
                    return
                if DCSConfig.DCS_CPU_PRIORITY(
                ) not in self.valid_priorities.keys():
                    LOGGER.error(
                        f'invalid priority: %s\n'
                        f'Choose one of: %s',
                        DCSConfig.DCS_CPU_PRIORITY(),
                        self.valid_priorities.keys(),
                    )
                    return
                self._work_with_dcs_process(_command)
            else:
                LOGGER.warning(
                    'no CPU priority given in config file for dcs.exe')
                return
            time.sleep(30)
Пример #3
0
    def reboot(force: bool = False):
        """Reboots the server computer"""
        if DCS.there_are_connected_players():
            if not force:
                return 'there are connected players; cannot restart the server now (use "--force" to restart anyway)'

            LOGGER.warning('forcing restart with connected players')
        os.system('shutdown /r /t 30 /c "Reboot initialized by ESST"')  # nosec
        return ''
Пример #4
0
    def check_for_connected_players() -> bool:
        """
        Returns: False if there are connected players
        """
        if DCS.there_are_connected_players():
            LOGGER.warning(
                'there are connected players; cannot kill the server now')
            return False

        return True
Пример #5
0
 def _work_with_dcs_process(self, func):
     if core.CTX.exit:
         return
     try:
         func()
         self._warned = False
     except (psutil.NoSuchProcess, AttributeError):
         if not core.CTX.exit and not self._warned:
             LOGGER.warning('DCS process does not exist')
             self._warned = True
Пример #6
0
    def queue_kill():
        """Kill DCS application when all players left"""
        def _queue_kill(queue: Queue):
            while DCS.there_are_connected_players():
                if not queue.empty():
                    queue.get_nowait()
                    LOGGER.debug('queued DCS kill has been cancelled')
                    return
                time.sleep(5)
            LOGGER.info('executing planned DCS restart')
            DCS.kill()

        LOGGER.warning('queuing DCS kill for when all players have left')
        CTX.loop.run_in_executor(None, _queue_kill, CANCEL_QUEUED_KILL)
Пример #7
0
def download_mission_from_discord(discord_attachment,
                                  overwrite: bool = False,
                                  load: bool = False,
                                  force: bool = False):
    """
    Downloads a mission from a discord message attachment

    Args:
        force: force restart even with players connected
        discord_attachment: url to download the mission from
        overwrite: whether or not to overwrite an existing file
        load: whether or not to restart the server with the downloaded mission
    """
    url = discord_attachment['url']
    size = discord_attachment['size']
    filename = discord_attachment['filename']
    local_file = MissionPath(Path(_get_mission_folder(), filename))

    overwriting = ''
    if local_file:
        if overwrite:
            overwriting = ' (replacing existing file)'
        else:
            LOGGER.warning(
                'this mission already exists: %s\nuse "overwrite" to replace it',
                local_file.path)
            return

    LOGGER.info(
        'downloading: %s (%s) %s',
        filename,
        humanize.naturalsize(size),
        overwriting,
    )
    with requests.get(url) as response:
        local_file.path.write_bytes(response.content)

    if load:
        if commands.DCS.there_are_connected_players() and not force:
            LOGGER.error(
                'there are connected players; cannot restart the server now (use "force" to kill anyway)'
            )
            return
        LOGGER.info('restarting the server with this mission')
        local_file.set_as_active()
        commands.DCS.restart(force=force)
    else:
        LOGGER.info('download successful, mission is now available')
Пример #8
0
 def kill(force: bool = False, queue: bool = False):
     """Kills DCS application"""
     CANCEL_QUEUED_KILL.put(1)
     if DCS.there_are_connected_players():
         if not force:
             if queue:
                 DCS.queue_kill()
             else:
                 LOGGER.error(
                     'there are connected players; cannot kill the server now'
                     ' (use "--force" to kill anyway)')
             return
         else:
             LOGGER.warning('forcing kill with connected players')
     LOGGER.debug('setting context for DCS kill')
     CTX.dcs_do_kill = True
Пример #9
0
 async def _read_socket(self):
     await asyncio.sleep(0.1)
     try:
         data, _ = self.sock.recvfrom(4096)
         data = json.loads(data.decode().strip())
         if data['type'] == 'ping':
             self._parse_ping(data)
         elif data['type'] == 'status':
             self._parse_status(data)
         # elif data['type'] == 'mission_load':
         #     self._parse_mission_load(data)
         else:
             LOGGER.warning('unknown command received on DCS socket: "%s"',
                            data['type'])
     except socket.timeout:
         pass
Пример #10
0
    def set_affinity(self):
        """
        Sets the DCS process CPU affinity to the CFG value
        """
        def _command():
            if list(self._app.cpu_affinity()) != list(
                    DCSConfig.DCS_CPU_AFFINITY()):
                LOGGER.debug('setting DCS process affinity to: %s',
                             DCSConfig.DCS_CPU_AFFINITY())
                self._app.cpu_affinity(list(DCSConfig.DCS_CPU_AFFINITY()))

        while True:
            if DCSConfig.DCS_CPU_AFFINITY():
                if core.CTX.exit:
                    return
                self._work_with_dcs_process(_command)
            else:
                LOGGER.warning('no CPU affinity given in config file')
                return
            time.sleep(30)
Пример #11
0
    def monitor_cpu_usage(self):
        """
        Gets the CPU usage of "DCS.exe" over 5 seconds, and sends an alert if the given threshold is exceeded

        Threshold is set via the config value "DCS_HIGH_CPU_USAGE", and it defaults to 80%
        """
        while not core.CTX.exit:
            try:
                if self.app and self.app.is_running():
                    cpu_usage = int(
                        self.app.cpu_percent(
                            DCSConfig.DCS_HIGH_CPU_USAGE_INTERVAL()))
                    mem_usage = int(self.app.memory_percent())
                    core.Status.dcs_cpu_usage = f'{cpu_usage}%'
                    if core.CTX.dcs_show_cpu_usage or core.CTX.dcs_show_cpu_usage_once:
                        commands.DISCORD.say(f'DCS cpu usage: {cpu_usage}%')
                        core.CTX.dcs_show_cpu_usage_once = False
                    if DCSConfig.DCS_HIGH_CPU_USAGE():
                        if cpu_usage > DCSConfig.DCS_HIGH_CPU_USAGE(
                        ) and not core.Status.paused:
                            LOGGER.warning(
                                'DCS cpu usage has been higher than %s%% for %s seconds',
                                DCSConfig.DCS_HIGH_CPU_USAGE(),
                                DCSConfig.DCS_HIGH_CPU_USAGE_INTERVAL(),
                            )

                    now_ = utils.now()
                    core.CTX.dcs_mem_history.append((now_, mem_usage))
                    core.CTX.dcs_cpu_history.append((now_, cpu_usage))

            except psutil.NoSuchProcess:
                pass

            # I didn't think it could, happen, but of course it did ...
            # See https://github.com/132nd-vWing/ESST/issues/59
            except AttributeError:
                pass
Пример #12
0
async def monitor_connection():
    """
    Loop that checks WAN every 5 seconds
    """
    LOGGER.debug('starting connection monitoring loop')

    while not core.CTX.exit:

        current_status = await wan_available()

        if current_status != core.CTX.wan:
            if current_status:
                LOGGER.debug('connected to the Internet')
                commands.DISCORD.say(
                    'I just lost internet connection, server is scheduled to be restarted'
                )
            else:
                LOGGER.warning('internet connection lost !')
                commands.DCS.kill(force=False, queue=True)
            core.CTX.wan = current_status

        await asyncio.sleep(10)

    LOGGER.debug('end of connection monitoring loop')
Пример #13
0
def _load(name, metar_or_icao, time, max_wind, min_wind, force):  # noqa: C901
    if max_wind or min_wind:
        LOGGER.warning(
            '"min_wind" and "max_wind" have been disabled for the time being')
    if name is None:
        mission = missions_manager.get_running_mission()
        if not mission:
            LOGGER.error('unable to retrieve current mission')
            return
    else:
        try:
            LOGGER.debug('trying to cast mission name into an int: %s', name)
            mission_number = int(name)
        except ValueError:
            LOGGER.debug('loading mission name: %s', name)
            mission = missions_manager.MissionPath(name)
            if not mission:
                LOGGER.debug('mission path not found: %s', mission.path)
                LOGGER.error('mission file not found: %s', mission.name)
                return
        else:
            LOGGER.debug('loading mission number: %s', mission_number)
            mission = _mission_index_to_mission_name(mission_number)
            if not mission:
                LOGGER.error(
                    'invalid mission index: %s; use "!mission  show" to see available indices',
                    mission_number)
                return

    LOGGER.info('loading mission file: %s', mission.path)
    if time:
        try:
            mission_time = elib_miz.MissionTime.from_string(time)
            LOGGER.info('setting mission time: %s', mission_time.iso_format)
        except elib_miz.exc.InvalidDateTimeString:
            LOGGER.error('invalid date-time string: %s', time)
            return
        except ValueError as err:
            LOGGER.error(err)
            return
    else:
        mission_time = None
    if metar_or_icao:
        LOGGER.info('analyzing METAR string: %s', metar_or_icao)
        try:
            weather_ = elib_wx.Weather(metar_or_icao)
            LOGGER.info('setting mission weather: %s', weather_.as_str())
        except elib_wx.BadStationError:
            LOGGER.error('wrong ICAO code: %s', metar_or_icao)
            return
        LOGGER.info('METAR: %s', weather_.raw_metar_str)
    else:
        LOGGER.info('building METAR from mission file')
        # noinspection SpellCheckingInspection
        weather_ = elib_wx.Weather(str(mission.path))
        LOGGER.info('METAR: %s', weather_.as_str())

    commands.DCS.block_start('loading mission')
    commands.DCS.kill(force=force)
    try:
        LOGGER.debug('waiting on DCS application to close')
        while core.Status.dcs_application != 'not running':
            sleep(1)
        LOGGER.debug('DCS has closed, carrying on')
        active_mission = mission
        if time:
            mission_time.apply_to_miz(str(mission.path),
                                      str(mission.auto.path),
                                      overwrite=True)
            active_mission = mission.auto
        if metar_or_icao:
            weather_.apply_to_miz(str(mission.path),
                                  str(mission.auto.path),
                                  overwrite=True)
            active_mission = mission.auto
        active_mission.set_as_active(weather_)
    finally:
        commands.DCS.unblock_start('loading mission')
Пример #14
0
def reboot(force: bool = False):
    """
    Restart the server computer (protected)
    """
    LOGGER.warning('rebooting server, ciao a tutti !')
    commands.SERVER.reboot(force)
Пример #15
0
 def _show_graph(graph):
     if graph:
         DISCORD.send_file(graph)
     else:
         LOGGER.warning('failed to create the graph')
Пример #16
0
def clean_old_logs():
    """
    Removes old logs
    """
    LOGGER.warning('removal of old logs has been temporarily disabled')
Пример #17
0
    async def run(self):  # noqa: C901
        """
        Entry point of the thread
        """
        if not core.CTX.start_dcs_loop:
            LOGGER.debug('skipping DCS application loop')
            return
        if not await self._get_dcs_version_from_executable():
            return
        await core.CTX.loop.run_in_executor(None, install_game_gui_hooks)
        await core.CTX.loop.run_in_executor(
            None, server_settings.write_server_settings)
        await core.CTX.loop.run_in_executor(
            None, missions_manager.get_latest_mission_from_github)
        await core.CTX.loop.run_in_executor(None,
                                            missions_manager.initial_setup)

        LOGGER.debug('starting DCS monitoring thread')
        if DCS.dcs_cannot_start():
            blockers = ", ".join(DCS.dcs_cannot_start())
            if blockers not in self._blockers_warned:
                self._blockers_warned.add(blockers)
                LOGGER.warning('DCS is prevented to start by: %s',
                               ', '.join(DCS.dcs_cannot_start()))
        else:
            if self._blockers_warned:
                self._blockers_warned = set()
            await self._try_to_connect_to_existing_dcs_application()
            await self._start_new_dcs_application_if_needed()
        cpu_monitor_thread = core.CTX.loop.run_in_executor(
            None, self.monitor_cpu_usage)
        cpu_affinity_thread = core.CTX.loop.run_in_executor(
            None, self.set_affinity)
        cpu_priority_thread = core.CTX.loop.run_in_executor(
            None, self.set_priority)
        while True:
            if core.CTX.exit:
                LOGGER.debug('interrupted by exit signal')
                await cpu_monitor_thread
                await cpu_affinity_thread
                await cpu_priority_thread
                break
            if DCS.dcs_cannot_start():
                blockers = ", ".join(DCS.dcs_cannot_start())
                if blockers not in self._blockers_warned:
                    self._blockers_warned.add(blockers)
                    LOGGER.warning('DCS is prevented to start by: %s',
                                   ', '.join(DCS.dcs_cannot_start()))
            else:
                if self._blockers_warned:
                    self._blockers_warned = set()
                await self._check_if_dcs_is_running()
                if not self.process_pid:
                    LOGGER.debug('DCS has stopped, re-starting')
                    await self.restart()
            if core.CTX.dcs_do_kill:
                await self.kill_running_app()
            if core.CTX.dcs_do_restart:
                await self.restart()
            await asyncio.sleep(0.1)

        LOGGER.debug('end of DCS loop')