utils/nb_check.py (162 lines of code) (raw):

# ------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- """Checker for Python and msticpy versions.""" import importlib import json import os import re import socket import sys import urllib from pathlib import Path from urllib import request from IPython import get_ipython from IPython.display import HTML, display from pkg_resources import parse_version __version__ = "2.0.0" AZ_GET_STARTED = ( "https://github.com/Azure/Azure-Sentinel-Notebooks/blob/master/A%20Getting" "%20Started%20Guide%20For%20Azure%20Sentinel%20ML%20Notebooks.ipynb" ) TROUBLE_SHOOTING = ( "https://github.com/Azure/Azure-Sentinel-Notebooks/blob/master/" "TroubleShootingNotebooks.ipynb" ) MISSING_PKG_ERR = """ <h4><font color='orange'>The package '<b>{package}</b>' is not installed or has an unsupported version (installed version = '{inst_ver}')</font></h4> Please install or upgrade before continuing: required version is {package}>={req_ver} """ MP_INSTALL_FAILED = """ <h4><font color='red'>The notebook may not run correctly without the correct version of '<b>{pkg}</b>' ({ver} or later).</font></h4> Please see the <a href="{nbk_uri}"> Getting Started Guide For Microsoft Sentinel ML Notebooks</a></b> for more information<br><hr> """ RELOAD_MP = """ <h4><font color='orange'>Kernel restart needed</h4> An error was detected trying to load the updated version of MSTICPy.<br> Please restart the notebook kernel and re-run this cell - it should run without error. """ MIN_PYTHON_VER_DEF = (3, 6) MSTICPY_REQ_VERSION = (0, 9, 0) VER_RGX = r"(?P<maj>\d+)\.(?P<min>\d+).(?P<pnt>\d+)(?P<suff>.*)" MP_ENV_VAR = "MSTICPYCONFIG" MP_FILE = "msticpyconfig.yaml" NB_CHECK_URI = ( "https://raw.githubusercontent.com/Azure/Azure-Sentinel-" "Notebooks/master/utils/nb_check.py" ) _IN_AML = os.environ.get("APPSETTING_WEBSITE_SITE_NAME") == "AMLComputeInstance" def check_versions( min_py_ver=MIN_PYTHON_VER_DEF, min_mp_ver=MSTICPY_REQ_VERSION, extras=None, mp_release=None, pip_quiet=True, **kwargs, ): """ Check the current versions of the Python kernel and MSTICPy. Parameters ---------- min_py_ver : Union[Tuple[int, int], str] Minimum Python version min_mp_ver : Union[Tuple[int, int], str] Minimum MSTICPy version extras : Optional[List[str]], optional A list of extras required for MSTICPy mp_release : Optional[str], optional Override the MSTICPy release version. This can also be specified in the environment variable 'MP_TEST_VER' pip_quiet : bool, optional If True (default) will suppress all output from pip except warnings and errors. False will display normal output. Raises ------ RuntimeError If the Python version does not support the notebook. If the MSTICPy version does not support the notebook and the user chose not to upgrade """ del kwargs _disp_html("Note: you may need to scroll down this cell to see the full output.") _disp_html("<h4>Starting notebook pre-checks...</h4>") if isinstance(min_py_ver, str): min_py_ver = _get_pkg_version(min_py_ver).release check_python_ver(min_py_ver=min_py_ver) _check_mp_install(min_mp_ver, mp_release, extras, pip_quiet) _disp_html("<h4>Notebook pre-checks complete.</h4>") def check_python_ver(min_py_ver=MIN_PYTHON_VER_DEF): """ Check the current version of the Python kernel. Parameters ---------- min_py_ver : Tuple[int, int] Minimum Python version Raises ------ RuntimeError If the Python version does not support the notebook. """ _disp_html("Checking Python kernel version...") if sys.version_info < min_py_ver: _disp_html( """ <h4><font color='red'>This notebook requires a later notebook (Python) kernel version.</h4></font> Select a kernel from the notebook toolbar (above), that is Python 3.6 or later (Python 3.8 recommended)<br> """ % min_py_ver ) _disp_html( f""" Please see the <a href="{TROUBLE_SHOOTING}">TroubleShootingNotebooks</a> for more information<br><br><hr> """ ) raise RuntimeError("Python %s.%s or later kernel is required." % min_py_ver) if sys.version_info < (3, 8, 0): _disp_html( "Recommended: switch to using the 'Python 3.8 - AzureML' notebook kernel" " if this is available." ) _disp_html( "Info: Python kernel version %s.%s.%s OK<br>" % (sys.version_info[0], sys.version_info[1], sys.version_info[2]) ) def _check_mp_install(min_mp_ver, mp_release, extras, pip_quiet): """Check for and try to install required MSTICPy version.""" # Use the release ver specified in params, in the environment or # the notebook default. pkg_version = _get_pkg_version(min_mp_ver) mp_install_version = mp_release or os.environ.get("MP_TEST_VER", str(pkg_version)) exact_version = bool(mp_release or os.environ.get("MP_TEST_VER")) try: check_mp_ver(min_msticpy_ver=mp_install_version) if extras: # If any extras are specified, always trigger an install _disp_html("Running install to ensure extras are installed...<br>") _install_mp( mp_install_version=mp_install_version, exact_version=exact_version, extras=extras, quiet=pip_quiet, ) except ImportError: _install_mp( mp_install_version=mp_install_version, exact_version=exact_version, extras=extras, quiet=pip_quiet, ) _disp_html("Installation completed. Attempting to re-import/reload MSTICPy...") # pylint: disable=unused-import, import-outside-toplevel if "msticpy" in sys.modules: try: importlib.reload(sys.modules["msticpy"]) except ImportError: _disp_html(RELOAD_MP) else: import msticpy # pylint: enable=unused-import, import-outside-toplevel check_mp_ver(min_msticpy_ver=mp_install_version) except RuntimeError: _disp_html("Installation skipped.") # pylint: disable=import-outside-toplevel def check_mp_ver(min_msticpy_ver=MSTICPY_REQ_VERSION): """ Check and optionally update the current version of msticpy. Parameters ---------- min_py_ver : Tuple[int, int] Minimum MSTICPy version Raises ------ RuntimeError If the MSTICPy version does not support the notebook and the user chose not to upgrade. ImportError If MSTICPy version is insufficient and we need to upgrade """ mp_min_pkg_ver = _get_pkg_version(min_msticpy_ver) _disp_html("Checking msticpy version...<br>") wrong_ver_err = f"msticpy {mp_min_pkg_ver} or later is needed." inst_version = "none" try: import msticpy inst_version = _get_pkg_version(msticpy.__version__) if inst_version < mp_min_pkg_ver: raise ImportError(wrong_ver_err) except ImportError as err: _disp_html( MISSING_PKG_ERR.format( package="msticpy", inst_ver=inst_version, req_ver=mp_min_pkg_ver, ) ) resp = input("Install now? (y/n)") # nosec if resp.casefold().startswith("y"): raise _disp_html( MP_INSTALL_FAILED.format( pkg="msticpy", ver=mp_min_pkg_ver, curr_ver=inst_version, nbk_uri=AZ_GET_STARTED, ) ) raise RuntimeError(wrong_ver_err) from err _disp_html(f"Info: msticpy version {mp_min_pkg_ver} OK<br>") def _install_mp(mp_install_version, exact_version, extras, quiet=True): """Try to install MSTICPY.""" sp_args = ["install", "--no-input"] if quiet: sp_args.append("--quiet") pkg_op = "==" if exact_version else ">=" mp_pkg_spec = f"msticpy[{','.join(extras)}]" if extras else "msticpy" mp_pkg_spec = f"{mp_pkg_spec}{pkg_op}{mp_install_version}" sp_args.append(mp_pkg_spec) _disp_html( f"<br>Running pip {' '.join(sp_args)} - this may take a few moments...<br>" ) ip_shell = get_ipython() ip_shell.run_line_magic("pip", " ".join(sp_args)) def _get_pkg_version(version): if isinstance(version, str): return parse_version(version) elif isinstance(version, tuple): return parse_version(".".join(str(ver) for ver in version)) raise TypeError(f"Unparseable type version {version}") def _disp_html(text): display(HTML(text)) def get_aml_user_folder(): """Return the root of the user folder.""" user_path = Path("/") path_parts = Path(".").absolute().parts for idx, part in enumerate(path_parts): if part.casefold() == "users": user_path = user_path.joinpath(part).joinpath(path_parts[idx + 1]) break user_path = user_path.joinpath(part) return user_path