Skip to content

Commit

Permalink
Merge pull request #198 from 0x41424142/patching
Browse files Browse the repository at this point in the history
Added pm.create_job, bumped mkdocs-material
  • Loading branch information
0x41424142 authored Nov 25, 2024
2 parents 0d79556 + 5ab9d4b commit 1d8359c
Show file tree
Hide file tree
Showing 7 changed files with 441 additions and 24 deletions.
132 changes: 132 additions & 0 deletions docs/patch.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ You can use any of the endpoints currently supported:
| ```list_jobs``` | Returns jobs that match given kwargs. |
| ```get_job_results``` | Returns a summary of a job. |
| ```get_job_runs``` | Returns a list of runs of a job. |
| ```create_job```| Creates a new job. |

## List Jobs API

Expand Down Expand Up @@ -131,6 +132,137 @@ runs = get_job_runs(auth, job.id)
]
```

## Create Job API

```create_job``` creates a new patch management job.

|Parameter| Possible Values |Description| Required|
|--|--|--|--|
|```auth```|```qualysdk.auth.TokenAuth``` | Authentication object ||
| ```name``` | ```str``` | The name of the job ||
| ```platform``` | ```Literal["Windows", "Linux"]``` | The platform of the job ||
| ```jobType``` | ```Literal["Install", "Rollback"]``` | The type of job to create. ```Rollback``` is Windows-only. ||
| ```scheduleType``` | ```Literal["On-demand", "Once", "Daily", "Weekly", "Monthly"]``` | The type of schedule to use ||
|```approvedPatches```|```List[str]```|An explicit list of patche GUIDs to add to the job||
| ```assetIds``` | ```List[str]``` | The IDs of the assets to target ||
| ```assetTagIds``` | ```List[str]``` | The IDs of the asset tags to target ||
| ```filterType``` | ```Literal["all", "any"] = "any"``` | The type of filter to use ||
| ```exclusionTagIds``` | ```List[str]``` | The IDs of the asset tags to exclude ||
| ```exclusionAssetIds``` | ```List[str]``` | The IDs of the assets to exclude ||
| ```description``` | ```str``` | The description of the job ||
| ```coAuthorUserIds``` | ```List[str]``` | The IDs of the co-authors to add to the job ||
| ```exclusionFilterType``` | ```Literal["all", "any"] = "any"``` | The type of exclusion filter to use ||
| ```startDateTime``` | ```str``` | The start date and time of the job | ❌ for ```On-demand```, ✅ for others |
| ```recurring``` | ```bool=False``` | Whether the job is recurring ||
| ```dayOfMonth``` | ```int, 0 <= x <= 31``` | The day of the month to run the job ||
| ```matchAllTagIds``` | ```list[str]``` | The IDs of the asset tags to match ||
| ```recurringLastDayOfMonth``` | ```bool=False``` | Whether the job runs on the last day of the month ||
| ```monthlyRecurringType``` | ```Literal[0, 1, "0", "1"]``` | If 1, run on Patch Tuesday ||
| ```patchTuesdayPlusXDays``` | ```int, -27 <= x <= 27``` | The number of days before or after Patch Tuesday to run the job ||
| ```recurringDayOfMonth``` | ```int, 1 <= x <= 5``` | Run the job on a specific weekday of the month ||
| ```recurringWeekDayOfMonth``` | ```int, 0 <= x <= 6``` | The day of the week to run the job ||
| ```recurringWeekDays``` | ```str``` like ```"0,0,0,0,0,0,0"```| Similar to cron. Replace a 0 with a 1 to run on that day. str[0] = Sunday ||
| ```dynamicQQLType``` | ```Literal[0,1,2]``` | 0 = Do not use QQL, 1 = use patch QQL, 2 = use vulnerability QQL ||
| ```isDynamicPatchesQQL``` | ```bool=False``` | Whether to use dynamic patches QQL ||
| ```dynamicPatchesQQL``` | ```str``` | The QQL to use for dynamic patches ||
| ```continueOnPatchFailure``` | ```bool=True``` | (Linux only) Whether to continue the job if a patch fails ||
| ```preDeployment``` | ```str``` | Specify a message to display before deployment starts ||
| ```duringDeployment``` | ```str``` | Specify a message to display during deployment ||
| ```postDeployment``` | ```str``` | Specify a message to display after deployment ||
| ```onComplete``` | ```str``` | Specify a message to display when the job completes ||
| ```rebootCountdown``` | ```str``` | Specify a message to display before a reboot ||
| ```rebootOption``` | ```str``` | Specify a message for after a reboot ||
| ```suppressReboots``` | ```bool=False``` | Allow users to suppress reboots ||
| ```minimizeWindow``` | ```bool=False``` | Allow users to minimize the deployment window ||
| ```status``` | ```Literal["Disabled", "Enabled"] = "Disabled"``` | The status of the job ||
| ```timeout``` | ```int 1 <= x <= 168``` for hours, ```int 1 <= x <= 10080``` for minutes | The timeout for the job in hours or minutes (specified by timeoutUnit) ||
| ```timeoutUnit``` | Literal["HOURS", "MINUTES"] | The unit of the timeout ||
| ```timezoneType``` | ```Literal["AGENT_TZ", "SPECIFIC_TZ"]``` | The timezone type to use ||
| ```timezone``` | ```str``` | The (timezone)[https://docs.qualys.com/en/pm/api/deployment_job_resource/time_zones.htm] to use. For example: ```"America/New_York"``` ||
| ```opportunisticDownloads``` | ```bool=False``` | Whether to use opportunistic downloads. Only available for Windows ||
| ```linkedJobId``` | ```str``` | The ID of the job to link to ||
| ```notificationType``` | ```bool``` | If true, email notifications are sent ||
| ```notificationConfigRecipientEmail``` | ```str``` | The email to send notifications to ||
| ```notificationConfigCompletedPercentage``` | ```int 1 <= x <= 100``` | The percentage of completion to send notifications at ||
| ```notificationEvents``` | ```bool``` | If true, send notifications when ```onJobStart``` or ```onJobComplete``` are triggered ||
| ```downloadRandomizeTime``` | ```str``` | Provide the job randomize time in hours or minutes. Max is 2 hours or 120 minutes and must be less than the timeout/timeoutUnit ||
| ```downloadRandomizeTimeUnit``` | ```Literal["HOURS", "MINUTES"]``` | The unit of the randomize time ||
| ```additionalDynamicQQLType``` | ```Literal[1,2]``` | 1 = Use patch QQL, 2 = Use vulnerability QQL ||


### Example 1 with GAV Query


```py
from qualysdk.auth import TokenAuth
from qualysdk.pm import create_job
from qualysdk.gav import query_assets

# There are a few ways to pass in certain assets. If you have
# very particular assets in mind, you can make a GAV API
# call to get the agentIds of the assets you want to target:

windows_assets = query_assets(
auth,
filter="operatingSystem.category: `Windows / Server`",
includeFields="agentId",
)

# PM uses GUIDs for almost everything, so we need
# to extract the GUIDs from the assets:
windows_assets_ids = [asset.agentId for asset in windows_assets]

auth = TokenAuth(<username>, <password>, platform='qg1')

# Create a new job for Windows servers. Let's
# focus on critical patches only:
job = create_job(
auth,
platform='Windows',
jobType='Install',
scheduleType='On-demand',
assetIds=windows_assets_ids,
name='My Job',
dynamicPatchesQQL="vendorSeverity:`Critical`",
dynamicQQLType=1,
isDynamicPatchesQQL=True,
status="Enabled", # Immediately enable the job. By default, the job is disabled!
)
>>>"Job 11111111-2222-3333-4444-555555555555 (My Job) created successfully."
```

### Example 2 with Tag GUIDs

```py
from qualysdk.auth import TokenAuth
from qualysdk.pm import create_job

# Using PM tag GUIDs is a bit more cumbersome since
# Qualys does not provide an easy way to look up tag GUIDs,
# but this method is much more flexible since new assets are
# picked up automatically by the job:

auth = TokenAuth(<username>, <password>, platform='qg1')

# Create a new job for Windows servers. Let's
# assume we have a tag for all Windows servers
# with GUID 22222222-3333-4444-5555-666666666666:

job = create_job(
auth,
platform='Windows',
jobType='Install',
scheduleType='On-demand',
assetTagIds=['22222222-3333-4444-5555-666666666666'],
name='My Job',
dynamicPatchesQQL="vendorSeverity:`Critical`",
dynamicQQLType=1,
isDynamicPatchesQQL=True,
status="Enabled", # Immediately enable the job. By default, the job is disabled!
)
>>>"Job 11111111-2222-3333-4444-555555555555 (My Job) created successfully."
```

## ```qualysdk-pm``` CLI tool

The ```qualysdk-pm``` CLI tool is a command-line interface for the PM portion of the SDK. It allows you to quickly pull down results from PM APIs and save them to an XLSX file.
Expand Down
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "qualysdk"
version = "0.1.79"
version = "0.1.80"
description = "SDK for interacting with Qualys APIs, across most modules the platform offers."
authors = ["0x41424142 <jake@jakelindsay.uk>", "0x4A616B65 <jake.lindsay@thermofisher.com>"]
maintainers = ["Jake Lindsay <jake@jakelindsay.uk>"]
Expand Down Expand Up @@ -32,7 +32,7 @@ numpy = "^2.0.1"
pymssql = "^2.3.2"
pymysql = "^1.1.1"
pymdown-extensions = "^10.9"
mkdocs-material = "^9.5.34"
mkdocs-material = "9.5.46"
psycopg2 = {version = "^2.9.9", platform = "win32"}
psycopg2-binary = [
{version = "^2.9.9", platform = "linux"},
Expand Down
9 changes: 5 additions & 4 deletions qualysdk/base/call_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,11 @@ def call_api(
auth_tuple = (auth.username, auth.password)

# Make certain payloads/params requests-friendly:
if payload:
payload = convert_bools_and_nones(payload)
if params:
params = convert_bools_and_nones(params)
if module != "pm":
if payload:
payload = convert_bools_and_nones(payload)
if params:
params = convert_bools_and_nones(params)

# If the URL of an endpoint has the substring
# {placeholder}, .pop() from the params/payload
Expand Down
52 changes: 50 additions & 2 deletions qualysdk/base/call_schemas/pm_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"pm": {
"url_type": "gateway",
"deploymentjobs": {
"endpoint": "/pm/v1/deploymentjobs/{placeholder}",
"endpoint": "/pm/v1/deploymentjobs{placeholder}",
"method": ["GET", "POST", "PATCH", "DELETE"],
"valid_params": [
"placeholder",
Expand All @@ -29,7 +29,7 @@
"auth_type": "token",
},
"deploymentjob": {
"endpoint": "/pm/v1/deploymentjob/{placeholder}",
"endpoint": "/pm/v1/deploymentjob{placeholder}",
"method": ["GET", "POST", "PATCH", "DELETE"],
"valid_params": ["placeholder"],
"valid_POST_data": [
Expand All @@ -38,6 +38,54 @@
"jobInstanceId",
"pageSize",
"sort",
"approvedPatches",
"assetIds",
"assetTagIds",
"coAuthorUserIds",
"continueOnPatchFailure",
"dayOfMonth",
"description",
"duringDeployment",
"dynamicPatchesQQL",
"dynamicQQLType",
"additionalDynamicPatchesQQL",
"additionalDynamicQQLType",
"exclusionAssetIds",
"exclusionTagIds",
"exclusionFilterType",
"filterType",
"isDynamicPatchesQQL",
"matchAllTagIds",
"minimizeWindow",
"monthlyRecurringType",
"name",
"notification",
"opportunisticDownloads",
"patchTuesdayPlusXDays",
"platform",
"postDeployment",
"rebootCountdown",
"rebootOption",
"suppressReboots",
"preDeployment",
"recurring",
"recurringDayOfMonth",
"recurringLastDayOfMonth",
"recurringDayOfMonth",
"recurringWeekDayOfMonth",
"recurringWeekDays",
"scheduleType",
"startDateTime",
"status",
"timeout",
"timeoutUnit",
"downloadRandomizeTime",
"downloadRandomizeTimeUnit",
"timezone",
"timezoneType",
"type",
"linkedJobId",
"linkedJobReferenceCount",
],
"use_requests_json_data": True,
"return_type": "json",
Expand Down
2 changes: 1 addition & 1 deletion qualysdk/pm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
patches themselves and patching jobs.
"""

from .jobs import list_jobs, get_job_results, get_job_runs
from .jobs import list_jobs, get_job_results, get_job_runs, create_job
Loading

0 comments on commit 1d8359c

Please sign in to comment.