This commit is contained in:
Bala P V 2022-09-28 17:06:11 -07:00
Родитель 270fc46966
Коммит 3b59366313
2 изменённых файлов: 142 добавлений и 106 удалений

144
.github/actions/generate-icm/main.py поставляемый
Просмотреть файл

@ -19,6 +19,7 @@ RoutingOptions: TypeAlias = Literal["None", "SkipTransferOnUpdate"]
class SOAPFault(Exception):
"""SOAP 1.2 Fault"""
def __init__(
self,
*,
@ -26,7 +27,7 @@ class SOAPFault(Exception):
reason: str,
role: Optional[str] = None,
node: Optional[str],
detail: Optional[str] = None
detail: Optional[str] = None,
):
self.code = code
self.reason = reason
@ -48,9 +49,15 @@ class AddOrUpdateIncident2Args(BaseModel):
class AddOrUpdateIncident2Result(BaseModel):
IncidentId: int
Status: Literal["Invalid", "AddedNew", "UpdatedExisting",
"DidNotChangeExisting", "AlertSourceUpdatesPending",
"UpdateToHoldingNotAllowed", "Discarded"]
Status: Literal[
"Invalid",
"AddedNew",
"UpdatedExisting",
"DidNotChangeExisting",
"AlertSourceUpdatesPending",
"UpdateToHoldingNotAllowed",
"Discarded",
]
class ActionArgs(BaseModel):
@ -60,23 +67,20 @@ class ActionArgs(BaseModel):
def sorted_dict(val: T) -> T:
"""Returns a copy of dict `val` where key-values are inserted in
"""Returns a copy of dict `val` where key-values are inserted in
alphabetical order. If `val` is not a dict, it's returned unchanged
Args:
val (T): Any value
Returns:
T: Either the same value passed in if not a dict, or a copy
T: Either the same value passed in if not a dict, or a copy
with keys inserted in alphabetical order
"""
if not isinstance(val, dict):
return val
return {
k: sorted_dict(v)
for k, v in sorted(val.items(), key=lambda kv: kv[0])
}
return {k: sorted_dict(v) for k, v in sorted(val.items(), key=lambda kv: kv[0])}
@overload
@ -122,11 +126,7 @@ def toXML(obj: dict, xsi_namespace="i") -> str:
NAMESPACEKEY = "$namespace"
def tag(
name: str,
value: str,
*,
attributes: dict = {},
namespace: str = None
name: str, value: str, *, attributes: dict = {}, namespace: str = None
) -> str:
"""Surrounds a string in xml tags
@ -139,17 +139,17 @@ def toXML(obj: dict, xsi_namespace="i") -> str:
Returns:
str: xml string
"""
attributes_str = ' '.join(f'{k}="{v}"' for k, v in attributes.items())
attributes_str = " ".join(f'{k}="{v}"' for k, v in attributes.items())
ns = f"{namespace}:" if namespace else ""
if attributes_str:
attributes_str = ' ' + attributes_str
attributes_str = " " + attributes_str
if value:
return f'<{ns}{name}{attributes_str}>{value}</{ns}{name}>'
return f'<{ns}{name}{attributes_str} />'
return f"<{ns}{name}{attributes_str}>{value}</{ns}{name}>"
return f"<{ns}{name}{attributes_str} />"
def serialize_dict(val: dict, namespace: str = None) -> str:
ns = val.pop(NAMESPACEKEY, namespace)
return ''.join(serialize(k, v, ns) for k, v in val.items())
return "".join(serialize(k, v, ns) for k, v in val.items())
def serialize(name: str, val: Any, namespace: str = None) -> str:
"""Serializes a key value pair into an XML element
@ -168,9 +168,7 @@ def toXML(obj: dict, xsi_namespace="i") -> str:
attrs = val.pop(ATTRIBUTESKEY, {})
return t(serialize_dict(val, namespace), attrs)
if isinstance(val, list):
return t(
''.join(serialize_dict(v, namespace=namespace) for v in val)
)
return t("".join(serialize_dict(v, namespace=namespace) for v in val))
elif isinstance(val, bool):
return t(str(val).lower())
elif val is None:
@ -188,6 +186,7 @@ def parseArgs():
Yields:
_type_: ActionArgs
"""
def getInput(name: str, *, required=False):
val = os.environ.get(f'INPUT_{name.replace(" ", "_").upper()}', "")
if not val and required:
@ -205,80 +204,65 @@ def parseArgs():
) as key_file:
yield ActionArgs(
host=host,
auth=AuthCert(
cert=Path(cert_file.name), private_key=Path(key_file.name)
),
args=AddOrUpdateIncident2Args(**body, connectorId=connectorId)
auth=AuthCert(cert=Path(cert_file.name), private_key=Path(key_file.name)),
args=AddOrUpdateIncident2Args(**body, connectorId=connectorId),
)
def add_or_update_incident_2(
host: str, auth: AuthCert, args: AddOrUpdateIncident2Args
):
def add_or_update_incident_2(host: str, auth: AuthCert, args: AddOrUpdateIncident2Args):
now = datetime.utcnow().isoformat()
soap_ns = "http://www.w3.org/2003/05/soap-envelope"
icm_ns = "http://schemas.datacontract.org/2004/07/Microsoft.AzureAd.Icm.Types"
baseIncident = {
"OccurringLocation": {},
"RaisingLocation": {},
"Source":
{
"CreatedBy": "Monitor",
"Origin": "Monitor",
"SourceId": args.connectorId,
"CreateDate": now,
"ModifiedDate": now,
},
"Source": {
"CreatedBy": "Monitor",
"Origin": "Monitor",
"SourceId": args.connectorId,
"CreateDate": now,
"ModifiedDate": now,
},
"Severity": 4,
}
incident = sorted_dict(
{
"$attributes":
{
"xmlns:b": icm_ns,
"xmlns:i": "http://www.w3.org/2001/XMLSchema-instance"
},
"$attributes": {
"xmlns:b": icm_ns,
"xmlns:i": "http://www.w3.org/2001/XMLSchema-instance",
},
"$namespace": "b",
**mergeLeft(baseIncident, args.incident)
**mergeLeft(baseIncident, args.incident),
}
)
soap_message = {
"s:Envelope":
{
"$attributes":
{
"xmlns:s": soap_ns,
"xmlns:a": "http://www.w3.org/2005/08/addressing",
},
"s:Header":
{
"a:Action":
"http://tempuri.org/IConnectorIncidentManager/AddOrUpdateIncident2",
"a:To":
f"https://{host}/Connector3/ConnectorIncidentManager.svc"
},
"s:Body":
{
# Key order is significant, must be in alphabetical
# order
"AddOrUpdateIncident2":
{
"connectorId": args.connectorId,
"incident": incident,
"routingOptions": args.routingOptions,
"$attributes": {
"xmlns": "http://tempuri.org/"
},
}
}
}
"s:Envelope": {
"$attributes": {
"xmlns:s": soap_ns,
"xmlns:a": "http://www.w3.org/2005/08/addressing",
},
"s:Header": {
"a:Action": "http://tempuri.org/IConnectorIncidentManager/AddOrUpdateIncident2",
"a:To": f"https://{host}/Connector3/ConnectorIncidentManager.svc",
},
"s:Body": {
# Key order is significant, must be in alphabetical
# order
"AddOrUpdateIncident2": {
"connectorId": args.connectorId,
"incident": incident,
"routingOptions": args.routingOptions,
"$attributes": {"xmlns": "http://tempuri.org/"},
}
},
}
}
response = requests.post(
f"https://{host}/Connector3/ConnectorIncidentManager.svc",
cert=(auth.cert, auth.private_key),
headers={'Content-Type': 'application/soap+xml; charset=utf-8'},
data='<?xml version="1.0" encoding="UTF-8"?>\n' + toXML(soap_message)
headers={"Content-Type": "application/soap+xml; charset=utf-8"},
data='<?xml version="1.0" encoding="UTF-8"?>\n' + toXML(soap_message),
)
xml_result = ElementTree.fromstring(response.text)
@ -288,8 +272,8 @@ def add_or_update_incident_2(
def innerXML(element: Optional[ElementTree.Element]) -> Optional[str]:
return element and (
element.text or
''.join(ElementTree.tostring(e, 'unicode') for e in element)
element.text
or "".join(ElementTree.tostring(e, "unicode") for e in element)
)
raise SOAPFault(
@ -301,8 +285,8 @@ def add_or_update_incident_2(
)
return AddOrUpdateIncident2Result(
IncidentId=int(xml_result.findtext(f'.//{{{icm_ns}}}IncidentId')),
Status=xml_result.findtext(f'.//{{{icm_ns}}}Status')
IncidentId=int(xml_result.findtext(f".//{{{icm_ns}}}IncidentId")),
Status=xml_result.findtext(f".//{{{icm_ns}}}Status"),
)
@ -310,7 +294,7 @@ def main():
# Utility functions to report status to Github Action Runner
core = {
"debug": lambda s: print(f"::debug::{s}"),
"error": lambda s: print(f"::error::{s}")
"error": lambda s: print(f"::error::{s}"),
}
with parseArgs() as args:

Просмотреть файл

@ -41,12 +41,13 @@
"\n",
"# Authentication package\n",
"from azure.identity import DefaultAzureCredential\n",
"\n",
"credential = DefaultAzureCredential()\n",
"\n",
"# Get a handle to the workspace. You can find the info on the workspace tab on ml.azure.com\n",
"ml_client = MLClient(\n",
" credential=credential,\n",
" subscription_id=\"<SUBSCRIPTION_ID>\", # this will look like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n",
" subscription_id=\"<SUBSCRIPTION_ID>\", # this will look like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n",
" resource_group_name=\"<RESOURCE_GROUP>\",\n",
" workspace_name=\"<AML_WORKSPACE_NAME>\",\n",
")"
@ -252,24 +253,27 @@
"registered_model_name = \"credit_defaults_model\"\n",
"\n",
"# configure the command job\n",
"job = command( \n",
"job = command(\n",
" inputs=dict(\n",
" #uri_file refers to a specific file as a data asset\n",
" data=Input(type=\"uri_file\", path=\"https://azuremlexamples.blob.core.windows.net/datasets/credit_card/default%20of%20credit%20card%20clients.csv\"), \n",
" test_train_ratio=0.2, #input variable in main.py\n",
" learning_rate=0.25, #input variable in main.py\n",
" registered_model_name=registered_model_name #input variable in main.py\n",
" # uri_file refers to a specific file as a data asset\n",
" data=Input(\n",
" type=\"uri_file\",\n",
" path=\"https://azuremlexamples.blob.core.windows.net/datasets/credit_card/default%20of%20credit%20card%20clients.csv\",\n",
" ),\n",
" code=\"./src/\", #location of source code\n",
" #The inputs/outputs are accessible in the command via the ${{ ... }} notation\n",
" command=\"python main.py --data ${{inputs.data}} --test_train_ratio ${{inputs.test_train_ratio}} --learning_rate ${{inputs.learning_rate}} --registered_model_name ${{inputs.registered_model_name}}\", \n",
" #This is the ready-made environment you are using\n",
" test_train_ratio=0.2, # input variable in main.py\n",
" learning_rate=0.25, # input variable in main.py\n",
" registered_model_name=registered_model_name, # input variable in main.py\n",
" ),\n",
" code=\"./src/\", # location of source code\n",
" # The inputs/outputs are accessible in the command via the ${{ ... }} notation\n",
" command=\"python main.py --data ${{inputs.data}} --test_train_ratio ${{inputs.test_train_ratio}} --learning_rate ${{inputs.learning_rate}} --registered_model_name ${{inputs.registered_model_name}}\",\n",
" # This is the ready-made environment you are using\n",
" environment=\"AzureML-sklearn-1.0-ubuntu20.04-py38-cpu@latest\",\n",
" #This is the compute you created earlier\n",
" # This is the compute you created earlier\n",
" compute=\"cpu-cluster\",\n",
" #An experiment is a container for all the iterations one does on a certain project. All the jobs submitted under the same experiment name would be listed next to each other in Azure ML studio.\n",
" # An experiment is a container for all the iterations one does on a certain project. All the jobs submitted under the same experiment name would be listed next to each other in Azure ML studio.\n",
" experiment_name=\"train_model_credit_default_prediction\",\n",
" display_name=\"credit_default_prediction\" \n",
" display_name=\"credit_default_prediction\",\n",
")"
]
},
@ -378,9 +382,9 @@
"endpoint = ManagedOnlineEndpoint(\n",
" name=online_endpoint_name,\n",
" description=\"this is an online endpoint\",\n",
" auth_mode=\"key\", # use \"key\" for key-based authentication. Use \"aml_token\" for Azure Machine Learning token-based authentication. \n",
" auth_mode=\"key\", # use \"key\" for key-based authentication. Use \"aml_token\" for Azure Machine Learning token-based authentication.\n",
" tags={\n",
" \"training_dataset\": \"credit_defaults\", # these tags are optional\n",
" \"training_dataset\": \"credit_defaults\", # these tags are optional\n",
" \"model_type\": \"sklearn.GradientBoostingClassifier\",\n",
" },\n",
")\n",
@ -455,10 +459,10 @@
"\n",
"# create an online deployment.\n",
"blue_deployment = ManagedOnlineDeployment(\n",
" name=\"blue\", # we chose color names for the deployment, you can change.\n",
" name=\"blue\", # we chose color names for the deployment, you can change.\n",
" endpoint_name=online_endpoint_name,\n",
" model=model,\n",
" instance_type=\"Standard_DS3_v2\", # this is the compute we created earlier\n",
" instance_type=\"Standard_DS3_v2\", # this is the compute we created earlier\n",
" instance_count=1,\n",
")\n",
"\n",
@ -496,14 +500,62 @@
"# write a json query for testing\n",
"%%writefile {deploy_dir}/sample-request.json\n",
"{\n",
" \"input_data\": {\n",
" \"columns\": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22],\n",
" \"index\": [0, 1],\n",
" \"data\": [\n",
" [20000,2,2,1,24,2,2,-1,-1,-2,-2,3913,3102,689,0,0,0,0,689,0,0,0,0],\n",
" [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, 9, 8]\n",
" ]\n",
" }\n",
" \"input_data\": {\n",
" \"columns\": [\n",
" 0,\n",
" 1,\n",
" 2,\n",
" 3,\n",
" 4,\n",
" 5,\n",
" 6,\n",
" 7,\n",
" 8,\n",
" 9,\n",
" 10,\n",
" 11,\n",
" 12,\n",
" 13,\n",
" 14,\n",
" 15,\n",
" 16,\n",
" 17,\n",
" 18,\n",
" 19,\n",
" 20,\n",
" 21,\n",
" 22,\n",
" ],\n",
" \"index\": [0, 1],\n",
" \"data\": [\n",
" [\n",
" 20000,\n",
" 2,\n",
" 2,\n",
" 1,\n",
" 24,\n",
" 2,\n",
" 2,\n",
" -1,\n",
" -1,\n",
" -2,\n",
" -2,\n",
" 3913,\n",
" 3102,\n",
" 689,\n",
" 0,\n",
" 0,\n",
" 0,\n",
" 0,\n",
" 689,\n",
" 0,\n",
" 0,\n",
" 0,\n",
" 0,\n",
" ],\n",
" [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, 9, 8],\n",
" ],\n",
" }\n",
"}"
]
},