Revision 9cb903f9

b/contrib/ganeti-hooks/mac2eui64
1
#!/usr/bin/env python
2

  
3
import sys
4

  
5
from IPy import IP
6

  
7
if len(sys.argv) != 3:
8
    sys.stderr.write("Usage: %s <mac_address> <IPv6 prefix>\n" % sys.argv[0])
9
    sys.exit(127)
10

  
11
mac = sys.argv[1]
12
try:
13
    prefix = IP(sys.argv[2])
14
except ValueError:
15
    sys.stderr.write("Invalid IPv6 prefix '%s'\n" % sys.argv[2])
16
    
17

  
18
if prefix.version() != 6:
19
    sys.stderr.write("%s is not a valid IPv6 prefix\n" % prefix)
20
    sys.exit(1)
21

  
22
if prefix.prefixlen() != 64:
23
    sys.stderr.write("Cannot generate an EUI-64 address on a non-64 subnet\n")
24
    sys.exit(1)
25

  
26
mac_parts = mac.split(":")
27
pfx_parts = prefix.net().strFullsize().split(":")
28

  
29
if len(mac_parts) != 6:
30
    sys.stderr.write("%s is not a valid MAC-48 address\n" % mac)
31
    sys.exit(1)
32

  
33
eui64 = mac_parts[:3] + [ "ff", "fe" ] + mac_parts[3:]
34

  
35
eui64[0] = "%02x" % (int(eui64[0], 16) ^ 0x02)
36

  
37
ip = ":".join(pfx_parts[:4])
38
for l in range(0, len(eui64), 2):
39
    ip += ":%s" % "".join(eui64[l:l+2])
40

  
41
try:
42
    print IP(ip).strCompressed()
43
except ValueError, e:
44
    sys.stderr.write("Ooops, something went wrong: '%s'!\n" % str(e))
45

  
46
# vim: set ts=4 sts=4 sw=4 et ai :
/dev/null
1
#!/usr/bin/env python
2

  
3
import sys
4

  
5
from IPy import IP
6

  
7
mac = sys.argv[1]
8
prefix = IP(sys.argv[2])
9

  
10
if prefix.version() != 6:
11
    print "%s is not a valid IPv6 prefix" % prefix
12
    sys.exit(1)
13

  
14
components = mac.split(":")
15
pfx = sys.argv[2].split("::")[0]
16

  
17
if len(components) != 6:
18
    print "%s is not a valid MAC-48 address" % mac
19
    sys.exit(1)
20

  
21
eui64 = components[:3] + [ "ff", "fe" ] + components[3:]
22

  
23
eui64[0] = "%02x" % (int(eui64[0], 16) ^ 0x02)
24

  
25
for l in range(0, len(eui64), 2):
26
    pfx += ":%s" % "".join(eui64[l:l+2])
27

  
28
print IP(pfx)
29

  
30
# vim: set ts=4 sts=4 sw=4 et ai :
b/ganeti/hooks.py
18 18

  
19 19
from amqplib import client_0_8 as amqp
20 20

  
21
from synnefo.util.mac2eui64 import mac2eui64
21 22
import synnefo.settings as settings
22 23

  
23

  
24 24
def ganeti_net_status(logger, environ):
25 25
    """Produce notifications of type 'Ganeti-net-status'
26 26
    
......
49 49
                else:
50 50
                    nics[index] = { key: environ[env] }
51 51

  
52
                # IPv6 support:
53
                #
54
                # The IPv6 of NIC with index 0 [the public NIC]
55
                # is derived using an EUI64 scheme.
56
                if index == 0 and key == 'mac':
57
                    nics[0]['ipv6'] = mac2eui64(nics[0]['mac'],
58
                                                settings.PUBLIC_IPV6_PREFIX)
59

  
52 60
    # Verify our findings are consistent with the Ganeti environment
53 61
    indexes = list(nics.keys())
54 62
    ganeti_nic_count = int(environ['GANETI_INSTANCE_NIC_COUNT'])
b/ganeti/tests.py
54 54
            'type': 'ganeti-net-status',
55 55
            'instance': 'instance2.example.com',
56 56
            'nics': [
57
                { 'ip': '147.102.3.1', 'mac': '00:01:de:ad:be:ef', 'link': 'xen-br0' },
57
                {
58
                    'ip': '147.102.3.1', 'mac': '00:01:de:ad:be:ef',
59
                    'link': 'xen-br0', 'ipv6': '2001:db8::201:deff:fead:beef'
60
                },
58 61
                { 'mac': '00:01:de:ad:ba:be' },
59 62
                { 'ip': '147.102.3.98', 'mac': '00:01:02:03:04:05' }
60 63
            ]
b/logic/backend.py
107 107
            network=net,
108 108
            index=i,
109 109
            mac=nic.get('mac', ''),
110
            ipv4=nic.get('ip', ''))
110
            ipv4=nic.get('ip', ''),
111
            ipv6=nic.get('ipv6',''))
111 112
    vm.save()
112 113

  
113 114

  
b/logic/callbacks.py
1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
#
7
#   1. Redistributions of source code must retain the above copyright
8
#      notice, this list of conditions and the following disclaimer.
9
#
10
#  2. Redistributions in binary form must reproduce the above copyright
11
#     notice, this list of conditions and the following disclaimer in the
12
#     documentation and/or other materials provided with the distribution.
13
#
14
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
# SUCH DAMAGE.
25
#
26
# The views and conclusions contained in the software and documentation are
27
# those of the authors and should not be interpreted as representing official
28
# policies, either expressed or implied, of GRNET S.A.
29

  
30
# Callback functions used by the dispatcher to process incoming notifications
31
# from AMQP queues.
32

  
33
import socket
34
import traceback
35
import json
36
import logging
37
import sys
38

  
39
from synnefo.db.models import VirtualMachine
40
from synnefo.logic import utils, backend, email_send
41

  
42
_logger = logging.getLogger("synnefo.dispatcher")
43

  
44
def update_db(message):
45
    """Process the status of a VM based on a ganeti status message"""
46
    _logger.debug("Processing ganeti-op-status msg: %s", message.body)
47
    try:
48
        msg = _parse_json(message.body)
49

  
50
        if msg["type"] != "ganeti-op-status":
51
            _logger.error("Message is of unknown type %s.", msg["type"])
52
            return
53

  
54
        if msg["operation"] == "OP_INSTANCE_QUERY_DATA":
55
            return status_job_finished(message)
56

  
57
        vmid = utils.id_from_instance_name(msg["instance"])
58
        vm = VirtualMachine.objects.get(id=vmid)
59

  
60
        backend.process_op_status(vm, msg["jobId"], msg["operation"],
61
                                  msg["status"], msg["logmsg"])
62
        _logger.debug("Done processing ganeti-op-status msg for vm %s.",
63
                      msg["instance"])
64
        message.channel.basic_ack(message.delivery_tag)
65
    except KeyError:
66
        _logger.error("Malformed incoming JSON, missing attributes: %s",
67
                      message.body)
68
    except VirtualMachine.InvalidBackendIdError:
69
        _logger.debug("Ignoring msg for unknown instance %s.",
70
                      msg["instance"])
71
    except VirtualMachine.DoesNotExist:
72
        _logger.error("VM for instance %s with id %d not found in DB.",
73
                      msg["instance"], vmid)
74
    except Exception as e:
75
        _logger.error("Unexpected error:\n%s" %
76
            "".join(traceback.format_exception(*sys.exc_info())))
77

  
78

  
79
def update_net(message):
80
    """Process a network status update notification from Ganeti"""
81
    _logger.debug("Processing ganeti-net-status msg: %s", message.body)
82
    try:
83
        msg = _parse_json(message.body)
84

  
85
        if msg["type"] != "ganeti-net-status":
86
            _logger.error("Message is of unknown type %s", msg["type"])
87
            return
88

  
89
        vmid = utils.id_from_instance_name(msg["instance"])
90
        vm = VirtualMachine.objects.get(id=vmid)
91

  
92
        backend.process_net_status(vm, msg["nics"])
93
        _logger.debug("Done processing ganeti-net-status msg for vm %s.",
94
                      msg["instance"])
95
        message.channel.basic_ack(message.delivery_tag)
96
    except KeyError:
97
        _logger.error("Malformed incoming JSON, missing attributes: %s",
98
                      message.body)
99
    except VirtualMachine.InvalidBackendIdError:
100
        _logger.debug("Ignoring msg for unknown instance %s.",
101
                      msg["instance"])
102
    except VirtualMachine.DoesNotExist:
103
        _logger.error("VM for instance %s with id %d not found in DB.",
104
                      msg["instance"], vmid)
105
    except Exception as e:
106
        _logger.error("Unexpected error:\n%s" %
107
            "".join(traceback.format_exception(*sys.exc_info())))
108

  
109

  
110
def send_email(message):
111
    """Process an email submission request"""
112

  
113
    try:
114
        msg = json.loads(message.body)
115

  
116
        email_send.send(sender=msg['frm'], recipient = msg['to'],
117
                        body=msg['body'], subject=msg['subject'])
118
        message.channel.basic_ack(message.delivery_tag)
119
    except KeyError:
120
        _logger.error("Malformed incoming JSON, missing attributes: %s",
121
                      message.body)
122
    except socket.error as e:
123
        _logger.error("Cannot connect to SMTP server:%s\n", e)
124
    except Exception as e:
125
        _logger.error("Unexpected error:%s\n", e)
126
        raise
127

  
128

  
129
def update_credits(message):
130
    _logger.debug("Request to update credits")
131
    message.channel.basic_ack(message.delivery_tag)
132

  
133
def trigger_status_update(message):
134
    """
135
        Triggers a status update job for a specific VM id.
136
    """
137
    _logger.debug("Request to trigger status update: %s", message.body)
138

  
139
    try:
140
        msg = _parse_json(message.body)
141

  
142
        if msg["type"] != "reconcile" :
143
             _logger.error("Message is of unknown type %s", msg["type"])
144
             return
145

  
146
        if msg["vmid"] == "" :
147
            _logger.error("Reconciliate message does not specify a VM id")
148
            return
149

  
150
        vm = VirtualMachine.objects.get(id=msg["vmid"])
151
        backend.request_status_update(vm)
152

  
153
        message.channel.basic_ack(message.delivery_tag)
154
    except KeyError as k:
155
        _logger.error("Malformed incoming JSON, missing attributes: %s", k)
156
    except Exception as e:
157
        _logger.error("Unexpected error:%s", e)
158

  
159
def status_job_finished (message) :
160
    """
161
        Updates VM status based on a previously sent status update request
162
    """
163
    try:
164
        msg = _parse_json(message.body)
165

  
166
        if msg["operation"] != 'OP_INSTANCE_QUERY_DATA':
167
            _logger.error("Message is of unknown type %s", msg["operation"])
168
            return
169

  
170
        if msg["status"] != "success" :
171
            _logger.warn("Ignoring non-success status update from job %d on VM %s",
172
                          msg['jobId'], msg['instance'])
173
            message.channel.basic_ack(message.delivery_tag)
174
            return
175

  
176
        status = backend.get_job_status(msg['jobId'])
177

  
178
        _logger.debug("Node status job result: %s" % status)
179

  
180
        if status['summary'][0] != u'INSTANCE_QUERY_DATA' :
181
             _logger.error("Status update is of unknown type %s", status['summary'])
182
             return
183

  
184
        conf_state = status['opresult'][0][msg['instance']]['config_state']
185
        run_state = status['opresult'][0][msg['instance']]['run_state']
186

  
187
        # XXX: The following assumes names like snf-12
188
        instid = msg['instance'].split('-')[1]
189

  
190
        vm = VirtualMachine.objects.get(id = instid)
191

  
192
        if run_state == "up":
193
            opcode = "OP_INSTANCE_REBOOT"
194
        else :
195
            opcode = "OP_INSTANCE_SHUTDOWN"
196

  
197
        backend.process_op_status(vm=vm, jobid=msg['jobId'],opcode=opcode,
198
                                  status="success",
199
                                  logmsg="Reconciliation: simulated event")
200

  
201
        message.channel.basic_ack(message.delivery_tag)
202
    except KeyError as k:
203
        _logger.error("Malformed incoming JSON, missing attributes: %s", k)
204
    except Exception as e:
205
        _logger.error("Unexpected error:%s"%e)
206

  
207
def dummy_proc(message):
208
    try:
209
        msg = _logger.debug(message.body)
210
        _logger.debug("Msg (exchange:%s) ", msg)
211
        message.channel.basic_ack(message.delivery_tag)
212
    except Exception as e:
213
        _logger.error("Could not receive message %s" % e.message)
214
        pass
215

  
216
def _parse_json(data):
217
    try:
218
        return json.loads(data)
219
    except Exception as e:
220
        _logger.error("Could not parse JSON file: %s", e)
221
        raise
b/logic/dispatcher.py
27 27
import time
28 28
import socket
29 29

  
30
from synnefo.logic import dispatcher_callbacks
30
from synnefo.logic import callbacks
31 31

  
32 32

  
33 33
class Dispatcher:
......
97 97
        # Bind queues to handler methods
98 98
        for binding in bindings:
99 99
            try:
100
                callback = getattr(dispatcher_callbacks, binding[3])
100
                callback = getattr(callbacks, binding[3])
101 101
            except AttributeError:
102 102
                self.logger.error("Cannot find callback %s" % binding[3])
103 103
                continue
/dev/null
1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
#
7
#   1. Redistributions of source code must retain the above copyright
8
#      notice, this list of conditions and the following disclaimer.
9
#
10
#  2. Redistributions in binary form must reproduce the above copyright
11
#     notice, this list of conditions and the following disclaimer in the
12
#     documentation and/or other materials provided with the distribution.
13
#
14
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
# SUCH DAMAGE.
25
#
26
# The views and conclusions contained in the software and documentation are
27
# those of the authors and should not be interpreted as representing official
28
# policies, either expressed or implied, of GRNET S.A.
29

  
30
# Callback functions used by the dispatcher to process incoming notifications
31
# from AMQP queues.
32

  
33
import socket
34
import traceback
35
import json
36
import logging
37
import sys
38

  
39
from synnefo.db.models import VirtualMachine
40
from synnefo.logic import utils, backend, email_send
41

  
42
_logger = logging.getLogger("synnefo.dispatcher")
43

  
44
def update_db(message):
45
    """Process the status of a VM based on a ganeti status message"""
46
    _logger.debug("Processing ganeti-op-status msg: %s", message.body)
47
    try:
48
        msg = _parse_json(message.body)
49

  
50
        if msg["type"] != "ganeti-op-status":
51
            _logger.error("Message is of unknown type %s.", msg["type"])
52
            return
53

  
54
        if msg["operation"] == "OP_INSTANCE_QUERY_DATA":
55
            return status_job_finished(message)
56

  
57
        vmid = utils.id_from_instance_name(msg["instance"])
58
        vm = VirtualMachine.objects.get(id=vmid)
59

  
60
        backend.process_op_status(vm, msg["jobId"], msg["operation"],
61
                                  msg["status"], msg["logmsg"])
62
        _logger.debug("Done processing ganeti-op-status msg for vm %s.",
63
                      msg["instance"])
64
        message.channel.basic_ack(message.delivery_tag)
65
    except KeyError:
66
        _logger.error("Malformed incoming JSON, missing attributes: %s",
67
                      message.body)
68
    except VirtualMachine.InvalidBackendIdError:
69
        _logger.debug("Ignoring msg for unknown instance %s.",
70
                      msg["instance"])
71
    except VirtualMachine.DoesNotExist:
72
        _logger.error("VM for instance %s with id %d not found in DB.",
73
                      msg["instance"], vmid)
74
    except Exception as e:
75
        _logger.error("Unexpected error:\n%s" %
76
            "".join(traceback.format_exception(*sys.exc_info())))
77

  
78

  
79
def update_net(message):
80
    """Process a network status update notification from Ganeti"""
81
    _logger.debug("Processing ganeti-net-status msg: %s", message.body)
82
    try:
83
        msg = _parse_json(message.body)
84

  
85
        if msg["type"] != "ganeti-net-status":
86
            _logger.error("Message is of unknown type %s", msg["type"])
87
            return
88

  
89
        vmid = utils.id_from_instance_name(msg["instance"])
90
        vm = VirtualMachine.objects.get(id=vmid)
91

  
92
        backend.process_net_status(vm, msg["nics"])
93
        _logger.debug("Done processing ganeti-net-status msg for vm %s.",
94
                      msg["instance"])
95
        message.channel.basic_ack(message.delivery_tag)
96
    except KeyError:
97
        _logger.error("Malformed incoming JSON, missing attributes: %s",
98
                      message.body)
99
    except VirtualMachine.InvalidBackendIdError:
100
        _logger.debug("Ignoring msg for unknown instance %s.",
101
                      msg["instance"])
102
    except VirtualMachine.DoesNotExist:
103
        _logger.error("VM for instance %s with id %d not found in DB.",
104
                      msg["instance"], vmid)
105
    except Exception as e:
106
        _logger.error("Unexpected error:\n%s" %
107
            "".join(traceback.format_exception(*sys.exc_info())))
108

  
109

  
110
def send_email(message):
111
    """Process an email submission request"""
112

  
113
    try:
114
        msg = json.loads(message.body)
115

  
116
        email_send.send(sender=msg['frm'], recipient = msg['to'],
117
                        body=msg['body'], subject=msg['subject'])
118
        message.channel.basic_ack(message.delivery_tag)
119
    except KeyError:
120
        _logger.error("Malformed incoming JSON, missing attributes: %s",
121
                      message.body)
122
    except socket.error as e:
123
        _logger.error("Cannot connect to SMTP server:%s\n", e)
124
    except Exception as e:
125
        _logger.error("Unexpected error:%s\n", e)
126
        raise
127

  
128

  
129
def update_credits(message):
130
    _logger.debug("Request to update credits")
131
    message.channel.basic_ack(message.delivery_tag)
132

  
133
def trigger_status_update(message):
134
    """
135
        Triggers a status update job for a specific VM id.
136
    """
137
    _logger.debug("Request to trigger status update: %s", message.body)
138

  
139
    try:
140
        msg = _parse_json(message.body)
141

  
142
        if msg["type"] != "reconcile" :
143
             _logger.error("Message is of unknown type %s", msg["type"])
144
             return
145

  
146
        if msg["vmid"] == "" :
147
            _logger.error("Reconciliate message does not specify a VM id")
148
            return
149

  
150
        vm = VirtualMachine.objects.get(id=msg["vmid"])
151
        backend.request_status_update(vm)
152

  
153
        message.channel.basic_ack(message.delivery_tag)
154
    except KeyError as k:
155
        _logger.error("Malformed incoming JSON, missing attributes: %s", k)
156
    except Exception as e:
157
        _logger.error("Unexpected error:%s", e)
158

  
159
def status_job_finished (message) :
160
    """
161
        Updates VM status based on a previously sent status update request
162
    """
163
    try:
164
        msg = _parse_json(message.body)
165

  
166
        if msg["operation"] != 'OP_INSTANCE_QUERY_DATA':
167
            _logger.error("Message is of unknown type %s", msg["operation"])
168
            return
169

  
170
        if msg["status"] != "success" :
171
            _logger.warn("Ignoring non-success status update from job %d on VM %s",
172
                          msg['jobId'], msg['instance'])
173
            message.channel.basic_ack(message.delivery_tag)
174
            return
175

  
176
        status = backend.get_job_status(msg['jobId'])
177

  
178
        _logger.debug("Node status job result: %s" % status)
179

  
180
        if status['summary'][0] != u'INSTANCE_QUERY_DATA' :
181
             _logger.error("Status update is of unknown type %s", status['summary'])
182
             return
183

  
184
        conf_state = status['opresult'][0][msg['instance']]['config_state']
185
        run_state = status['opresult'][0][msg['instance']]['run_state']
186

  
187
        # XXX: The following assumes names like snf-12
188
        instid = msg['instance'].split('-')[1]
189

  
190
        vm = VirtualMachine.objects.get(id = instid)
191

  
192
        if run_state == "up":
193
            opcode = "OP_INSTANCE_REBOOT"
194
        else :
195
            opcode = "OP_INSTANCE_SHUTDOWN"
196

  
197
        backend.process_op_status(vm=vm, jobid=msg['jobId'],opcode=opcode,
198
                                  status="success",
199
                                  logmsg="Reconciliation: simulated event")
200

  
201
        message.channel.basic_ack(message.delivery_tag)
202
    except KeyError as k:
203
        _logger.error("Malformed incoming JSON, missing attributes: %s", k)
204
    except Exception as e:
205
        _logger.error("Unexpected error:%s"%e)
206

  
207
def dummy_proc(message):
208
    try:
209
        msg = _logger.debug(message.body)
210
        _logger.debug("Msg (exchange:%s) ", msg)
211
        message.channel.basic_ack(message.delivery_tag)
212
    except Exception as e:
213
        _logger.error("Could not receive message %s" % e.message)
214
        pass
215

  
216
def _parse_json(data):
217
    try:
218
        return json.loads(data)
219
    except Exception as e:
220
        _logger.error("Could not parse JSON file: %s", e)
221
        raise
b/settings.d/20-api.conf
58 58
# (e.g., right before the NIC gets deleted).
59 59
# This value is also hardcoded in a fixture in db/fixtures/initial_data.json.
60 60
GANETI_NULL_LINK = 'snf_null'
61
# Public IPv6: A /64 IPv6 prefix for autoconfiguration with EUI-64 addresses
62
PUBLIC_IPV6_PREFIX = "2001:db8::/64"
61 63
#
62 64
# The pool of private network links to use is
63 65
# $GANETI_LINK_PREFIX{1..$GANETI_MAX_LINK_NUMBER}.
b/util/mac2eui64.py
1
# Copyright 2011 GRNET S.A. All rights reserved.
2
# 
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
# 
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
# 
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
# 
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33
# 
34

  
35
import sys
36
from IPy import IP
37

  
38
# Adapted from NFDHCPD's mac2eui64 utility, repository commit:feca7bb95
39

  
40
def mac2eui64(mac, prefixstr):
41
    try:
42
        prefix = IP(prefixstr)
43
    except ValueError:
44
        raise Exception("Invalid IPv6 prefix '%s'" % prefixstr)
45

  
46
    if prefix.version() != 6:
47
        raise Exception("%s is not a valid IPv6 prefix" % prefixstr)
48

  
49
    if prefix.prefixlen() != 64:
50
        raise Exception("Cannot generate an EUI-64 address on a non-64 subnet")
51

  
52
    mac_parts = mac.split(":")
53
    pfx_parts = prefix.net().strFullsize().split(":")
54

  
55
    if len(mac_parts) != 6:
56
        raise Exception("%s is not a valid MAC-48 address" % mac)
57

  
58
    eui64 = mac_parts[:3] + [ "ff", "fe" ] + mac_parts[3:]
59

  
60
    eui64[0] = "%02x" % (int(eui64[0], 16) ^ 0x02)
61

  
62
    ip = ":".join(pfx_parts[:4])
63
    for l in range(0, len(eui64), 2):
64
        ip += ":%s" % "".join(eui64[l:l+2])
65

  
66
    return IP(ip).strCompressed()
67

  
68
# vim: set ts=4 sts=4 sw=4 et ai :

Also available in: Unified diff