Skip to content

Instantly share code, notes, and snippets.

@badstreff
Last active November 14, 2016 21:17
Show Gist options
  • Save badstreff/c5b47bf94630f6d3824e490fc177f665 to your computer and use it in GitHub Desktop.
Save badstreff/c5b47bf94630f6d3824e490fc177f665 to your computer and use it in GitHub Desktop.
#!/usr/bin/python
'''
jss.conf template...
[JSS]
username = api
password = password
URL = https://yourjss.yourcompany.edu:8443
repo_rw_username = username
repo_rw_password = password
repo_name = smb.yourcompany.edu/jss
repo_mount_point = jss_prod_repo
[JSS-DEV]
username = api
password = password
URL = https://yourjss-dev.yourcompany.edu:8443
repo_rw_username = username
repo_rw_password = password
repo_name = smb.yourcompany.edu/jss
repo_mount_point = jss_dev_repo
'''
import jss
import requests
from requests.auth import HTTPBasicAuth
from shutil import copyfile
import xml.etree.ElementTree as ET
import time
import re
import os
import ConfigParser
# GLOBALS
JSS_PROD_URL = None
JSS_PROD_RW_USERNAME = None
JSS_PROD_RW_PASSWORD = None
JSS_PROD_API_USER = None
JSS_PROD_API_PASSWORD = None
JSS_PROD_REPO_NAME = None
JSS_PROD_REPO_MOUNT_POINT = None
JSS_DEV_URL = None
JSS_DEV_RW_USERNAME = None
JSS_DEV_RW_PASSWORD = None
JSS_DEV_API_USER = None
JSS_DEV_API_PASSWORD = None
JSS_DEV_REPO_NAME = None
JSS_DEV_REPO_MOUNT_POINT = None
def main():
# parse the config file to populate global variables
read_config()
# Establish jss connections
jss_prod = jss.JSS(url=JSS_PROD_URL,
user=JSS_PROD_API_USER,
password=JSS_PROD_API_PASSWORD)
jss_dev = jss.JSS(url=JSS_DEV_URL,
user=JSS_DEV_API_USER,
password=JSS_DEV_API_PASSWORD)
# Clear dev objects
print('Clearing objects from dev...')
delete_all(jss_dev.Policy().retrieve_all())
delete_all(jss_dev.ComputerGroup().retrieve_all())
delete_all(jss_dev.OSXConfigurationProfile().retrieve_all())
delete_all(jss_dev.LicensedSoftware().retrieve_all())
delete_all(jss_dev.ComputerExtensionAttribute().retrieve_all())
delete_all(jss_dev.Category().retrieve_all())
delete_all(jss_dev.Package().retrieve_all())
delete_all(jss_dev.Script().retrieve_all())
# sync repo's
print('\nSyncing jss_dev repo with jss_prod...')
mount_smb_share(JSS_PROD_RW_USERNAME,
JSS_PROD_RW_PASSWORD,
JSS_PROD_REPO_NAME,
JSS_PROD_REPO_MOUNT_POINT)
mount_smb_share(JSS_DEV_RW_USERNAME,
JSS_DEV_RW_PASSWORD,
JSS_DEV_REPO_NAME,
JSS_DEV_REPO_MOUNT_POINT)
for pkg in os.listdir(os.path.join(JSS_PROD_REPO_MOUNT_POINT, 'Packages')):
src = os.path.join(JSS_PROD_REPO_MOUNT_POINT, 'Packages', pkg)
dest = os.path.join(JSS_DEV_REPO_MOUNT_POINT, 'Packages', pkg)
if os.path.exists(dest):
print('Package already exists, skipping %s' % dest)
continue
print("Copying %s..." % src)
copyfile(src, dest)
print('done!')
unmount_smb_share(JSS_PROD_REPO_MOUNT_POINT)
unmount_smb_share(JSS_DEV_REPO_MOUNT_POINT)
# Give JSS DB a few seconds to update before copying
print('\nWaiting a few seconds to let the database settle ')
time.sleep(3)
print('\nCopying categories from prod to dev...')
copy_all_categories(jss_prod, jss_dev)
time.sleep(1)
print('\nCopying scripts from prod to dev...')
copy_all_scripts(jss_prod, jss_dev)
time.sleep(1)
print('\nCopying packages from prod to dev...')
copy_all_packages(jss_prod, jss_dev)
time.sleep(1)
print('\nCopying licensed software from prod to dev...')
copy_all_licensed_software(jss_prod, jss_dev)
time.sleep(1)
print('\nCopying extension attributes from prod to dev...')
copy_all_extension_attributes(jss_prod, jss_dev)
time.sleep(1)
print('\nCopying groups from prod to dev...')
copy_all_groups(jss_prod, jss_dev)
time.sleep(1)
print('\nCopying policies from prod to dev...')
copy_all_policies(jss_prod, jss_dev)
time.sleep(1)
def mount_smb_share(username, password, smb, path):
# create path if it does not exist
if not os.path.exists(path):
os.makedirs(path)
# mount it
cmd = ('mount -t smbfs //' + username + ':' + password + '@' + smb + ' ' +
path)
os.system(cmd)
def unmount_smb_share(path):
os.system('umount -f ' + path)
def copy_all_policies(source, dest):
headers = {'Content-Type': 'application/xml', 'Accept': 'application/xml'}
all_policies = source.Policy().retrieve_all()
for p in all_policies:
# Skip policies created by casper remote
if re.match('.*\\|.*\\|.*[0-9]+\\sComputer[s]?', p.name):
print('Skipping remote policy %s' % p.name)
continue
# set category
category = p.find('./general/category/name').text
p.set_category(category)
# fix group id's
for g in p.findall('.//computer_group'):
g.find('id').text = get_group_id_by_name(dest, g.find('name').text)
# fix package id's
for pkg in p.findall(".//package"):
pkg.find('id').text = get_package_id(dest, pkg.find('name').text)
# fix script id's
for script in p.findall(".//script"):
script.find('id').text = get_script_id(dest,
script.find('name').text)
# Strip settings that haven't been implemented yet
search_and_delete_xml(p, 'computers')
# search_and_delete_xml(p, 'scripts')
search_and_delete_xml(p, 'printers')
search_and_delete_xml(p, 'self_service_icon')
search_and_delete_xml(p, 'self_service_categories')
payload = ET.tostring(p)
r = requests.post((dest.base_url + '/JSSResource/policies/id/' + p.id),
data=payload,
headers=headers,
auth=HTTPBasicAuth(JSS_DEV_API_USER,
JSS_DEV_API_PASSWORD))
if r.status_code == 201:
print('Copied policy %s' % p.name)
else:
print('Error copying policy %s' % p.name)
if '<p>Error: Duplicate name</p>' in r.content:
print('Attempting to fix duplicate error for policy...')
p.find('general').find('name').text = (p.name +
' auto_correct_dupe')
payload = ET.tostring(p)
r = requests.post((dest.base_url +
'/JSSResource/policies/id/' +
p.id),
data=payload,
headers=headers,
auth=HTTPBasicAuth(JSS_DEV_API_USER,
JSS_DEV_API_PASSWORD))
if r.status_code == 201:
print('Copied policy %s' % p.name)
time.sleep(1)
def search_and_delete_xml(xml, tag):
for child in xml:
if child.tag == tag:
xml.remove(child)
else:
search_and_delete_xml(child, tag)
return
def copy_all_groups(source, dest):
all_groups = source.ComputerGroup().retrieve_all()
for g in all_groups:
headers = {'Content-Type': 'application/xml',
'Accept': 'application/xml'}
# Strip all computers from the group before copying
g.remove(g.find('computers'))
payload = ET.tostring(g)
r = requests.post((dest.base_url + '/JSSResource/computergroups/id/' +
g.id),
data=payload,
headers=headers,
auth=HTTPBasicAuth(JSS_DEV_API_USER,
JSS_DEV_API_PASSWORD))
if r.status_code == 201:
print('Copied group %s' % g.name)
else:
print('Error copying group %s' % g.name)
print(r.status_code)
print(r.content)
print(g)
def copy_all_packages(source, dest):
all_packages = source.Package().retrieve_all()
for p in all_packages:
headers = {'Content-Type': 'application/xml',
'Accept': 'application/xml'}
search_and_delete_xml(p, 'id')
payload = ET.tostring(p)
r = requests.post((dest.base_url + '/JSSResource/packages/id/0'),
data=payload,
headers=headers,
auth=HTTPBasicAuth(JSS_DEV_API_USER,
JSS_DEV_API_PASSWORD))
if r.status_code == 201:
print('Created package %s' % p.name)
else:
print('Error creating package %s' % p.name)
print(r.status_code)
print(r.content)
print(p)
def get_package_id(connection, pkg_name):
all_packages = connection.Package().retrieve_all()
for p in all_packages:
if p.name == pkg_name:
return p.id
def copy_all_licensed_software(source, dest):
all_software = source.LicensedSoftware().retrieve_all()
for software in all_software:
headers = {'Content-Type': 'application/xml',
'Accept': 'application/xml'}
# Strip all computers from the group before copying
payload = ET.tostring(software)
r = requests.post((dest.base_url +
'/JSSResource/licensedsoftware/id/' +
software.id),
data=payload,
headers=headers,
auth=HTTPBasicAuth(JSS_DEV_API_USER,
JSS_DEV_API_PASSWORD))
if r.status_code == 201:
print('Copied licensed software %s' % software.name)
else:
print('Error copying licensed software %s' % software.name)
print(r.status_code)
print(r.content)
print(software)
def copy_all_extension_attributes(source, dest):
all_attributes = source.ComputerExtensionAttribute().retrieve_all()
for attr in all_attributes:
headers = {'Content-Type': 'application/xml',
'Accept': 'application/xml'}
# Strip all computers from the group before copying
payload = ET.tostring(attr)
r = requests.post((dest.base_url +
'/JSSResource/computerextensionattributes/id/' +
attr.id),
data=payload,
headers=headers,
auth=HTTPBasicAuth(JSS_DEV_API_USER,
JSS_DEV_API_PASSWORD))
if r.status_code == 201:
print('Copied extension attribute %s' % attr.name)
else:
print('Error copying extension attribute %s' % attr.name)
print(r.status_code)
print(r.content)
print(attr)
def copy_all_categories(source, dest):
headers = {'Content-Type': 'application/xml', 'Accept': 'application/xml'}
all_categories = source.Category().retrieve_all()
for category in all_categories:
# Strip all computers from the group before copying
payload = ET.tostring(category)
r = requests.post((dest.base_url +
'/JSSResource/categories/id/' +
category.id),
data=payload,
headers=headers,
auth=HTTPBasicAuth(JSS_DEV_API_USER,
JSS_DEV_API_PASSWORD))
if r.status_code == 201:
print('Copied category %s' % category.name)
else:
print('Error copying category %s' % category.name)
print(r.status_code)
print(r.content)
print(category)
def copy_all_scripts(source, dest):
headers = {'Content-Type': 'application/xml', 'Accept': 'application/xml'}
all_scripts = source.Script().retrieve_all()
for script in all_scripts:
search_and_delete_xml(script, 'id')
payload = ET.tostring(script)
r = requests.post((dest.base_url +
'/JSSResource/scripts/id/0'),
data=payload,
headers=headers,
auth=HTTPBasicAuth(JSS_DEV_API_USER,
JSS_DEV_API_PASSWORD))
if r.status_code == 201:
print('Copied script %s' % script.name)
else:
print('Error copying script %s' % script.name)
print(r.status_code)
print(r.content)
print(script)
def get_group_id_by_name(connection, name):
all_groups = connection.ComputerGroup().retrieve_all()
for g in all_groups:
if g.name == name:
return g.id
def get_script_id(connection, name):
all_scripts = connection.Script().retrieve_all()
for s in all_scripts:
if s.name == name:
return s.id
def delete_all(objects):
for o in objects:
if o.can_delete:
o.delete()
print('Found and deleted object: %s' % o.name)
else:
print('Unable to delete object: %s' % o.name)
def read_config():
global JSS_PROD_URL
global JSS_PROD_RW_USERNAME
global JSS_PROD_RW_PASSWORD
global JSS_PROD_API_USER
global JSS_PROD_API_PASSWORD
global JSS_PROD_REPO_NAME
global JSS_PROD_REPO_MOUNT_POINT
global JSS_DEV_URL
global JSS_DEV_RW_USERNAME
global JSS_DEV_RW_PASSWORD
global JSS_DEV_API_USER
global JSS_DEV_API_PASSWORD
global JSS_DEV_REPO_NAME
global JSS_DEV_REPO_MOUNT_POINT
# Config Parsing and variable setup
config = ConfigParser.ConfigParser()
config.read('jss.conf')
JSS_PROD_URL = config.get('JSS', 'URL')
JSS_PROD_API_USER = config.get('JSS', 'username')
JSS_PROD_API_PASSWORD = config.get('JSS', 'password')
JSS_PROD_RW_USERNAME = config.get('JSS', 'repo_rw_username')
JSS_PROD_RW_PASSWORD = config.get('JSS', 'repo_rw_password')
JSS_PROD_REPO_NAME = config.get('JSS', 'repo_name')
JSS_PROD_REPO_MOUNT_POINT = config.get('JSS', 'repo_mount_point')
JSS_DEV_URL = config.get('JSS-DEV', 'URL')
JSS_DEV_API_USER = config.get('JSS-DEV', 'username')
JSS_DEV_API_PASSWORD = config.get('JSS-DEV', 'password')
JSS_DEV_RW_USERNAME = config.get('JSS-DEV', 'repo_rw_username')
JSS_DEV_RW_PASSWORD = config.get('JSS-DEV', 'repo_rw_password')
JSS_DEV_REPO_NAME = config.get('JSS-DEV', 'repo_name')
JSS_DEV_REPO_MOUNT_POINT = config.get('JSS-DEV', 'repo_mount_point')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment