Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dell removed the ordernumbers, Moved over to DOQL for purchases #24

Open
wants to merge 42 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f611309
Added routine to also update systems which had their serial numbers c…
arjenannes-tomtom Feb 15, 2017
43ff847
bugfix: the serial list got longer one every loop. Corrected by initi…
arjenannes-tomtom Feb 15, 2017
938bbe8
Added routine to also update systems which had their serial numbers c…
arjenannes-tomtom Feb 15, 2017
cbb107f
bugfix: Dell has systems registered without Productid. script failed …
arjenannes-tomtom Feb 15, 2017
6f1143f
reduced offset to 50, added service group to dell warranty
arjenannes-tomtom Feb 16, 2017
915eb0b
merged with upstream
arjenannes-tomtom Apr 19, 2017
896e0de
Changed the ServiceLevelDescription to 64 chars limit (as introduced …
arjenannes-tomtom Apr 20, 2017
ab0d6a2
Introduced an option to force the existing records to be updated
arjenannes-tomtom Apr 20, 2017
a2e8cae
Introduced an option to force the existing records to be updated
arjenannes-tomtom Apr 20, 2017
7177ca2
Extended the hasher to support updating line items. Added forcedupdat…
arjenannes-tomtom Apr 20, 2017
ad77a72
Changed 32 char limit to 64 (since 13.1.0). Added support for forced …
arjenannes-tomtom Apr 20, 2017
61d44ad
mentioning the new changes. Notice of IBM warranty lookup up wrong sy…
arjenannes-tomtom Apr 20, 2017
1313951
relocated ship_date to original location. If not there script may hal…
arjenannes-tomtom Apr 21, 2017
d68f9e5
Update warranty as well even if serial has been changed by lifecycle.…
arjenannes-tomtom Apr 21, 2017
ce1c3ec
Merged with upstream
arjenannes-tomtom May 1, 2017
e438d3f
On forcedupdate records were added, not updated. Now fixed for dell
arjenannes-tomtom May 1, 2017
6505b34
Merge branch 'master' of https://github.com/device42/warranty_check
arjenannes-tomtom Apr 10, 2019
7b1c954
decommissioned systems can have _ in serial name. Splitting to real s…
arjenannes-tomtom Apr 10, 2019
1149c6f
Stop script on DELL Api auth failure
arjenannes-tomtom Apr 10, 2019
9087770
removed irrelevant selection
arjenannes-tomtom Apr 10, 2019
bb5735d
on no HTTP 200, wait 30 secs to calm down api
arjenannes-tomtom Apr 15, 2019
0c5bfa9
Merge branch 'master' of https://github.com/blafkachel/warranty_check
arjenannes-tomtom Apr 15, 2019
8c9b8a1
remove duplicate at merge
arjenannes-tomtom Apr 15, 2019
d045371
Add boolean for DEBUG and DOQL
arjenannes-tomtom Apr 18, 2019
d7b936b
hasher correction. lowercase on serial
arjenannes-tomtom Apr 23, 2019
a458601
Use DOQL for gathering the purchases (much faster)
arjenannes-tomtom Apr 23, 2019
359cae2
Dell: if an ordernumber exists in D42, make use of that one. (dell do…
arjenannes-tomtom Apr 23, 2019
b21ce87
serial correction is already being taken care off
arjenannes-tomtom Apr 23, 2019
487a700
Increased Dell API timeout to 60secs
arjenannes-tomtom Apr 23, 2019
24440a5
Relocated output to show real serials, not with epoch
arjenannes-tomtom Apr 23, 2019
e7780f2
Due to Dell removing ordernumber from API, opt to have shipdate as or…
arjenannes-tomtom Apr 23, 2019
433ee42
order number defaults to common.
arjenannes-tomtom Apr 23, 2019
da86408
Default to Dell production URL
arjenannes-tomtom Apr 23, 2019
1982e76
removed incorrect pause comment
arjenannes-tomtom Apr 25, 2019
ba1210d
removed obsolete comments
arjenannes-tomtom Apr 25, 2019
c213c21
Stop script on no return of existing purchaselines to prevent double …
arjenannes-tomtom Apr 25, 2019
b11bf5d
april 2019 updates
arjenannes-tomtom Apr 25, 2019
ae07840
doql and debug are booleans!
arjenannes-tomtom Apr 25, 2019
86f4544
Added known issues section
arjenannes-tomtom Apr 25, 2019
501a6bf
Emphesized debug output on reason for halting the script
arjenannes-tomtom Apr 25, 2019
dc7786f
Timeout to 30secs
arjenannes-tomtom Jul 8, 2019
4f051c0
added python.org comment
arjenannes-tomtom Jul 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,17 @@
## 25.04.2019
- ordernumbers: Added shipdate option in warranty.cfg for dell systems. This due to dell removing the orderno from the api output
- if an existing registration exists, make use of that order number to compare/register
- ordernumbers: instead of vendor set common as default. Though might want to set descrete as default, or skipdate. Device42 doesn't show more than 1000 lines in the webbgui overview of an order.
- Defaulting to Dell API production url
- Debug output shows proper serials, not including the \_epoch
- Using DOQL for getting existing purchases to speed up script execution
- added boolean for DOQL and DEBUG (debug existed, but now also works in starter.py)
- bugfix: corrected hasher comparison
- bugfix: corrected working of the booleans (true/false actually works now)
- Dell API: increased api retry to 60secs
- bugfix: If no purchaselines are returned the script will stop (can be forced to go on with boolean forcedupdate in warranty.cfg). Reason for doing: noticed sometimes the query didn't give purchaselines, just ordernumbers. In that case the script would continue and make double entries.


## 30.04.2017
- service type field has been extended in version 13.1.0.
- Added an option to force updating the line items. Needed as the Service Type does not get updated otherwise as the contents of the field can't be checked via the api to see if it needs a change.
Expand Down
Binary file added Files/__init__.pyc
Binary file not shown.
30 changes: 30 additions & 0 deletions Files/shared.py
Expand Up @@ -14,6 +14,7 @@
if os.path.isfile(CONFIGFILE):
CC.readfp(open(CONFIGFILE, "r"))
DEBUG = CC.getboolean('other', 'debug')
DOQL = CC.getboolean('other', 'doql')
RETRY = int(CC.get('other', 'retry'))
ORDER_NO_TYPE = CC.get('other', 'order_no_type')
else:
Expand Down Expand Up @@ -44,6 +45,8 @@ def get_config(self, source):
res = self.__get_ibm_cfg()
elif source == 'lenovo':
res = self.__get_lenovo_cfg()
elif source == 'other':
res = self.__get_other_cfg()
else:
print '\n[!] Error. Unknown source "%s".\n\tExiting...\n' % source
sys.exit()
Expand Down Expand Up @@ -125,6 +128,15 @@ def __get_lenovo_cfg(self):
'url2': lenovo_url2
}

def __get_other_cfg(self):
# lenovo -------------------------------------------
debug = self.cc.getboolean('other', 'debug')
doql = self.cc.getboolean('other', 'doql')
return {
'debug': debug,
'doql': doql
}


class Device42rest:
def __init__(self, params):
Expand Down Expand Up @@ -192,6 +204,24 @@ def get_purchases(self):
response = self.get_data(api_path)
return response

def fetcher(self, url):
headers = {
'Authorization': 'Basic ' + base64.b64encode(self.username + ':' + self.password),
'Content-Type': 'application/x-www-form-urlencoded'
}

r = requests.get(url, headers=headers, verify=False)
msg = 'Status code: %s' % str(r.status_code)
return r.text

def get_doqldata(self, query):
if DEBUG:
print '\n[!] DOQL: ', query
api_path = '/services/data/v1.0/query/?query='
url = self.url+'/services/data/v1.0/query/?query='+query
response = self.fetcher(url)
return response

def get_lifecycle(self):
if DEBUG:
print '\n[!] Fetching life cycle purchase events from Device42'
Expand Down
Binary file added Files/shared.pyc
Binary file not shown.
14 changes: 10 additions & 4 deletions Files/warranty.cfg.example
Expand Up @@ -15,7 +15,10 @@ forcedupdate = False
[dell]
# set api_key as provided by Dell
api_key =
url = https://sandbox.api.dell.com/support/assetinfo/v4/getassetwarranty
# production
url = https://api.dell.com/support/assetinfo/v4/getassetwarranty
# sandbox
#url = https://sandbox.api.dell.com/support/assetinfo/v4/getassetwarranty

[hp]
# set api_key as provided by HP
Expand All @@ -34,10 +37,13 @@ url2 = http://support.lenovo.com/us/en/products
[other]
# print verbose info to STDOUT
debug = True
# use DOQL instead
doql = True
# number of retries for HTTP connections
retry = 3
# order number type can be one of:
# 1. vendor - order number is returned from vendor ( only for Dell )
# 1. vendor - order number is returned from vendor ( used to work for Dell. API doesn't return them anymore )
# 2. common - order number is randomly generated and same for all purchases
# 3. descrete - order number is randomly generated and uniqe for every purchase
order_no_type = vendor
# 3. descrete - order number is randomly generated and unique for every purchase
# 4. shipdate - order number is based on the shipping date of the equipment ( only for Dell )
order_no_type = common
Binary file added Files/warranty_abstract.pyc
Binary file not shown.
45 changes: 32 additions & 13 deletions Files/warranty_dell.py
Expand Up @@ -30,11 +30,6 @@ def run_warranty_check(self, inline_serials, retry=True):
global full_serials
full_serials = {}

if self.debug:
print '\t[+] Checking warranty info for "%s"' % inline_serials
timeout = 10


# making sure the warranty also gets updated if the serial has been changed by decom lifecycle process
incoming_serials = inline_serials.split(',')
inline_serials = []
Expand All @@ -52,16 +47,24 @@ def run_warranty_check(self, inline_serials, retry=True):
inline_serials.append(d42_serial)
inline_serials = ','.join(inline_serials)

if self.debug:
print '\t[+] Checking warranty info for "%s"' % inline_serials
timeout = 30

payload = {'id': inline_serials, 'apikey': self.api_key, 'accept': 'Application/json'}

try:
resp = requests.get(self.url, params=payload, verify=True, timeout=timeout)
msg = 'Status code: %s' % str(resp.status_code)
if str(resp.status_code) == '401' or str(resp.status_code) == '404':
if str(resp.status_code) != '200':
print '\t[!] HTTP error. Message was: %s' % msg
print '\t[!] waiting for 30 seconds to let the api server calm down'
# suspecting blockage due to to many api calls. Put in a pause of 30 seconds and go on
time.sleep(30)
if str(resp.status_code) == '401':
print '\t[!] API call unauthorized. Wrong/expired key? Wrong endpoint?'
if str(resp.status_code) == '404':
print '\t[!] 404: Information not found?'
# suspecting blockage due to to many api calls. Put in a pause and go on
print '\t[!] waiting for 60 seconds to let the api server calm down'
time.sleep(60)
if retry:
print '\n[!] Retry'
self.run_warranty_check(inline_serials, False)
Expand All @@ -74,7 +77,7 @@ def run_warranty_check(self, inline_serials, retry=True):
self.error_msg(e)
return None

def process_result(self, result, purchases):
def process_result(self, result, purchases, ordernos):
global full_serials
data = {}

Expand All @@ -98,9 +101,24 @@ def process_result(self, result, purchases):
order_no = asset['OrderNumber']
elif self.order_no == 'common':
order_no = self.common
elif self.order_no == 'shipdate':
if self.debug:
print '[!] WORKING ON: \n',item
try:
#some systems have no shipping date mentioned
#or the service_tag is incorrect. Confirm via iDrac and support.dell.com
order_no = asset['ShipDate'].split('T')[0]
except:
order_no = '0000-00-00'
else:
order_no = self.generate_random_order_no()

try:
#If an existing registration with ordernumber exists, make use of that one.
order_no = ordernos[asset['ServiceTag'].lower()]
except Exception as e:
print e

serial = asset['ServiceTag']
customernumber = asset['CustomerNumber']
country = asset['CountryLookupCode']
Expand Down Expand Up @@ -171,13 +189,14 @@ def process_result(self, result, purchases):
data.update({'line_end_date': end_date})

# update or duplicate? Compare warranty dates by serial, contract_id, start date and end date
hasher = serial + line_contract_id + start_date + end_date
hasher = serial.lower() + line_contract_id + start_date + end_date
try:
d_purchase_id, d_order_no, d_line_no, d_contractid, d_start, d_end, forcedupdate = purchases[hasher]

if forcedupdate:
data['purchase_id'] = d_purchase_id
data.pop('order_no')
data.update({'order_no': start_date})
data.update({'purchase_id': d_purchase_id})
data.update({'line_no': d_line_no})
raise KeyError

# check for duplicate state
Expand Down
Binary file added Files/warranty_dell.pyc
Binary file not shown.
2 changes: 1 addition & 1 deletion Files/warranty_hp.py
Expand Up @@ -231,7 +231,7 @@ def process_result(self, result, purchases):
pass

# update or duplicate? Compare warranty dates by serial, contract_id and end date
hasher = serial + start_date + end_date
hasher = serial.lower() + start_date + end_date

try:
d_purchase_id, d_order_no, d_line_no, d_contractid, d_start, d_end, forcedupdate = purchases[hasher]
Expand Down
Binary file added Files/warranty_hp.pyc
Binary file not shown.
2 changes: 1 addition & 1 deletion Files/warranty_ibm_lenovo.py
Expand Up @@ -152,7 +152,7 @@ def process_result(self, result, purchases):
pass

# update or duplicate? Compare warranty dates by serial, contract_id and end date
hasher = serial.split('.')[0] + start_date + end_date
hasher = serial.lower() + start_date + end_date

try:
d_purchase_id, d_order_no, d_line_no, d_contractid, d_start, d_end, forcedupdate = purchases[hasher]
Expand Down
Binary file added Files/warranty_ibm_lenovo.pyc
Binary file not shown.
7 changes: 6 additions & 1 deletion README.md
Expand Up @@ -20,9 +20,13 @@ In order for this script to check warranty status of the device, the device must
- If either hardware model or serial # is missing, warranty status won't be checked for device.
- IBM script points to warranty info not related to the SKU, serial given

## Known issues
- Leave the DOQL boolean to True. The regular API doesn't built up the dictionary for compensating Dell removing the ordernumbers. If on false you could get double registrations

## Change Log
- Please check `CHANGELOG.md`


## Usage
- Set required parameters in warranty.cfg file and run warranty_dell.py script:

Expand All @@ -37,8 +41,9 @@ In order for this script to check warranty status of the device, the device must
- Add the path to your environment : "%PythonFolder%\Scripts"
- Run `pip install requests`
- Run `python starter.py`
- Or just download python 2.7 from https://www.python.org/ as pip is included

## Compatibility
* requests module required
* Script runs on Linux and Windows
* Python 2.7
* Python 2.7
90 changes: 67 additions & 23 deletions starter.py
Expand Up @@ -58,6 +58,7 @@ def get_vendor_api(name):


def loader(name, api, d42):
global ordernos

# Locate the devices involved, based on the hardware models found, add offset with recursion
offset = 0
Expand Down Expand Up @@ -92,7 +93,7 @@ def loader(name, api, d42):
result = vendor_api.run_warranty_check(inline_serials)

if result is not None:
api.process_result(result, purchases)
api.process_result(result, purchases, ordernos)

offset += 50
else:
Expand All @@ -105,6 +106,7 @@ def loader(name, api, d42):
cfg = Config()
d42_cfg = cfg.get_config('d42')
discover = cfg.get_config('discover')
other = cfg.get_config('other')

# init
d42_params = {
Expand All @@ -114,30 +116,72 @@ def loader(name, api, d42):
}
d42_rest = Device42rest(d42_params)

forcedupdate = discover['forcedupdate']
DEBUG = other['debug']
DOQL = other['doql']

# get purchases data from Device42
orders = d42_rest.get_purchases()
purchases = {}

if orders and 'purchases' in orders:
for order in orders['purchases']:
if 'line_items' in order:
purchase_id = order.get('purchase_id')
order_no = order.get('order_no')

for line_item in order['line_items']:
line_no = line_item.get('line_no')
devices = line_item.get('devices')
contractid = line_item.get('line_notes')
start = line_item.get('line_start_date')
end = line_item.get('line_end_date')

if start and end and devices:
for device in devices:
if 'serial_no' in device:
serial = device['serial_no']
hasher = serial + contractid + start + end
if hasher not in purchases:
purchases[hasher] = [purchase_id, order_no, line_no, contractid, start, end, discover['forcedupdate']]
global ordernos
ordernos = {}

if DOQL:
orders = d42_rest.get_doqldata('select purchaselineitem_pk,order_no,line_no,contract_id,contract_type_name,service_type_name,li.start_date,li.end_date,LIDEV.device_name,DEV.serial_no,li.cc_code from view_purchase_v1 PUR LEFT JOIN view_purchaselineitem_v1 LI ON PUR.purchase_pk=LI.purchase_fk LEFT JOIN view_purchaselineitems_to_devices_v1 LIDEV on (LI.purchaselineitem_pk=LIDEV.purchaselineitem_fk) LEFT JOIN view_device_v1 DEV on (LIDEV.device_fk=DEV.device_pk)' )
#if DEBUG:
# print orders
orders = orders.splitlines()
for order in orders:
if order.split(',')[1]:
if not order.split(',')[2]:
# some occurances happened that only the ordernu was returned.
# As a result there is no information to compare with, so double registrations were made.
# some alerting needs to be put in place for this.
if forcedupdate:
if DEBUG: print '[!] no purchaselines found. Continue is forced. This could result in double entries!'
else:
if DEBUG: print '[!] No purchaselines!\n\tHalting script to prevent double registrations.'
sys.exit()
#Check if there is a lineitem for the order. If so register the lineitem in the array
purchase_id = order.split(',')[0]
order_no = order.split(',')[1]
line_no = order.split(',')[2]
contractid = order.split(',')[3]
contracttype = order.split(',')[4]
servicetype = order.split(',')[5]
start = order.split(',')[6]
end = order.split(',')[7]
devices = order.split(',')[8]
serial = order.split(',')[9]

# Build dictionary to compensate for Dell removing order_nos from the api output
ordernos[serial.split('_')[0].lower()] = order_no

if start and end and devices and contractid and serial:
hasher = serial.split('_')[0].lower() + contractid + start + end
if hasher not in purchases:
purchases[hasher] = [purchase_id, order_no, line_no, contractid, start, end, discover['forcedupdate']]
else:
orders = d42_rest.get_purchases()
if orders and 'purchases' in orders:
for order in orders['purchases']:
if 'line_items' in order:
purchase_id = order.get('purchase_id')
order_no = order.get('order_no')

for line_item in order['line_items']:
line_no = line_item.get('line_no')
devices = line_item.get('devices')
contractid = line_item.get('line_notes')
start = line_item.get('line_start_date')
end = line_item.get('line_end_date')

if start and end and devices and contractid:
for device in devices:
if 'serial_no' in device:
serial = device['serial_no']
hasher = serial + contractid + start + end
if hasher not in purchases:
purchases[hasher] = [purchase_id, order_no, line_no, contractid, start, end, discover['forcedupdate']]

APPS_ROW = []
if discover['dell']:
Expand Down