Category Archives: tales

Sparse files are handy sometimes

Disk Space Piechart
I have somewhere an old 2T ext3 volume that holds some disk images, one of which being 1.5T in size (I know, I know). The filesystem was almost full and I needed some extra space to copy off some data. So I need to delete something in order to free up some space, right ? Wrong.

zerofree and fallocate to the rescue! [1]

root@host :/volume/images# zerofree -v 100/vm-100-disk-1.raw
221330954/236263080/402653184
root@host:/volume/images# fallocate -v --dig-holes 100/vm-100-disk-1.raw
100/vm-100-disk-1.raw: 895.5 GiB (961573908480 bytes) converted to sparse holes.

Now I have the free space to use as I wish!

Someday I must convert this volume to LVM-Thin.

[1]
root@host:~# apropos zerofree fallocate
fallocate (1) – preallocate or deallocate space to a file
zerofree (8) – zero free blocks from ext2, ext3 and ext4 file-systems

Nostalgic

Original Red Hat Linux 5 Box

 

 

Red Hat Linux 5.0. (Not RHEL).
I still have the box after 20 years.
Too bad I don’t have a floppy drive anymore. 🙂

How to close a JIRA ticket using the REST api

logoJIRAPNGI had to close a bunch of tickets during a cleanup operation and I thought I better spend a few hours learning something new instead of mindlessly clicking buttons into a web interface and waiting for it to refresh. So I set out to learn about JIRA’s REST api. Using python, because why not, I want to learn python as well.

Fortunately Atlassian has pretty good docs and tutorials on it’s webpages. Unfortunately our JIRA instance requires you to fill out the time spent and some custom fields when you want to transition a ticket to the “Resolved” state.

The following hackish script is the result. It will require the username and password for the JIRA instance, the ticket, the time spent and a comment as parameters. Proper argument and error handling is left as an exercise to the reader.

#!/usr/bin/python
import urllib
import urllib2
import base64
import json
import sys

if (len(sys.argv) != 6):
     print "Usage: closeticket.py JiraUser JiraPassword JiraTicket TimeSpent \"Comment text\""
     sys.exit(2)

username = sys.argv[1]
password = sys.argv[2]
key = sys.argv[3]
timespent = sys.argv[4]
comment = sys.argv[5]

# Modify the url to suit your jira instance
url = 'https://jira.url/rest/api/2/issue/%s/transitions' % key
auth = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
data = json.dumps({
  "update": {
    "assignee": [
      {
        "set": {
          "name": username
        }
      }
    ],
    "comment": [
      {
        "add": {
          "body": comment
        }
      }
    ],
    "worklog": [
      {
        "add": {
          "timeSpent": timespent
        }
      }
    ],
    "customfield_10513": [
      {
        "set": [
          {
            "id": "17102"
          }
        ]
      }
    ],
    "customfield_11612": [
      {
        "set": {
          "id": "21903",
          "child": {
            "id": "21904"
          }
        }
      }
    ]
  },
  "transition": {
    "id": "5"
  }
})

request = urllib2.Request(url, data, {
    'Authorization': 'Basic %s' % auth,
    'Content-Type': 'application/json',
})
print urllib2.urlopen(request).read()

Note: custom fields are a bitch. You can use the following to figure them out:

https://jira.url/rest/api/2/issue/MY-ISSUE/transitions?expand=transitions.fields

Tales from the factory – curl vs wget

open_book_02

 

From time to time I will share some stories based on true events, maybe someone will learn something from them. Then again, maybe not. To protect the innocent, some names and events might be edited. Here comes the first one.

 

Someone raised a ticket that their application cannot access a certain url, let’s say “http://My.url.tld”. You dutifully log in to the system in question and try to access the url. Since the app is using the “libcurl” library, you naturally try to test with the respective utility. You confirm that it does not work:

[user@someserver ~]$ curl http://My.url.tld
Error message.
[user@someserver ~]$

In the same time a colleague also sees the ticket but for some reason he does the testing by the way of “wget”. It’s working for him:

[user@someserver ~]$ wget http://My.url.tld
Correct result.
[user@someserver ~]$

You go back and forth with “it’s working”, “no, it’s not” messages until both of you realize that you test differently. So, it’s working with “wget” but not with “curl”. Baffling. What could be wrong ?

After running both utils in debug mode you spot a minute difference:

[user@someserver ~]$ curl -v http://My.url.tld
* About to connect() to My.url.tld port 80 (#0)
* Trying 1.1.1.1... connected
* Connected to My.url.tld (1.1.1.1) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl
> Host: My.url.tld:80
> Accept: */*
>
<
< Error message
<
<
* Connection #0 to host My.url.tld left intact
* Closing connection #0
[user@someserver ~]$

[user@someserver ~]$ wget -d http://My.url.tld
DEBUG output created by Wget 1.12 on linux-gnu.
Resolving My.url.tld... 1.1.1.1
Caching My.url.tld => 1.1.1.1
Connecting to My.url.tld|1.1.1.1|:80... connected.
Created socket 3.
Releasing 0x000000000074fb60 (new refcount 1).

---request begin---
GET / HTTP/1.0
User-Agent: Wget (linux-gnu)
Accept: */*
Host: my.url.tld:80
Connection: Keep-Alive

---request end---
HTTP request sent, awaiting response...
---response begin---
HTTP/1.1 200 OK

Correct answer

---response end---
200 OK
Registered socket 3 for persistent reuse.
Length: 242 [text/xml]
Saving to: “filename”

100%[=========================================================================================================================================================================>] 242 --.-K/s in 0s

“filename” saved [242/242]
[user@someserver ~]$

Have you seen it ?

.
.
.
.
.
(suspense drumroll)
.
.
.
.
.
.
.

Turns out that wget is doing the equivalent of an tolower(“url”) so in the actual http request it’s sending “Host: my.url.tld” and curl it’s just taking what I specified in the command line, namely “Host: My.url.tld”. Taking the test test further it turns out that calling curl with the “only lowercase” url is producing the expected results (i.e. working).

I know what you are thinking, it should not matter how you call an hostname. True. Except that in this story there is an load balancer in the way, who tries (and mostly succeeds) to do smart stuff. Well, it turns out that there was an host-based string match in that load balancer that did not quite matched the mixed-case cases.

But a question remains. What is the correct behavior ? The “curl” or the “wget” one ? I lean on the “curl” approach but maybe I am biased. What do you think ?