Publish Map Service with modified Properties

5929
11
Jump to solution
07-06-2016 01:38 AM
BenVan_Kesteren1
Regular Contributor


Hi All,

I currently have a Python script which does 98% of what I require.

Currently, it picks up a local MXD file that has been modified in the last 3 days, then uses arcpy.mapping.CreateMapSDDraft() to create the Draft Service Definition, then stages the service using arcpy.StageService_server() and finally arcpy.UploadServiceDefinition_server()

Each time this file is uploaded, it is overwriting an existing map service, and these services all have Advanced Properties modified, for example the maxImageWidth and Height are modified, along with Max Number of Instanced modified to be 15.

The problem I have is that each time this script runs, it reverts those three settings back to the default, so then I have to manually set them to what we require, which in turn defeats the purpose of automating this process... I have pasted my working code below, its not pretty, but seems to do most of what I require....

Any ideas on how to set those additional parameters in python?

def publish_services(service):


    log.write_log(python_file, 'Updating Services (' + service + ') to GISServer')
    err = False
    # Define Local Variables
    wrkspc = r'\\gisserver\weave projects\Project_Files'
    doc = os.path.join(wrkspc, service + '.mxd')
    log.write_log(python_file, 'Publishing File to Service:')
    log.write_log(python_file, '    %s [doc]' % doc)
    is_edited = check_edit_date(service, doc)
    if is_edited is True:
        mapDoc = arcpy.mapping.MapDocument(doc)


        # Provide path to connection file
        # To create this file, right-click a folder in the Catalog window and
        #   click New > ArcGIS Server Connection
        con = 'GIS Servers\GisServer (Administration)'


        # Provide other service details
        sddraft = os.path.join(wrkspc, service + '.sddraft')
        sd = os.path.join(wrkspc, service + '.sd')
        summary = 'Map document displayed by the Weave Mapping System'
        tags = 'Weave, python, automated, MXD, Map Service Automation, No Man Hours Used'


        def del_sd():
            try:
                os.remove(sd)
                log.write_log(python_file, 'Removed the existing sd file - %s' % str(sd))
            except OSError:
                pass


        log.write_log(python_file, 'Creating .sddraft file')


        # Create service definition draft (.sddraft)
        arcpy.mapping.CreateMapSDDraft(mapDoc, sddraft, service, 'ARCGIS_SERVER',
            con, True, None, summary, tags)


        log.write_log(python_file, 'Analyzing the .sddraft file for errors')
        # Analyze the service definition draft
        analysis = arcpy.mapping.AnalyzeForSD(sddraft)


        # Print errors, warnings, and messages returned from the analysis
        try:
            for key in 'errors':  # 'messages', 'warnings'):    # This is commented out for clarity
                log.write_log(python_file,  "--------" + key.upper() + "--------")
                vars = analysis[key]
                for ((message, code), layerlist) in vars.iteritems():
                    log_message = "    ", message, " (CODE %i)" % code
                    log.write_log(python_file, log_message)
                    log.write_log(python_file, "       applies to:", )
                    for layer in layerlist:
                        log.write_log(python_file, '\n             ' + str(layer.name), )
                    log.write_log(python_file, "")
        except:
            log.write_log(python_file, 'Ironically, there was an error printing the errors')
            print 'Ironically, there was an error printing the errors in the publish_services() module'
            err = True


        # remove file if it already exists,
        # need the folder to be cleared before creating again
        del_sd()


        # Stage and upload the service if the sddraft analysis did not contain errors
        if analysis['errors'] == {} or err is False:
            # Execute StageService
            arcpy.StageService_server(sddraft, sd)
            # Execute UploadServiceDefinition
            arcpy.UploadServiceDefinition_server(sd, con)
            log.write_log(python_file, "Service %s successfully published" % service)
        else:
            # if the sddraft analysis contained errors, display them
            log.write_log(python_file, str(analysis['errors']))
            log.write_log(python_file, "Service %s could not be published because errors were found during analysis."
                          % service
                          )


        # remove the temporary file once tasks complete
       del_sd()
    else:
        pass
0 Kudos
1 Solution

Accepted Solutions
BenVan_Kesteren1
Regular Contributor

So after many hours of trial and error, then implementing most of the changes as suggested in this thread, I contacted esri australia tech support. 

They pointed out the errors of my ways.

A few misunderstandings from me as to how JSON works, and other simple code tweaks, the end result is below, works a treat!

service = 'Service Name'

# A function to generate a token given username, password and the adminURL.
def getToken():

    portalAdminName = 'gisadmin'  # Gisserver login
    portalAdminPassword = 'xxxxxxx'  # gisserver password
    serverName = 'gisserver'  # Server Name
    serverPort = 6080  # Port number of the server
    # Token URL is typically http://server[:port]/arcgis/admin/generateToken
    tokenURL = "/arcgis/admin/generateToken"

    params = urllib.urlencode({
        'username': portalAdminName,
        'password': portalAdminPassword,
        'client': 'requestip',
        'f': 'json'
    })

    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}

    # Connect to URL and post parameters
    httpConn = httplib.HTTPConnection(serverName, serverPort)
    httpConn.request("POST", tokenURL, params, headers)

    # Read response
    response = httpConn.getresponse()

    if response.status != 200:
        httpConn.close()
        print "Error while fetching tokens from admin URL. Please check the URL and try again."
        return
    else:
        data = response.read()
        httpConn.close()

    # Extract the token from it
    token = json.loads(data)

    return token.get('token')


# Generate Token
token = getToken()

# Add token to service URL to authenticate access.
params = {'token': token, 'f': 'json'}

# build the URL used to connect to the web form
fUrl = 'http://gisserver:6080/arcgis/admin/services/' + service + '.MapServer?' + urllib.urlencode(params)

# Open service URL to access parameters
openUrl = urllib2.urlopen(fUrl, '').read()

# Load json response in string serviceJson to access later
servicePython = json.loads(openUrl)

# Change the maxImage size properties in the python dictionary
servicePython['properties']['maxImageHeight'] = 5200
servicePython['properties']['maxImageWidth'] = 6600
servicePython['maxInstancesPerNode'] = 15

# Convert python dictionary back into a JSON string
serviceJson = json.dumps(servicePython)

# Define params dictionary
params = {"service": serviceJson, "f": "json", "token": token}

# REST endpoint for editing the service
finalUrl = 'http://gisserver:6080/arcgis/admin/services/' + service + '.MapServer/edit?'

# URLencoded paramaters dictionary can be included as a parameter of the call to urllib2.urlopen
openUrl = urllib2.urlopen(finalUrl, urllib.urlencode(params))

‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

View solution in original post

0 Kudos
11 Replies
BenVan_Kesteren1
Regular Contributor

Hey,

So I might have found something useful here, it may work - Example: Edit service properties—Documentation (10.3 and 10.3.1) | ArcGIS for Server

This section of the code may do part of what I require:

# Edit desired properties of the service
dataObj["minInstancesPerNode"] = minInstancesNum
dataObj["maxInstancesPerNode"] = maxInstancesNum

My secondary question is, how do I find what all the options are called? The above example exactly answers part of my question, as one of the items I want to update is max instances, but how do i find out what the syntax would be for the Max image Width and height will be? How does one go about searching for this??

Thanks in advance.

Cheers

DanPatterson_Retired
MVP Esteemed Contributor

If you are in this extension, An overview of the Publishing toolset—Help | ArcGIS for Desktop

just check the code samples for each of the tools

0 Kudos
SubuSwaminathan1
Occasional Contributor

ArcGIS REST API - Out of box Service/extension types and properties listed here in the REST API docs

0 Kudos
ChrisPedrezuela
Occasional Contributor III

from documentation, Upload Service Definition—Help | ArcGIS for Desktop , this script creates and modifies service definition draft before uploading to server

0 Kudos
BenVan_Kesteren1
Regular Contributor

Hi Everyone for your suggestions. I have looked through each of the links provided, unfortunately I am yet to see an example to help me set the MaxImageWidth and MaxImageHeight... back to the drawing board, I may have to continue to do this manually :\

Thanks anyways.

0 Kudos
DavidMann1
New Contributor III

Ben - I was struggling with the same issue last week and came across this post along with many others to help me figure out how to do it.  What I ended up doing was using the ArcGIS Server Admin URL to access the service parameters of the service to be overwritten in JSON format, went through the process you describe of creating SDDRAFT, Staging SD, and Uploading SD, and then finally using the JSON config I saved to Edit the parameters of the service after published with default parameters.  No need to pick out the individual parameters this way that you want to preserve, you keep them all.  Code snippets of applicable parts below (sorry don't know how to format as code block)...

adminUrl = 'https://services.gisserver.com:6443/arcgis/admin/services/'

# Start of get token function
def generateToken(username, password, portalUrl):

   '''Retrieves a token to be used with API requests.'''
   parameters = urllib.urlencode({'username': username,

   'password': password,

   'client': 'referer',

   'referer': portalUrl,

   'expiration': 60,

   'f': 'json'})

   try:

  response = urllib2.urlopen(portalUrl + '/sharing/rest/generateToken?',

  parameters).read()

   except Exception as e:

  log.write('Unable to open the url %s/sharing/rest/generateToken' % (portalUrl))

  log.write(e)

  responseJSON = json.loads(response.strip(' \t\n\r'))

   # Log results
   if responseJSON.has_key('error':(

  errDict = responseJSON['error']

   if int(errDict['code']) == 498:

   message = 'Token Expired. Getting new token... '
   token = generateToken(username, password, portalUrl)

   else:

  message = 'Error Code: %s \n Message: %s' % (errDict['code'],

  errDict['message'])

  log.write(message)

   token = responseJSON.get('token')

   return token

# Generate a token
token = generateToken(portalAdminName, portalAdminPassword, portalUrl)

# Add token to service URL to authenticate access.
params = {'token': token,

   'f': 'json'}

fUrl = adminUrl + sfolder + '/' + service + '.MapServer?' + urllib.urlencode(params)

# Open service URL to access parameters

openUrl = urllib2.urlopen(fUrl, '').read()

# Load json response in string serviceJson to access later
serviceJson = json.loads(openUrl)

# Check response for valid service
if '{"serviceName":' not in serviceJson:

   raise Exception(('Service access error, response = {}').format(json.dumps(serviceJson)))

# After republishing service...

# Load parameters for edit service call, dumping serviceJson string back into JSON format
params = {'token': token,

   'f': 'json',

   'service': json.dumps(serviceJson)}

fUrl = adminUrl + sfolder + '/' + service + '.MapServer/edit?' + urllib.urlencode(params)

openUrl = urllib2.urlopen(fUrl, '')

BenVan_Kesteren1
Regular Contributor

Thanks very much for the reply David, I have tweaked what you have provided and been able to work out how to connect and generate a token. That works nicely.

I am now trying to connect back to the server and publish my new parameters, and I am getting this error:

Traceback (most recent call last):

  File "U:/Automated/editMapServiceParameters.py", line 62, in <module>

    raise Exception('Service access error, response = {}'.format(json.dumps(serviceJson)))

Exception: Service access error, response = {"status": "error", "code": 500, "messages": ["1"]}

I am unsure how to progress from here. Did you hit this hurdle in your travels by any chance?

Here is the code which causes the above error:

import json
import urllib
import urllib2
import httplib


adminUrl = 'http://gisserver:6080/arcgis/admin/services'
portalAdminName = 'gisadmin'
portalAdminPassword = PASSWORD
portalUrl = "/arcgis/admin/generateToken"
serverName = 'gisserver'
serverPort = 6080




# A function to generate a token given username, password and the adminURL.
def getToken(username, password, serverName, serverPort):
    # Token URL is typically http://server[:port]/arcgis/admin/generateToken
    tokenURL = "/arcgis/admin/generateToken"


    params = urllib.urlencode({'username': username, 'password': password, 'client': 'requestip', 'f': 'json'})


    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}


    # Connect to URL and post parameters
    httpConn = httplib.HTTPConnection(serverName, serverPort)
    httpConn.request("POST", tokenURL, params, headers)


    # Read response
    response = httpConn.getresponse()


    if response.status != 200:
        httpConn.close()
        print "Error while fetching tokens from admin URL. Please check the URL and try again."
        return
    else:
        data = response.read()
        httpConn.close()


    # Extract the token from it
    token = json.loads(data)


    return token.get('token')


# Generate Token
token = getToken(portalAdminName, portalAdminPassword, serverName, serverPort)


print 'token:: %s' % token


# Add token to service URL to authenticate access.
params = {'token': token, 'f': 'json'}


fUrl = 'http://gisserver:6080/arcgis/admin/services/Maps/Weave_Inset.MapServer?' + urllib.urlencode(params)
print fUrl


# Open service URL to access parameters
openUrl = urllib2.urlopen(fUrl, '').read()


# Load json response in string serviceJson to access later
serviceJson = json.loads(openUrl)


# Check response for valid service
if '{"serviceName":' not in serviceJson:
    raise Exception('Service access error, response = {}'.format(json.dumps(serviceJson)))

Thanks again.

0 Kudos
DavidMann1
New Contributor III

Hey Ben - The error is caused by the code not being able to access valid json parameters for an existing service.  What the code is checking for ("serviceName":) is one of the first items to appear on that parameters page when the service exists.  So my guesses would be either a) the service you are trying to overwrite doesn't already exist/isn't running or b) there is a problem accessing due to the token maybe.  I would try accessing the fUrl manually and see if the response looks the same as you are getting in the error.

BenVan_Kesteren1
Regular Contributor

So after many hours of trial and error, then implementing most of the changes as suggested in this thread, I contacted esri australia tech support. 

They pointed out the errors of my ways.

A few misunderstandings from me as to how JSON works, and other simple code tweaks, the end result is below, works a treat!

service = 'Service Name'

# A function to generate a token given username, password and the adminURL.
def getToken():

    portalAdminName = 'gisadmin'  # Gisserver login
    portalAdminPassword = 'xxxxxxx'  # gisserver password
    serverName = 'gisserver'  # Server Name
    serverPort = 6080  # Port number of the server
    # Token URL is typically http://server[:port]/arcgis/admin/generateToken
    tokenURL = "/arcgis/admin/generateToken"

    params = urllib.urlencode({
        'username': portalAdminName,
        'password': portalAdminPassword,
        'client': 'requestip',
        'f': 'json'
    })

    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}

    # Connect to URL and post parameters
    httpConn = httplib.HTTPConnection(serverName, serverPort)
    httpConn.request("POST", tokenURL, params, headers)

    # Read response
    response = httpConn.getresponse()

    if response.status != 200:
        httpConn.close()
        print "Error while fetching tokens from admin URL. Please check the URL and try again."
        return
    else:
        data = response.read()
        httpConn.close()

    # Extract the token from it
    token = json.loads(data)

    return token.get('token')


# Generate Token
token = getToken()

# Add token to service URL to authenticate access.
params = {'token': token, 'f': 'json'}

# build the URL used to connect to the web form
fUrl = 'http://gisserver:6080/arcgis/admin/services/' + service + '.MapServer?' + urllib.urlencode(params)

# Open service URL to access parameters
openUrl = urllib2.urlopen(fUrl, '').read()

# Load json response in string serviceJson to access later
servicePython = json.loads(openUrl)

# Change the maxImage size properties in the python dictionary
servicePython['properties']['maxImageHeight'] = 5200
servicePython['properties']['maxImageWidth'] = 6600
servicePython['maxInstancesPerNode'] = 15

# Convert python dictionary back into a JSON string
serviceJson = json.dumps(servicePython)

# Define params dictionary
params = {"service": serviceJson, "f": "json", "token": token}

# REST endpoint for editing the service
finalUrl = 'http://gisserver:6080/arcgis/admin/services/' + service + '.MapServer/edit?'

# URLencoded paramaters dictionary can be included as a parameter of the call to urllib2.urlopen
openUrl = urllib2.urlopen(finalUrl, urllib.urlencode(params))

‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos