cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From d...@apache.org
Subject [12/50] [abbrv] git commit: updated refs/heads/feature/systemvm-persistent-config to 8620642
Date Mon, 16 Feb 2015 16:47:53 GMT
Lots of changes to do with redundancy


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/61490196
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/61490196
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/61490196

Branch: refs/heads/feature/systemvm-persistent-config
Commit: 614901969b7816da3e86657798e1686954dc77e9
Parents: 7f6fb1f
Author: Ian Southam <isoutham@schubergphilis.com>
Authored: Mon Jan 26 14:17:25 2015 +0100
Committer: wilderrodrigues <wrodrigues@schubergphilis.com>
Committed: Mon Feb 16 16:08:37 2015 +0100

----------------------------------------------------------------------
 .../debian/config/opt/cloud/bin/cs/CsDatabag.py |  3 ++
 .../debian/config/opt/cloud/bin/cs/CsDhcp.py    |  6 +--
 .../config/opt/cloud/bin/cs/CsGuestNetwork.py   | 16 +++----
 .../debian/config/opt/cloud/bin/cs/CsProcess.py |  2 +-
 .../config/opt/cloud/bin/cs/CsRedundant.py      | 48 ++++++++++++++------
 .../config/opt/cloud/bin/cs_guestnetwork.py     | 11 +++--
 .../patches/debian/config/opt/cloud/bin/ian.py  | 10 ++++
 .../debian/config/opt/cloud/bin/merge.py        |  1 -
 .../config/opt/cloud/bin/update_config.py       | 33 ++++++++------
 .../opt/cloud/templates/keepalived.conf.templ   |  6 +--
 systemvm/test/python/TestCsCmdLine.py           |  4 ++
 11 files changed, 91 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDatabag.py
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDatabag.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDatabag.py
index 0f19a7c..fbafad2 100644
--- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDatabag.py
+++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDatabag.py
@@ -61,6 +61,9 @@ class CsCmdLine(CsDataBag):
             return self.idata()['router_pr']
         return 99
 
+    def set_priority(self, val):
+        self.idata()['router_pr'] = val
+
     def is_redundant(self):
         if "redundant_router" in self.idata():
             return self.idata()['redundant_router'] == "true"

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py
index 173be47..7278785 100644
--- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py
+++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py
@@ -68,8 +68,8 @@ class CsDhcp(CsDataBag):
             line = "dhcp-option=tag:interface-%s,15,%s" % (device, gn.get_domain())
             self.conf.search(sline, line)
             # DNS search order
-            sline = "dhcp-option=tag:interface-%s,119" % device
-            line = "dhcp-option=tag:interface-%s,119,%s" % (device, ','.join(gn.get_dns()))
+            sline = "dhcp-option=tag:interface-%s,6" % device
+            line = "dhcp-option=tag:interface-%s,6,%s" % (device, ','.join(gn.get_dns()))
             self.conf.search(sline, line)
             # Gateway
             gateway = ''
@@ -103,7 +103,7 @@ class CsDhcp(CsDataBag):
                       "del": False
                       }
                 changed.append(to)
-                
+
                 for v in changed:
                     if v['mac'] == to['mac'] or v['ip'] == to['ip'] or v['host'] == to['host']:
                         to['del'] = True

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/bin/cs/CsGuestNetwork.py
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsGuestNetwork.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsGuestNetwork.py
index f9ecbfc..4010007 100644
--- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsGuestNetwork.py
+++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsGuestNetwork.py
@@ -37,29 +37,29 @@ class CsGuestNetwork:
 
     def get_dns(self):
         if not self.guest:
-        	return self.config.get_dns()
-		# Can a router provide dhcp but not dns?
+            return self.config.get_dns()
+        # Can a router provide dhcp but not dns?
         if 'dns' in self.data:
-            return [ self.data['router_guest_gateway'] ] + self.data['dns'].split(',')
+            return [self.data['router_guest_gateway']] + self.data['dns'].split(',')
         elif "router_guest_gateway" in self.data:
-            return [ self.data['router_guest_gateway'] ]
+            return [self.data['router_guest_gateway']]
         else:
             return [""]
 
     def set_dns(self, val):
-		self.data['dns'] = val
+        self.data['dns'] = val
 
     def set_router(self, val):
-		self.data['router_guest_gateway'] = val
+        self.data['router_guest_gateway'] = val
 
     def get_netmask(self):
-        #We need to fix it properly. I just added the if, as Ian did in some other files,
to avoid the exception.
+        # We need to fix it properly. I just added the if, as Ian did in some other files,
to avoid the exception.
         if 'router_guest_netmask' in self.data:
             return self.data['router_guest_netmask']
         return ''
 
     def get_gateway(self):
-        #We need to fix it properly. I just added the if, as Ian did in some other files,
to avoid the exception.
+        # We need to fix it properly. I just added the if, as Ian did in some other files,
to avoid the exception.
         if 'router_guest_gateway' in self.data:
             return self.data['router_guest_gateway']
         return ''

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/bin/cs/CsProcess.py
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsProcess.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsProcess.py
index e768852..afa6310 100644
--- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsProcess.py
+++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsProcess.py
@@ -50,4 +50,4 @@ class CsProcess(object):
 
     def find(self):
         has_pid = len(self.find_pid()) > 0
-        return has_pid
\ No newline at end of file
+        return has_pid

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRedundant.py
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRedundant.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRedundant.py
index aca10b2..d9d36e2 100644
--- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRedundant.py
+++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRedundant.py
@@ -43,6 +43,8 @@ from CsConfig import CsConfig
 class CsRedundant(object):
 
     CS_RAMDISK_DIR = "/ramdisk"
+    CS_PRIO_UP = 1
+    CS_PRIO_DOWN = -1
     CS_ROUTER_DIR = "%s/rrouter" % CS_RAMDISK_DIR
     CS_TEMPLATES = [
         "heartbeat.sh.templ", "check_heartbeat.sh.templ",
@@ -50,9 +52,10 @@ class CsRedundant(object):
     ]
     CS_TEMPLATES_DIR = "/opt/cloud/templates"
     CONNTRACKD_BIN = "/usr/sbin/conntrackd"
-    CONNTRACKD_LOCK = "/var/lock/conntrack.lock"
-    CONNTRACKD_CONFIG = "/etc/conntrackd/conntrackd.conf"
+    CONNTRACKD_KEEPALIVED_CONFLOCK = "/var/lock/conntrack.lock"
+    CONNTRACKD_CONF = "/etc/conntrackd/conntrackd.conf"
     RROUTER_LOG = "/var/log/cloud.log"
+    KEEPALIVED_CONF = "/etc/keepalived/keepalived.conf"
 
     def __init__(self, config):
         self.cl = config.cmdline()
@@ -71,8 +74,8 @@ class CsRedundant(object):
         CsHelper.service("keepalived", "stop")
         CsHelper.umount_tmpfs(self.CS_RAMDISK_DIR)
         CsHelper.rmdir(self.CS_RAMDISK_DIR)
-        CsHelper.rm("/etc/conntrackd/conntrackd.conf")
-        CsHelper.rm("/etc/keepalived/keepalived.conf")
+        CsHelper.rm(self.CONNTRACKD_CONF)
+        CsHelper.rm(self.KEEPALIVED_CONF)
 
     def _redundant_on(self):
         CsHelper.mkdir(self.CS_RAMDISK_DIR, 0755, False)
@@ -83,17 +86,18 @@ class CsRedundant(object):
             if s.endswith(".templ"):
                 d = s.replace(".templ", "")
             CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, s), "%s/%s" % (self.CS_ROUTER_DIR,
d))
-        CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, "keepalived.conf.templ"),
"/etc/keepalived/keepalived.conf")
-        CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, "conntrackd.conf.templ"),
"/etc/conntrackd/conntrackd.conf")
+        CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, "keepalived.conf.templ"),
self.KEEPALIVED_CONF)
+        CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, "conntrackd.conf.templ"),
self.CONNTRACKD_CONF)
         CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, "checkrouter.sh.templ"),
"/opt/cloud/bin/checkrouter.sh")
 
+        CsHelper.execute('sed -i "s/--exec\ \$DAEMON;/--exec\ \$DAEMON\ --\ --vrrp;/g" /etc/init.d/keepalived')
         # checkrouter.sh configuration
         file = CsFile("/opt/cloud/bin/checkrouter.sh")
         file.greplace("[RROUTER_LOG]", self.RROUTER_LOG)
         file.commit()
 
         # keepalived configuration
-        file = CsFile("/etc/keepalived/keepalived.conf")
+        file = CsFile(self.KEEPALIVED_CONF)
         file.search(" router_id ", "    router_id %s" % self.cl.get_name())
         file.search(" priority ", "    priority %s" % self.cl.get_priority())
         file.search(" weight ", "    weight %s" % 2)
@@ -103,7 +107,7 @@ class CsRedundant(object):
 
         # conntrackd configuration
         guest = self.address.get_guest_if()
-        connt = CsFile("/etc/conntrackd/conntrackd.conf")
+        connt = CsFile(self.CONNTRACKD_CONF)
         connt.section("Multicast {", "}", [
                       "IPv4_address 225.0.0.50\n",
                       "Group 3780\n",
@@ -139,7 +143,7 @@ class CsRedundant(object):
         ads = [o for o in self.address.get_ips() if o.needs_vrrp()]
         for o in ads:
             CsHelper.execute("ifconfig %s down" % o.get_device())
-        cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONFIG)
+        cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONF)
         CsHelper.execute("%s -s" % cmd)
         CsHelper.service("ipsec", "stop")
         CsHelper.service("xl2tpd", "stop")
@@ -163,14 +167,15 @@ class CsRedundant(object):
         ads = [o for o in self.address.get_ips() if o.is_public()]
         for o in ads:
             CsHelper.execute("ifconfig %s down" % o.get_device())
-        cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONFIG)
+        cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONF)
         CsHelper.execute("%s -d" % cmd)
         CsHelper.service("ipsec", "stop")
         CsHelper.service("xl2tpd", "stop")
         CsHelper.service("cloud-passwd-srvr", "stop")
         CsHelper.service("dnsmasq", "stop")
+        self._set_priority(self.CS_PRIO_DOWN)
         self.cl.dbag['config']['redundant_master'] = "false"
-        CsHelper.service("keepalived", "restart")
+        #CsHelper.service("keepalived", "restart")
         self.cl.save()
         logging.info("Router switched to backup mode")
 
@@ -192,7 +197,7 @@ class CsRedundant(object):
             CsHelper.execute("arping -I %s -A %s -c 1" % (o.get_device(), o.get_ip()))
         # FIXME Need to add in the default routes but I am unsure what the gateway is
         # ip route add default via $gw table Table_$dev proto static
-        cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONFIG)
+        cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONF)
         CsHelper.execute("%s -c" % cmd)
         CsHelper.execute("%s -f" % cmd)
         CsHelper.execute("%s -R" % cmd)
@@ -202,10 +207,17 @@ class CsRedundant(object):
         CsHelper.service("cloud-passwd-srvr", "restart")
         CsHelper.service("dnsmasq", "restart")
         self.cl.dbag['config']['redundant_master'] = "true"
+        self._set_priority(self.CS_PRIO_UP)
         self.cl.save()
-        CsHelper.service("keepalived", "restart")
+        #CsHelper.service("keepalived", "restart")
         logging.info("Router switched to master mode")
 
+    def _set_priority(self, dir):
+        self.cl.set_priority(int(self.cl.get_priority()) + dir)
+        file = CsFile(self.KEEPALIVED_CONF)
+        file.search(" priority ", "    priority %s" % self.cl.get_priority())
+        file.commit()
+
     def _collect_ignore_ips(self):
         """
         This returns a list of ip objects that should be ignored
@@ -232,4 +244,14 @@ class CsRedundant(object):
             if o.needs_vrrp():
                 str = "        %s brd %s dev %s\n" % (o.get_gateway_cidr(), o.get_broadcast(),
o.get_device())
                 lines.append(str)
+                # This is wrong set_master and set_backup need to do this
+                self.check_is_up(o.get_device())
         return lines
+
+    def check_is_up(self, device):
+        """ Ensure device is up """
+        cmd = "ip link show %s | grep 'state DOWN'" % device
+        for i in CsHelper.execute(cmd):
+            if " DOWN " in i:
+                cmd2 = "ip link set %s up" % device
+                CsHelper.execute(cmd2)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/bin/cs_guestnetwork.py
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs_guestnetwork.py b/systemvm/patches/debian/config/opt/cloud/bin/cs_guestnetwork.py
index 6cb29a1..31c0796 100644
--- a/systemvm/patches/debian/config/opt/cloud/bin/cs_guestnetwork.py
+++ b/systemvm/patches/debian/config/opt/cloud/bin/cs_guestnetwork.py
@@ -19,11 +19,12 @@ from pprint import pprint
 
 keys = ['eth1', 'eth2', 'eth3', 'eth4', 'eth5', 'eth6', 'eth7', 'eth8', 'eth9']
 
+
 def merge(dbag, gn):
     device = gn['device']
-    
+
     if not gn['add'] and device in dbag:
-        
+
         if dbag[device]:
             device_to_die = dbag[device][0]
             try:
@@ -33,8 +34,8 @@ def merge(dbag, gn):
                 del(dbag[device])
         else:
             del(dbag[device])
-    
+
     else:
         dbag.setdefault(device, []).append(gn)
-    
-    return dbag
\ No newline at end of file
+
+    return dbag

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/bin/ian.py
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/bin/ian.py b/systemvm/patches/debian/config/opt/cloud/bin/ian.py
new file mode 100644
index 0000000..17cc466
--- /dev/null
+++ b/systemvm/patches/debian/config/opt/cloud/bin/ian.py
@@ -0,0 +1,10 @@
+from cs.CsGuestNetwork import CsGuestNetwork
+import merge
+
+merge.DataBag.DPATH = "."
+csguestnetwork = CsGuestNetwork({}, {})
+csguestnetwork.guest = True
+csguestnetwork.set_dns("1.1.1.1,2.2.2.2")
+csguestnetwork.set_router("3.3.3.3")
+dns = csguestnetwork.get_dns()
+print dns

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/bin/merge.py
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/bin/merge.py b/systemvm/patches/debian/config/opt/cloud/bin/merge.py
index 7229254..4999757 100755
--- a/systemvm/patches/debian/config/opt/cloud/bin/merge.py
+++ b/systemvm/patches/debian/config/opt/cloud/bin/merge.py
@@ -126,7 +126,6 @@ class updateDataBag:
 
     def processGuestNetwork(self, dbag):
         d = self.qFile.data
-        
         dp = {}
         dp['public_ip'] = d['router_guest_ip']
         dp['netmask'] = d['router_guest_netmask']

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/bin/update_config.py
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/bin/update_config.py b/systemvm/patches/debian/config/opt/cloud/bin/update_config.py
index e84fd09..77557f9 100755
--- a/systemvm/patches/debian/config/opt/cloud/bin/update_config.py
+++ b/systemvm/patches/debian/config/opt/cloud/bin/update_config.py
@@ -38,11 +38,13 @@ jsonPath = "/var/cache/cloud/%s"
 jsonCmdConfigPath = jsonPath % sys.argv[1]
 currentGuestNetConfig = "/etc/cloudstack/guestnetwork.json"
 
+
 def finish_config():
     # Converge
     returncode = configure.main([])
     sys.exit(returncode)
 
+
 def process_file():
     print "[INFO] Processing JSON file %s" % sys.argv[1]
     qf = QueueFile()
@@ -51,59 +53,60 @@ def process_file():
     # Converge
     finish_config()
 
+
 def is_guestnet_configured(guestnet_dict, keys):
-    
+
     existing_keys = []
     new_eth_key = None
-    
+
     for k1, v1 in guestnet_dict.iteritems():
         if k1 in keys and len(v1) > 0:
             existing_keys.append(k1)
-    
+
     if not existing_keys:
         '''
         It seems all the interfaces have been removed. Let's allow a new configuration to
come in.
         '''
         print "[WARN] update_config.py :: Reconfiguring guest network..."
         return False
-    
+
     file = open(jsonCmdConfigPath)
     new_guestnet_dict = json.load(file)
-    
+
     if not new_guestnet_dict['add']:
         '''
         Guest network has to be removed.
         '''
         print "[INFO] update_config.py :: Removing guest network..."
         return False
-    
+
     '''
     Check if we have a new guest network ready to be setup
     '''
     device = new_guestnet_dict['device']
-    
+
     if device in existing_keys:
         '''
         Device already configured, ignore.
         '''
         return True
-    
+
     exists = False
-    
+
     for key in existing_keys:
         for interface in guestnet_dict[key]:
             new_mac = new_guestnet_dict["mac_address"].encode('utf-8')
             old_mac = interface["mac_address"].encode('utf-8')
             new_ip = new_guestnet_dict["router_guest_ip"].encode('utf-8')
             old_ip = interface["router_guest_ip"].encode('utf-8')
-    
+
             if (new_mac == old_mac) and (new_ip == old_ip):
                 exists = True
                 break
-        
+
         if exists:
             break
-        
+
     return exists
 
 if not (os.path.isfile(jsonCmdConfigPath) and os.access(jsonCmdConfigPath, os.R_OK)):
@@ -122,7 +125,7 @@ if sys.argv[1] == "guest_network.json":
     if os.path.isfile(currentGuestNetConfig):
         file = open(currentGuestNetConfig)
         guestnet_dict = json.load(file)
-    
+
         if not is_guestnet_configured(guestnet_dict, ['eth1', 'eth2', 'eth3', 'eth4', 'eth5',
'eth6', 'eth7', 'eth8', 'eth9']):
             print "[INFO] update_config.py :: Processing Guest Network."
             process_file()
@@ -133,5 +136,5 @@ if sys.argv[1] == "guest_network.json":
         print "[INFO] update_config.py :: No GuestNetwork configured yet. Configuring first
one now."
         process_file()
 else:
-    print "[INFO] update_config.py :: Processing incoming file => %s" % sys.argv[1] 
-    process_file()
\ No newline at end of file
+    print "[INFO] update_config.py :: Processing incoming file => %s" % sys.argv[1]
+    process_file()

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/patches/debian/config/opt/cloud/templates/keepalived.conf.templ
----------------------------------------------------------------------
diff --git a/systemvm/patches/debian/config/opt/cloud/templates/keepalived.conf.templ b/systemvm/patches/debian/config/opt/cloud/templates/keepalived.conf.templ
index a79f207..9f3c24b 100644
--- a/systemvm/patches/debian/config/opt/cloud/templates/keepalived.conf.templ
+++ b/systemvm/patches/debian/config/opt/cloud/templates/keepalived.conf.templ
@@ -52,7 +52,7 @@ vrrp_instance inside_network {
     }
 
     !That's the correct path of the master.py file.
-    !notify_master "/opt/cloud/bin/master.py --master"
-    !notify_backup "/opt/cloud/bin/master.py --backup"
-    !notify_fault "/opt/cloud/bin/master.py --fault"
+    notify_master "/opt/cloud/bin/master.py --master"
+    notify_backup "/opt/cloud/bin/master.py --backup"
+    notify_fault "/opt/cloud/bin/master.py --fault"
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/61490196/systemvm/test/python/TestCsCmdLine.py
----------------------------------------------------------------------
diff --git a/systemvm/test/python/TestCsCmdLine.py b/systemvm/test/python/TestCsCmdLine.py
index 8b954d8..87d0471 100644
--- a/systemvm/test/python/TestCsCmdLine.py
+++ b/systemvm/test/python/TestCsCmdLine.py
@@ -18,6 +18,10 @@ class TestCsCmdLine(unittest.TestCase):
     def test_get_priority(self):
         self.assertTrue(self.cscmdline.get_priority() == 99)
 
+    def test_set_priority(self):
+        self.cscmdline.set_priority(100)
+        self.assertTrue(self.cscmdline.get_priority() == 100)
+
     def test_is_redundant(self):
         self.assertTrue(self.cscmdline.is_redundant() is False)
         self.cscmdline.set_redundant()


Mime
View raw message