Skip to content

Commit 9dfd651

Browse files
committed
Sync buildings from jamf to snipe
1 parent d706864 commit 9dfd651

File tree

2 files changed

+115
-4
lines changed

2 files changed

+115
-4
lines changed

jamf2snipe

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,62 @@ def get_snipe_user_id(username):
565565
except:
566566
return "NotFound"
567567

568+
def get_snipe_locations(previous=[]):
569+
locations_url = f"{snipe_base}/api/v1/locations"
570+
payload = {"limit": 100, "offset": len(previous)}
571+
logging.debug("The payload for the snipe locations GET is {}".format(payload))
572+
response = session.get(
573+
locations_url,
574+
headers=snipeheaders,
575+
params=payload,
576+
verify=user_args.do_not_verify_ssl,
577+
hooks={"response": request_handler},
578+
)
579+
response_json = response.json()
580+
current = response_json["rows"]
581+
if previous:
582+
current = previous + current
583+
if response_json["total"] > len(current):
584+
logging.debug(
585+
"We have more than 100 locations, get the next page - total: {} current: {}".format(
586+
response_json["total"], len(current)
587+
)
588+
)
589+
return get_snipe_locations(current)
590+
else:
591+
return current
592+
593+
594+
def get_snipe_location_id(location_name):
595+
if location_name == "":
596+
return "NotFound"
597+
location_name = location_name.lower()
598+
for location in snipe_locations:
599+
for value in location.values():
600+
if str(value).lower() == location_name:
601+
id = location["id"]
602+
return id
603+
logging.debug(
604+
"No matches in snipe_locations for {}, querying the API for the next closest match".format(
605+
location_name
606+
)
607+
)
608+
location_id_url = "{}/api/v1/locations".format(snipe_base)
609+
payload = {"search": location_name, "limit": 1, "sort": "name", "order": "asc"}
610+
logging.debug("The payload for the snipe location search is: {}".format(payload))
611+
response = session.get(
612+
location_id_url,
613+
headers=snipeheaders,
614+
params=payload,
615+
verify=user_args.do_not_verify_ssl,
616+
hooks={"response": request_handler},
617+
)
618+
try:
619+
return response.json()["rows"][0]["id"]
620+
except:
621+
return "NotFound"
622+
623+
568624
# Function that creates a new Snipe Model - not an asset - with a JSON payload
569625
def create_snipe_model(payload):
570626
api_url = '{}/api/v1/models'.format(snipe_base)
@@ -644,7 +700,28 @@ def checkin_snipe_asset(asset_id):
644700
return response
645701

646702
# Function that checks out an asset in snipe
647-
def checkout_snipe_asset(user, asset_id, checked_out_user=None):
703+
def checkout_snipe_asset(location, asset_id, checked_out_user=None):
704+
jamfsplit = config["user-mapping"]["jamf_api_field"].split()
705+
checked_out = None
706+
707+
user = None
708+
building = None
709+
for field in jamfsplit:
710+
if field in location:
711+
if field == "username":
712+
user = location[field]
713+
elif field == "building":
714+
building = location[field]
715+
716+
if user:
717+
checked_out = checkout_snipe_asset_to_user(user, asset_id, checked_out_user)
718+
719+
if not checked_out and building:
720+
checked_out = checkout_snipe_asset_to_location(building, asset_id, checked_out_user)
721+
722+
return checked_out
723+
724+
def checkout_snipe_asset_to_user(user, asset_id, checked_out_user=None):
648725
logging.debug('Asset {} is being checked out to {}'.format(user, asset_id))
649726
user_id = get_snipe_user_id(user)
650727
if user_id == 'NotFound':
@@ -677,6 +754,37 @@ def checkout_snipe_asset(user, asset_id, checked_out_user=None):
677754
logging.error('Asset checkout failed for asset {} with error {}'.format(asset_id,response.text))
678755
return response
679756

757+
758+
def checkout_snipe_asset_to_location(location, asset_id, checked_out_user=None):
759+
location_id = get_snipe_location_id(location)
760+
api_url = "{}/api/v1/hardware/{}/checkout".format(snipe_base, asset_id)
761+
logging.info("Checking out {} to check it out to {}".format(asset_id, location))
762+
payload = {
763+
"checkout_to_type": "location",
764+
"assigned_location": location_id,
765+
"note": "Assignment made automatically, via script from Jamf.",
766+
}
767+
logging.debug("The payload for the snipe checkin is: {}".format(payload))
768+
response = session.post(
769+
api_url,
770+
headers=snipeheaders,
771+
json=payload,
772+
verify=user_args.do_not_verify_ssl,
773+
hooks={"response": request_handler},
774+
)
775+
logging.debug("The response from Snipe IT is: {}".format(response.json()))
776+
if response.status_code == 200:
777+
logging.debug("Got back status code: 200 - {}".format(response.content))
778+
return "CheckedOut"
779+
else:
780+
logging.error(
781+
"Asset checkout failed for asset {} with error {}".format(
782+
asset_id, response.text
783+
)
784+
)
785+
return response
786+
787+
680788
### Run Testing ###
681789
# Report if we're verifying SSL or not.
682790
logging.info("SSL Verification is set to: {}".format(user_args.do_not_verify_ssl))
@@ -749,6 +857,7 @@ jamf_types = {
749857

750858
if user_args.users or user_args.users_force or user_args.users_inverse:
751859
snipe_users = get_snipe_users()
860+
snipe_locations = get_snipe_locations()
752861

753862
TotalNumber = 0
754863
if user_args.computers:
@@ -892,8 +1001,8 @@ for jamf_type in jamf_types:
8921001
if jamfsplit[1] not in jamf[jamfsplit[0]]:
8931002
logging.info("Couldn't find {} for this device in {}, not checking it out.".format(jamfsplit[1], jamfsplit[0]))
8941003
continue
895-
logging.info('Checking out new item {} to user {}'.format(jamf['general']['name'], jamf['{}'.format(jamfsplit[0])]['{}'.format(jamfsplit[1])]))
896-
checkout_snipe_asset(jamf['{}'.format(jamfsplit[0])]['{}'.format(jamfsplit[1])],new_snipe_asset[1].json()['payload']['id'], "NewAsset")
1004+
logging.info('Checking out new item {} to user {}'.format(jamf['general']['name'], jamf['{}'.format(jamfsplit[0])]))
1005+
checkout_snipe_asset(jamf['{}'.format(jamfsplit[0])],new_snipe_asset[1].json()['payload']['id'], "NewAsset")
8971006
# Log an error if there's an issue, or more than once match.
8981007
elif snipe == 'MultiMatch':
8991008
logging.warning("WARN: You need to resolve multiple assets with the same serial number in your inventory. If you can't find them in your inventory, you might need to purge your deleted records. You can find that in the Snipe Admin settings. Skipping serial number {} for now.".format(jamf['general']['serial_number']))
@@ -967,7 +1076,7 @@ for jamf_type in jamf_types:
9671076
if jamfsplit[1] not in jamf[jamfsplit[0]]:
9681077
logging.info("Couldn't find {} for this device in {}, not checking it out.".format(jamfsplit[1], jamfsplit[0]))
9691078
continue
970-
checkout_snipe_asset(jamf['{}'.format(jamfsplit[0])]['{}'.format(jamfsplit[1])], snipe_id, snipe['rows'][0]['assigned_to'])
1079+
checkout_snipe_asset(jamf['{}'.format(jamfsplit[0])], snipe_id, snipe['rows'][0]['assigned_to'])
9711080
else:
9721081
logging.info("Can't checkout {} since the status isn't set to deployable".format(jamf['general']['name']))
9731082

settings.conf.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ name = general name
3131

3232
[user-mapping] # The field from jamf that you want to search Snipe with
3333
jamf_api_field = location username
34+
# if you want to sync buildings
35+
# jamf_api_field = location username building

0 commit comments

Comments
 (0)